firmware: auto-sleep screen after 30 min idle
AMOLED brightness fades to 0 after IDLE_TIMEOUT_MS without a physical button press; any of the three buttons (BTN_BACK / BTN_FWD / PWR) fades it back in. The first press from sleep is consumed for wake only — Space / Shift+Tab / cycle-screen only fire on the second press, so reaching for the device to glance at it doesn't accidentally send PTT to Claude. Touch never counts as activity and is fully swallowed while asleep: pets and sleeves can't wake it, and LVGL can't secretly toggle splash<->usage behind a black panel. Rotation handling defers while asleep so its blank+ramp doesn't fight the idle fade; a rotation that happens during sleep is detected and ramped in on wake. All tunables live in idle_cfg.h (timeout, fade durations, default brightness) — nothing is hard-coded in main.cpp. AMOLED brightness=0 is true 0 emission on these self-emissive panels, so no extra power-management plumbing (light-sleep, AXP rail gating) is needed to get the battery savings.
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
#include <Arduino.h>
|
||||
#include "idle.h"
|
||||
#include "idle_cfg.h"
|
||||
#include "display_cfg.h" // declares `extern Arduino_CO5300 *gfx;`
|
||||
|
||||
enum IdleState {
|
||||
STATE_AWAKE,
|
||||
STATE_FADING_OUT,
|
||||
STATE_ASLEEP,
|
||||
STATE_FADING_IN,
|
||||
};
|
||||
|
||||
static IdleState state = STATE_AWAKE;
|
||||
static uint32_t last_activity_ms = 0;
|
||||
static uint32_t fade_started_ms = 0;
|
||||
static uint32_t fade_last_step_ms = 0;
|
||||
static uint8_t fade_from = DISPLAY_DEFAULT_BRIGHTNESS;
|
||||
static uint8_t fade_to = 0;
|
||||
|
||||
static void apply_brightness(uint8_t b) {
|
||||
gfx->setBrightness(b);
|
||||
}
|
||||
|
||||
static void begin_fade(uint8_t to, uint32_t now) {
|
||||
fade_from = (to == 0) ? DISPLAY_DEFAULT_BRIGHTNESS : 0;
|
||||
fade_to = to;
|
||||
fade_started_ms = now;
|
||||
fade_last_step_ms = now;
|
||||
}
|
||||
|
||||
void idle_init(void) {
|
||||
state = STATE_AWAKE;
|
||||
last_activity_ms = millis();
|
||||
apply_brightness(DISPLAY_DEFAULT_BRIGHTNESS);
|
||||
}
|
||||
|
||||
void idle_note_activity(void) {
|
||||
last_activity_ms = millis();
|
||||
if (state == STATE_FADING_IN) return;
|
||||
if (state == STATE_AWAKE) return;
|
||||
// Asleep/fading-out shouldn't reach here in normal flow (callers gate via
|
||||
// idle_consume_wake_press first), but if it does: trigger a wake.
|
||||
begin_fade(DISPLAY_DEFAULT_BRIGHTNESS, last_activity_ms);
|
||||
state = STATE_FADING_IN;
|
||||
}
|
||||
|
||||
bool idle_consume_wake_press(void) {
|
||||
if (state == STATE_ASLEEP || state == STATE_FADING_OUT) {
|
||||
uint32_t now = millis();
|
||||
last_activity_ms = now;
|
||||
begin_fade(DISPLAY_DEFAULT_BRIGHTNESS, now);
|
||||
state = STATE_FADING_IN;
|
||||
return true;
|
||||
}
|
||||
if (state == STATE_FADING_IN) {
|
||||
// Mid-wake — still swallow this press; the user shouldn't get a
|
||||
// half-wake half-action surprise.
|
||||
last_activity_ms = millis();
|
||||
return true;
|
||||
}
|
||||
last_activity_ms = millis();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool idle_is_asleep(void) {
|
||||
return state == STATE_ASLEEP || state == STATE_FADING_OUT;
|
||||
}
|
||||
|
||||
void idle_tick(void) {
|
||||
uint32_t now = millis();
|
||||
|
||||
switch (state) {
|
||||
case STATE_AWAKE:
|
||||
if (now - last_activity_ms >= IDLE_TIMEOUT_MS) {
|
||||
begin_fade(0, now);
|
||||
state = STATE_FADING_OUT;
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_FADING_OUT:
|
||||
case STATE_FADING_IN: {
|
||||
if (now - fade_last_step_ms < IDLE_FADE_STEP_MS) break;
|
||||
fade_last_step_ms = now;
|
||||
uint32_t dur = (state == STATE_FADING_OUT) ? IDLE_FADE_OUT_MS : IDLE_FADE_IN_MS;
|
||||
uint32_t elapsed = now - fade_started_ms;
|
||||
if (elapsed >= dur) {
|
||||
apply_brightness(fade_to);
|
||||
state = (state == STATE_FADING_OUT) ? STATE_ASLEEP : STATE_AWAKE;
|
||||
} else {
|
||||
// Linear interpolation fade_from -> fade_to over dur ms.
|
||||
int32_t span = (int32_t)fade_to - (int32_t)fade_from;
|
||||
int32_t b = (int32_t)fade_from + (span * (int32_t)elapsed) / (int32_t)dur;
|
||||
if (b < 0) b = 0;
|
||||
if (b > 255) b = 255;
|
||||
apply_brightness((uint8_t)b);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case STATE_ASLEEP:
|
||||
break;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user