Public Access
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
6.0 KiB
6.0 KiB
M0 — Skeleton: Launcher, Status Bar, Settings, battery
Status: done on 2026-10-02, tagged v0.1.0.
Outcome
Every "Done when" item below works on the device, apart from the gaps listed here. Changes made along the way:
- Not done, carried over:
- Charging indicator: the battery voltage alone can't tell charging apart reliably. Revisit if the hardware exposes a charger status.
- Waking from power-off with a keyboard key: G0 only for now.
- Status Bar placeholders for GNSS, mesh, Wi-Fi and unread count are left out until those Services exist (M1, M2, M4).
- Added:
- SD card erase, in Settings → Storage.
- A Notification wakes an Off screen (dimmed) while its Toast shows.
- Navigation arrows without Fn outside Text Entry.
- Serial dev commands and
scripts/serial_log.sh.
- Measured on the device:
- About 225 KB free heap with the 64 KB frame buffer.
- About 15 ms per frame.
- 19% of the app flash used.
Goal: a firmware you can flash and hold. It boots to a Launcher, shows a live Status Bar, navigates with the keyboard and saves Settings. It proves the App/Service architecture that every later milestone plugs into.
Done when
- Building, testing and flashing each work with one command, run inside a local Docker container (no toolchain on the host).
- First boot runs the setup wizard (names, Region, timezone), and later boots skip it.
- The Launcher lists Apps. Enter starts one,
`goes back, and Fn+`returns home from anywhere. - The Status Bar shows battery %, a charging indicator and the clock (relative until set), with placeholders for GNSS, mesh, Wi-Fi and unread count.
- The Settings App edits and persists: long and short name, Region, timezone, brightness, dim and off timeouts, sound on/off, and probe-request MAC handling (raw by default).
- An About screen shows the version, free heap, battery voltage, SD status and usage, and uptime.
- The screen dims at 30 s and turns off at 60 s. Any key wakes it without the key also acting on the App.
- A long press of G0 powers off (deep sleep). G0 or a key wakes the device.
- The Compose Key types accented characters in the line editor (
opt'e→ é). - Toast Notifications work. A demo App can raise one, with a beep and LED flash.
- SD usage is watched: a Storage Warning appears once per boot above 80%. The Log-write cutoff flag is set at 90%, with no Log writers yet.
Out of scope for M0
Storage Clean-up screen (M1, once IRC Logs exist), any radio, GNSS, Wi-Fi.
Work breakdown
- Project scaffold
- PlatformIO project with the pioarduino platform (Arduino-ESP32 3.x on ESP-IDF 5.x). Board
m5stack-stamps3, 8 MB partitions, USB-CDC on boot. - Dependencies:
M5Cardputer(pulls in M5Unified and M5GFX). LICENSE(GPL-3.0),README.mdwith build and flash steps, a version string injected from git at build time (semver tags).- A
nativeenvironment for host-side unit tests (Unity). - Local CI in a Docker image with PlatformIO. One script builds the firmware and runs the native tests, and the same container flashes over USB. The repo is hosted on self-hosted Gitea, and a Gitea Actions workflow can reuse this image later.
- PlatformIO project with the pioarduino platform (Arduino-ESP32 3.x on ESP-IDF 5.x). Board
- Hardware bring-up checks (manual, on the device)
- Display, keyboard (TCA8418) and speaker through M5Cardputer.
- Confirm there's no PSRAM, and record the heap at boot.
- Investigate the G38 backlight/LED power-rail coupling, then decide how dimming works without killing the LED.
- Battery ADC on G10 (×2 divider): calibrate the voltage-to-% curve.
- SD card mount on the shared SPI bus (CS=12), behind a bus lock ready for the LoRa radio in M3.
- Core runtime
- Event bus: Services publish events (battery changed, notification, storage threshold), and the UI consumes them on the UI task.
- Service interface: start, stop, periodic tick, state snapshot for the Status Bar.
- App interface: enter, exit, key event, draw. Plus a registry, so adding an App means one file and one registration line.
- App lifecycle: one foreground App; Home and Back handling.
- Services for M0
- Settings store: typed keys in NVS (internal flash), with defaults and change events.
- Battery Service: sampled voltage, smoothed %, charging detection if the hardware allows it.
- Clock Service: no time source yet. API for "set from source X", plus relative-time formatting and Europe/Brussels conversion.
- Storage Service: SD present/absent, usage %, 80% and 90% thresholds raising events, Log-write permission flag.
- Power Service: dim and off timers, wake-key swallowing, G0 long-press → deep sleep.
- Widget kit (ADR 0002): an off-screen buffer pushed to the display.
- Status Bar, list, text view, line editor (with the Compose Key), dialog and Toast.
- A Latin-1 font set.
- Input layer
- Map key events to logical keys: arrows (Fn +
;.,/), Back, Home, Select. - Compose Key dead-key state machine. This is pure logic, unit-tested on the host.
- Map key events to logical keys: arrows (Fn +
- Apps: Launcher, Settings (including About), first-boot wizard, and a hidden Demo App for exercising Toasts and widgets.
- Docs: a short walkthrough for a first-time setup: install PlatformIO, USB permissions on Linux, flash, recover via download mode.
Host-tested logic (TDD candidates)
- Compose Key state machine
- Battery voltage → % curve and smoothing
- Storage threshold and once-per-boot warning logic
- Relative-time formatting and timezone conversion
- Settings defaults and validation (e.g. Region must be confirmed before any transmit flag)
Risks to resolve early
- G38 shared rail: if the backlight and LED really share power, "screen off" may also need to turn the LED off.
- RAM headroom: the off-screen buffer is 240×135×2 ≈ 64 KB at 16-bit, or about 32 KB at 8-bit. Measure the free heap now, because Wi-Fi + TLS (M1) is the tightest point.
- Keyboard library maturity for the ADV's TCA8418 in
M5Cardputer. Fall back to Adafruit_TCA8418 directly if needed.