Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,18 @@ config PROSPECTOR_FIXED_BRIGHTNESS
int "Fixed display brightness"
default 50
range 1 100
depends on !PROSPECTOR_USE_AMBIENT_LIGHT_SENSOR
depends on !PROSPECTOR_USE_AMBIENT_LIGHT_SENSOR

config PROSPECTOR_DISPLAY_SLEEP_ENABLE
bool "Turn off display on idle"
default n

config PROSPECTOR_DISPLAY_IDLE_TIMEOUT_MS
int "Display idle timeout in ms"
default 30000
depends on PROSPECTOR_DISPLAY_SLEEP_ENABLE

config PROSPECTOR_DISPLAY_SLEEP_USE_PM
bool "Use Zephyr PM to suspend the display (SLEEP_IN)"
default n
depends on PROSPECTOR_DISPLAY_SLEEP_ENABLE && PM_DEVICE
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,21 @@ To customize, add config options to your `config/[YOUR KEYBOARD SHIELD].conf` li
```ini
CONFIG_PROSPECTOR_USE_AMBIENT_LIGHT_SENSOR=n
CONFIG_PROSPECTOR_FIXED_BRIGHTNESS=80
CONFIG_PROSPECTOR_DISPLAY_SLEEP_ENABLE=y
CONFIG_PROSPECTOR_DISPLAY_IDLE_TIMEOUT_MS=30000
# Optional: put display controller into sleep
# CONFIG_PM_DEVICE=y
# CONFIG_PROSPECTOR_DISPLAY_SLEEP_USE_PM=y
```

### Available config options:
| Name | Description | Default |
| ------------------------------------------------- | --------------------------------------------------------------------------| ------------ |
| `CONFIG_PROSPECTOR_USE_AMBIENT_LIGHT_SENSOR` | Use ambient light sensor for auto brightness, set to `n` if building without one | y |
| `CONFIG_PROSPECTOR_FIXED_BRIGHTNESS` | Set fixed display brightess when not using ambient light sensor | 50 (1-100) |
| `CONFIG_PROSPECTOR_FIXED_BRIGHTNESS` | Set fixed display brightess when not using ambient light sensor | 50 (1-100) |
| `CONFIG_PROSPECTOR_PROSPECTOR_ROTATE_DISPLAY_180` | Rotate the display 180 degrees | n |
| `CONFIG_PROSPECTOR_LAYER_ROLLER_ALL_CAPS` | Convert layer names to all caps | n |
| `CONFIG_PROSPECTOR_LAYER_ROLLER_ALL_CAPS` | Convert layer names to all caps | n |
| `CONFIG_PROSPECTOR_DISPLAY_SLEEP_ENABLE` | Turn off display and backlight on idle | n |
| `CONFIG_PROSPECTOR_DISPLAY_IDLE_TIMEOUT_MS` | Idle timeout before turning display off (ms) | 30000 |
| `CONFIG_PROSPECTOR_DISPLAY_SLEEP_USE_PM` | Use Zephyr PM to suspend display controller (requires `CONFIG_PM_DEVICE`) | n |

1 change: 1 addition & 0 deletions boards/shields/prospector_adapter/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ if(CONFIG_SHIELD_PROSPECTOR_ADAPTER)
zephyr_library_sources(src/brightness.c)
zephyr_library_sources(src/custom_status_screen.c)
zephyr_library_sources(src/display_rotate_init.c)
zephyr_library_sources(src/display_idle.c)
zephyr_library_sources(src/widgets/layer_roller.c)
zephyr_library_sources(src/widgets/battery_bar.c)
zephyr_library_sources_ifdef(CONFIG_DT_HAS_ZMK_BEHAVIOR_CAPS_WORD_ENABLED src/widgets/caps_word_indicator.c)
Expand Down
13 changes: 12 additions & 1 deletion boards/shields/prospector_adapter/src/brightness.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(als, 4);

#include "prospector/display_power.h"

static const struct device *pwm_leds_dev = DEVICE_DT_GET_ONE(pwm_leds);
#define DISP_BL DT_NODE_CHILD_IDX(DT_NODELABEL(disp_bl))

Expand All @@ -17,7 +19,7 @@ static uint8_t current_brightness = 100;

#define SENSOR_MIN 0 // Minimum sensor reading
#define SENSOR_MAX 100 // Maximum sensor reading
#define PWM_MIN 1 // Minimum PWM duty cycle (%) - keep display visible
#define PWM_MIN 15 // Minimum PWM duty cycle (%) - keep display visible even in dark
#define PWM_MAX 100 // Maximum PWM duty cycle (%)

