idle owns the panel brightness (it fades between a "full" level and 0), so
user brightness control routes through a new idle_set_awake_brightness():
idle now fades to a runtime awake level instead of the hard-coded
DISPLAY_DEFAULT_BRIGHTNESS, and the chosen level survives idle fades + wake.
brightness.{h,cpp} holds a 4-step ramp {64,128,200,255} (default 200 == the
old constant, so default behavior is unchanged) persisted to NVS, and applies
the level through idle. Wired to the PWR button in the next commit.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replaces the build-flag-driven #ifdef sprawl (~30 blocks across 6 files)
with a small HAL in firmware/src/hal/ and per-board folders under
firmware/src/boards/. Shared code (main.cpp, ui.cpp, splash.cpp) no
longer contains a single `#ifdef BOARD_*` — optional features are
guarded by BoardCaps (runtime) and BOARD_HAS_* macros (compile-time,
inside the board's own files).
Why: lets community contributors port to new ESP32 + AMOLED + touch
combos by dropping in a boards/<name>/ folder + a PlatformIO env,
without touching shared files. See docs/porting/adding-a-board.md.
Highlights:
- New HAL: display_hal, touch_hal, input_hal, power_hal, imu_hal,
board_caps. Each board provides display.cpp, touch.cpp, input.cpp,
power.cpp, imu.cpp, caps.cpp, board_init.cpp + private hardware
drivers (e.g. io_expander.{h,cpp} on AMOLED-1.8).
- PlatformIO build_src_filter selects each board's folder per env.
- ui.cpp picks fonts and layout from board_caps() via compute_layout()
with screen-height breakpoints (>= 460 → large, else compact).
- splash.cpp computes CELL = min(W,H)/20 — responsive instead of two
hardcoded values.
- idle.cpp (from #24) rewired through display_hal + power_hal — no
longer depends on the deleted display_cfg.h / power.h.
- power_hal gains power_hal_is_vbus_in() for idle's
IDLE_SLEEP_WHEN_CHARGING gate.
- boards/template/ + docs/porting/{adding-a-board,hal-contract,
capability-flags}.md to bootstrap new ports.
- display_cfg.h, power.{h,cpp}, imu.{h,cpp}, io_expander.{h,cpp}
deleted from src/ root (moved into boards/<name>/ or hal/).
Verification: both `pio run -e waveshare_amoled_216` and
`pio run -e waveshare_amoled_18` succeed unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Addresses review feedback on #24:
- New config IDLE_SLEEP_WHEN_CHARGING (default false): while USB is
plugged in we don't enter sleep and we wake immediately if power
comes back. Covers both "I like watching Clawd animations on my
plugged-in desk device" and "alternative hardware without a battery
is always on USB, sleep would be odd."
- New config IDLE_WAKE_ON_TOUCH (default true): a touch on the dark
panel wakes the device. The first touch is swallowed (mirrors the
button wake-consumption), so tapping a dark screen wakes without
also toggling splash<->usage. Set false to keep the original
pets/sleeves-safe behaviour where touch is fully ignored while
asleep.
power.{h,cpp} grow a power_is_vbus_in() helper backed by
pmu.isVbusIn() — distinct from isCharging() so the no-battery case
("plugged in, nothing to charge") is detected correctly.
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.