Skip to content

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 directory
  • GET /api/files/read?path=/foo.json → raw file content
  • PUT /api/files/write?path=/foo.json → body is new content
  • POST /api/files/upload → multipart
  • DELETE /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 .json file 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.md with the esp-coredump workflow

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-K opens 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 .sc bundles 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.