Release 10 — Operator UX & System Quality¶
Theme: Releases 3–9 built the what — drivers, effects, protocols, fixtures. Release 10 polishes the how for operators and field technicians: a file manager for LittleFS, real-time FreeRTOS task visibility, system metrics, core dump support, OTA updates, presets/scenes, and the long-deferred test visibility + mobile-ready UI polish. By the end, projectMM is a production-ready LED controller that a non-developer can operate, update, and recover from a crash.
Release Overview¶
| Gap | Addressed by |
|---|---|
| No in-UI file browser for LittleFS | Sprint 1 |
| FreeRTOS task health invisible at runtime | Sprint 2 |
| System status / safe-mode not exposed | Sprint 3 |
| No historical metrics (FPS timeline, PSRAM usage, battery) | Sprint 4 |
| Crashes lose diagnostic info — no core dump support | Sprint 5 |
| Firmware updates require USB reflash | Sprint 6 |
| Layer / mapping internals invisible to operators | Sprint 7 |
| No presets / scenes persistence | Sprint 8 |
| Test results invisible in CI and on docs site | Sprint 9 (carried from original R3) |
| UI is functional but not field-polished | Sprint 10 |
Sprint 1 — File Manager¶
Scope¶
Goal: a browser-based LittleFS file manager — navigate, create, edit, upload, delete.
Part A — FileManagerModule. Read-only permanent module; provides REST endpoints:
GET /api/files/list?path=/→ JSON tree of current directoryGET /api/files/read?path=/foo.json→ raw file contentPUT /api/files/write?path=/foo.json→ body is new contentPOST /api/files/upload→ multipartDELETE /api/files/remove?path=/foo.json
Part B — UI pane. Left-tree navigation + right-pane preview/editor. Syntax highlight for .json, .sc (Live Script), .txt. Dotfile toggle.
Part C — Safety. Deleting a permanent-module state file (state/modulemanager.json, etc.) requires a confirm dialog. Free-space warning when < 10% free.
Definition of Done:
- Upload a 50 KB file via UI; content matches source byte-for-byte
- Edit-in-place on a
.jsonfile works (auto-parses on save; error on invalid JSON) - Delete operation requires confirmation for permanent-state files
Result¶
To be completed after implementation.
Retrospective¶
To be completed after implementation.
Sprint 2 — Tasks Module (FreeRTOS Introspection)¶
Scope¶
Goal: expose uxTaskGetSystemState output in the UI — one row per task.
Part A — TasksModule (ESP32 only). Per-task: name, state (Ready/Running/Blocked/Suspended/Deleted), current core, priority, stack high-water mark, runtime counter.
Part B — Live update via WS. Streamed at 2 Hz (not 50 Hz — this is diagnostics, not hot data).
Part C — Warning flags. Stack high-water mark under a configurable threshold → warning icon. Runtime counter growing too fast → warning icon.
Definition of Done:
- UI table shows all tasks (usually ~20 on ESP32-S3)
- Stack warnings trigger when we intentionally undersize a task
- Table filters: per-core, per-state
Result¶
To be completed after implementation.
Retrospective¶
To be completed after implementation.
Sprint 3 — System Status + Safe Mode¶
Scope¶
Goal: one-page system status + a safe-mode indicator after crashes.
Part A — Extend SystemStatusModule. Add: effect-loop FPS, driver-loop FPS, overall FPS, firmware target + build date, chip model, coprocessor FW version (P4 only), free heap, PSRAM usage.
Part B — Safe-mode flag. Persist a safe_mode flag in KvStore. On boot, if previous boot crashed (detected via reset reason + bootloader), set flag + log. UI shows prominent "Safe Mode" banner until operator clears it.
Part C — Sleep / Restart / Factory Reset buttons. Three operator actions on SystemStatusModule:
- Sleep: deep sleep for N seconds
- Restart: ESP.restart() — previously deferred from original R3 backlog, now implemented
- Factory Reset: wipe LittleFS state (after confirm dialog), reboot
Definition of Done:
- All status fields populated and live-updating
- Safe-mode flag correctly triggers after simulated crash
- All three buttons work end-to-end with confirm dialogs
Result¶
To be completed after implementation.
Retrospective¶
To be completed after implementation.
Sprint 4 — System Metrics (FPS Timeline, PSRAM, Battery)¶
Scope¶
Goal: historical metrics visible as time-series charts.
Part A — MetricsModule. Rolling buffers (60-sec, 10-min, 1-hr) for: overall FPS, effect FPS, driver FPS, free heap, free PSRAM, battery voltage (if ADC configured), CPU temp.
Part B — Chart UI. Lightweight canvas-based time-series renderer in the frontend (no heavy chart lib dependency).
Part C — Per-module loop timing heatmap. For heavier effects like audio-reactive or Live Scripts, a heatmap shows loop cost over time.
Definition of Done:
- FPS chart shows drop when we spawn a heavy effect; recovers when removed
- PSRAM usage chart shows growth when layer count increases
- 1-hr window survives Wi-Fi disconnect (buffer is in RAM, not dependent on client)
Result¶
To be completed after implementation.
Retrospective¶
To be completed after implementation.
Sprint 5 — Core Dump Support¶
Scope¶
Goal: after a crash, a coredump.bin is persisted to flash and downloadable from the UI.
Part A — Partition table update. Add a 64 KB coredump partition per ESP32 variant; partition CSV files for classic / S3 / P4.
Part B — Core dump download. GET /api/system/coredump returns the saved binary; UI shows a "Download coredump" button when one is present.
Part C — SHA-256 reference. The build captures projectMM.elf SHA-256; dump download includes that hash so the operator knows which elf to pair with esp-coredump for symbolication.
Definition of Done:
- Intentional crash (divide by zero) produces downloadable coredump
- SHA-256 matches the built elf
- Docs page:
docs/developer-guide/debugging-core-dumps.mdwith theesp-coredumpworkflow
Result¶
To be completed after implementation.
Retrospective¶
To be completed after implementation.
Sprint 6 — OTA Update¶
Scope¶
Goal: update firmware from the UI without USB — GitHub releases list + manual upload.
Part A — UpdateModule. REST: GET /api/update/releases returns GitHub releases (stable + nightly); POST /api/update/install?tag=vX.Y.Z triggers download + flash.
Part B — Manual upload. "Upload .bin" alternative for custom builds; client-side SHA-256 verification before flash.
Part C — Progress + verification. Streaming progress (% downloaded, % flashed), post-flash CRC check, auto-restart after success.
Part D — Board compatibility. Check target env against running board's chip + partition layout; refuse mismatches.
Definition of Done:
- Update from v1.0 stub to v1.1 stub via GitHub tag works end-to-end
- Manual .bin upload verifies SHA-256 before flash
- Incompatible .bin refused cleanly — no bricked devices
Result¶
To be completed after implementation.
Retrospective¶
To be completed after implementation.
Sprint 7 — MoonLight Info (Layer/Mapping Introspection)¶
Scope¶
Goal: a diagnostic page showing the internals of the pixel pipeline — MoonLight's MoonLight Info equivalent.
Part A — PipelineInfoModule. Read-only display of:
- Physical layer: total lights, channels/light, total bytes, bounds (min/max x/y/z)
- Per virtual layer: light count, mapping table size, counts of (zero-mapped, one-to-one, many-to-one, many-to-many) slots
- Effect node counts per layer
Part B — Mapping-health diagnostics. Warn on many-to-many or all-zero mappings (often a config bug). Auto-heal button: rebuild mapping from scratch.
Part C — Memory-per-module breakdown. Re-use R2's classSize() + heapSize() — now displayed as a table in the PipelineInfo page, summing total per category.
Definition of Done:
- All fields populated correctly for a 2D panel + 3D cube simultaneously
- Mapping warnings trigger on an intentional misconfig
- Total memory displayed matches actual heap delta (within 10%)
Result¶
To be completed after implementation.
Retrospective¶
To be completed after implementation.
Sprint 8 — Presets & Scenes¶
Scope¶
Goal: save a snapshot of the full module configuration as a preset; load via a single click or MQTT/DMX command.
Part A — PresetsModule. REST: POST /api/presets {name} saves current modulemanager.json + all state/*.json as a preset bundle; POST /api/presets/load {name} restores it.
Part B — UI. List of presets; save current-state button; load + delete actions. Preset bundle stored in state/presets/<name>.json.
Part C — Scene schedules (bonus). If time permits: simple time-based preset schedule ("apply preset evening at 18:00 local"). If cut, stays in backlog.
Part D — Remote preset application. POST /api/presets/load/<name> and the MQTT topic projectmm/<host>/preset/load both trigger loading. Critically: SuperSync group broadcasts preset load to all peers.
Definition of Done:
- Save + load preserves every control value and every added module
- Load via MQTT from Home Assistant works
- Group-broadcast preset load verified against 2 peers in a SuperSync group
Result¶
To be completed after implementation.
Retrospective¶
To be completed after implementation.
Sprint 9 — Test Visibility (CI, Docs Site, Frontend Panel)¶
Carried from the original Release 3 plan — fits here with the operator-UX theme.
Scope¶
Goal: test pass/fail visible in three places — the CI job summary, the docs site, and the live frontend.
Part A — CI summary. The test-results.json artifact from R2 S3 is rendered as a Markdown table in the GitHub Actions job summary alongside the footprint report. One row per test suite: pass / fail / duration.
Part B — Docs site test page. docs/test-results.md generated at docs-build time from the latest test-results.json. Updated on every push to main.
Part C — Per-module test status. Each docs/modules/<name>.md gains a "Test Status" section; generated from the test-results JSON's mapping of test file → module.
Part D — Frontend health panel. Collapsible Tests panel in the UI showing the live health-check state of every module (from healthReport()). Green/red indicators. Sourced from the existing WS state push.
Definition of Done:
- GitHub Actions summary shows test table
- Docs site has live test-results page
- Module doc pages show test status
- Frontend health panel shows per-module health green/red
Result¶
To be completed after implementation.
Retrospective¶
To be completed after implementation.
Sprint 10 — UI Polish: Mobile, Search, Keyboard Shortcuts¶
Scope¶
Goal: the frontend looks and feels good on every device; power users get shortcuts.
Part A — Mobile-responsive audit. Walk every page on a 375 px viewport; fix overflow, unreadable text, too-small tap targets (< 44 px). Layer cards already polished in R4 S10; extend to every other module.
Part B — Global search. Cmd/Ctrl-K opens a spotlight-style search: modules, controls, presets, layouts, effects — jump by typing.
Part C — Keyboard shortcuts. B toggle brightness slider focus, P palette picker, +/- brightness ±5, Space toggle on/off, 1..9 load preset N.
Part D — Accessibility pass. aria-labels on all controls, tab-focus order sane, high-contrast mode respected.
Definition of Done:
- Every page usable on iPhone 12-size viewport
Cmd-Kopens search; fuzzy-matches across all module names- All keyboard shortcuts documented in a
?help overlay - Lighthouse accessibility score ≥ 90 on each top-level page
Result¶
To be completed after implementation.
Retrospective¶
To be completed after implementation.
Release 10 Backlog¶
- Plugin / extension system for third-party nodes. User-installable
.scbundles with metadata. Large scope; deferred to R11. - Multi-user / per-user preset. JWT from ESP32-SvelteKit would help. Pick up when real multi-user demand lands.
- Time-based schedules (scenes). If cut from Sprint 8, stays here.
- Cloud-free-text chart export. Export metrics as CSV/JSON. Pick up on operator request.
- Secondary UI dashboards. Grid-of-peer dashboards, preset matrix visualizations — pick up when operator workflows solidify.