#define FADE_STEP 1
Expand Down Expand Up @@ -94,6 +96,15 @@ extern void als_thread(void *d0, void *d1, void *d2) {

while (1) {

if (prospector_display_is_sleeping()) {
if (current_brightness != 0) {
led_set_brightness(pwm_leds_dev, DISP_BL, 0);
current_brightness = 0;
}
k_msleep(NORMAL_SAMPLE_SLEEP_MS);
continue;
}

k_msleep(NORMAL_SAMPLE_SLEEP_MS);


Expand Down
158 changes: 158 additions & 0 deletions boards/shields/prospector_adapter/src/display_idle.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/display.h>
#include <zephyr/drivers/led.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(display_idle, CONFIG_ZMK_LOG_LEVEL);

#include <zmk/event_manager.h>
#include <zmk/events/keycode_state_changed.h>
#include <zmk/events/position_state_changed.h>

#if IS_ENABLED(CONFIG_PM_DEVICE)
#include <zephyr/pm/device.h>
#endif

#include "prospector/display_power.h"

static const struct device *display_dev;
static const struct device *pwm_leds_dev;

#define DISP_BL DT_NODE_CHILD_IDX(DT_NODELABEL(disp_bl))

static struct k_work_delayable idle_work;
static atomic_t sleeping = ATOMIC_INIT(0);

static void prospector_display_blank_on(void) {
if (!display_dev) {
return;
}

#if IS_ENABLED(CONFIG_PROSPECTOR_DISPLAY_SLEEP_USE_PM) && IS_ENABLED(CONFIG_PM_DEVICE)
pm_device_action_run(display_dev, PM_DEVICE_ACTION_SUSPEND);
#else
display_blanking_on(display_dev);
#endif
}

static void prospector_display_blank_off(void) {
if (!display_dev) {
return;
}

#if IS_ENABLED(CONFIG_PROSPECTOR_DISPLAY_SLEEP_USE_PM) && IS_ENABLED(CONFIG_PM_DEVICE)
pm_device_action_run(display_dev, PM_DEVICE_ACTION_RESUME);
#else
display_blanking_off(display_dev);
#endif
}

void prospector_display_set_sleeping(bool s) {
atomic_set(&sleeping, s ? 1 : 0);
}

bool prospector_display_is_sleeping(void) {
return atomic_get(&sleeping) != 0;
}

static void display_go_to_sleep(void) {
if (prospector_display_is_sleeping()) {
return;
}

prospector_display_blank_on();

if (pwm_leds_dev) {
led_set_brightness(pwm_leds_dev, DISP_BL, 0);
}

prospector_display_set_sleeping(true);
}

static void display_wake_up(void) {
if (!prospector_display_is_sleeping()) {
return;
}

prospector_display_set_sleeping(false);

prospector_display_blank_off();

#if !IS_ENABLED(CONFIG_PROSPECTOR_USE_AMBIENT_LIGHT_SENSOR)
if (pwm_leds_dev) {
led_set_brightness(pwm_leds_dev, DISP_BL, CONFIG_PROSPECTOR_FIXED_BRIGHTNESS);
}
#else
/* For ALS mode: force minimum visible brightness on wake (20%)
* ALS thread will adjust to proper level shortly after */
if (pwm_leds_dev) {
led_set_brightness(pwm_leds_dev, DISP_BL, 20);
}
/* Give ALS thread time to react and adjust brightness */
k_msleep(150);
#endif
}

static void idle_work_handler(struct k_work *work) {
ARG_UNUSED(work);
if (!IS_ENABLED(CONFIG_PROSPECTOR_DISPLAY_SLEEP_ENABLE)) {
return;
}
display_go_to_sleep();
}

static int display_idle_init(const struct device *unused) {
ARG_UNUSED(unused);

display_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_display));
if (!device_is_ready(display_dev)) {
return 0;
}

pwm_leds_dev = DEVICE_DT_GET_ONE(pwm_leds);
if (!device_is_ready(pwm_leds_dev)) {
pwm_leds_dev = NULL;
}

k_work_init_delayable(&idle_work, idle_work_handler);

#if IS_ENABLED(CONFIG_PROSPECTOR_DISPLAY_SLEEP_ENABLE)
k_work_schedule(&idle_work, K_MSEC(CONFIG_PROSPECTOR_DISPLAY_IDLE_TIMEOUT_MS));
#endif

return 0;
}
SYS_INIT(display_idle_init, APPLICATION, 70);

static int on_any_key_event(const zmk_event_t *eh) {
if (!IS_ENABLED(CONFIG_PROSPECTOR_DISPLAY_SLEEP_ENABLE)) {
return ZMK_EV_EVENT_BUBBLE;
}

const struct zmk_keycode_state_changed *kc = as_zmk_keycode_state_changed(eh);
const struct zmk_position_state_changed *pos = as_zmk_position_state_changed(eh);

bool pressed = false;
if (kc) {
pressed = kc->state;
} else if (pos) {
pressed = pos->state;
}

if (!pressed) {
return ZMK_EV_EVENT_BUBBLE;
}

display_wake_up();
#if IS_ENABLED(CONFIG_PROSPECTOR_DISPLAY_SLEEP_ENABLE)
k_work_reschedule(&idle_work, K_MSEC(CONFIG_PROSPECTOR_DISPLAY_IDLE_TIMEOUT_MS));
#endif

return ZMK_EV_EVENT_BUBBLE;
}

ZMK_LISTENER(prospector_display_idle, on_any_key_event);
ZMK_SUBSCRIPTION(prospector_display_idle, zmk_keycode_state_changed);
ZMK_SUBSCRIPTION(prospector_display_idle, zmk_position_state_changed);


11 changes: 11 additions & 0 deletions include/prospector/display_power.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* Prospector display power state helper
*/
#pragma once

#include <stdbool.h>

void prospector_display_set_sleeping(bool sleeping);
bool prospector_display_is_sleeping(void);