Release 3 — Pixel Pipeline Foundations (original plan)¶
Theme: The minimum viable LED controller — test harness, pin config, one driver, 2D layout on a 16×16 panel, first effects, real LEDs lighting up on ESP32-S3-N16R8.
See moonlight-scope/index.md for what was done vs what remains.
Release Overview¶
| Gap | Addressed by |
|---|---|
| No end-user test+flash+monitor script; preview pipeline unvalidated; ESP32-S3-N16R8 not supported | Sprint 1 |
| No formal pin-assignment system | Sprint 2 (IO Module) |
| Each board variant needs its pin map rewritten | Sprint 3 (Board Presets) |
| No real pixel output path | Sprint 4 (PhysicalLayer) |
| No (x,y,z) → LED index mapping | Sprint 5 (VirtualLayer + mapping) |
| No 2D layout module | Sprint 6 (LayoutPanel, serpentine) |
| No real LED driver | Sprint 7 (FastLED driver) |
| No shared effect framework | Sprint 8 (Effect framework + first effects) |
| No global brightness / on-off / color tint | Sprint 9 (LightsControl) |
| Too many required module overrides; hot path not tuned | Sprint 10 (module simplification + 500 FPS) |
Key design decisions (still current):
- Unified
StatefulModulebase. MoonLight's Effect/Modifier/Layout/Driver are separate node types; projectMM keeps one base class and usescategoryfor UI grouping. - Dual-core split. Effects on Core 0, drivers on Core 1.
- Start with 2D, 3D-ready from day one. Coordinates are
(x,y,z)everywhere; layouts emitz=0for 2D. - Primary hardware target: ESP32-S3-N16R8. Classic ESP32 kept compiling; PC is the default development loop.
- Performance target: 500 FPS for a trivial 2D effect on a 16×16 panel (compositing applied, driver output skipped).
Sprint 1 — Test Scripts + ESP32-S3-N16R8 + Preview Pipeline Validation ✅¶
Done. Delivered as part of actual Release 3. See Release 3 Sprint 8 (deploy system) and Release 3 Sprint 1 (preview pipeline).
Key outcomes: Python deploy system (deploy/) replaces the shell scripts planned here; both boards (esp32dev + esp32s3_n16r8) build, flash, and pass live tests; test_preview_e2e.cpp validates headless preview pipeline; PSRAM + FS controls added to SystemStatusModule.
| Metric | Value |
|---|---|
| Tests | 179 total, 629 assertions |
| ESP32-S3-N16R8 fps | ~67 K (Network only) |
| ESP32-S3-N16R8 Flash | 26.6% of 4 MB |
| esp32dev Flash | 85.9% of 1.28 MB |
Sprint 2 — IO Module v1¶
Pending.
Goal: first-class pin-assignment system so every hardware-facing module can declare which GPIO it uses without hardcoding pin numbers.
IoModule— centralStatefulModuleowning pin-type descriptors:led_data,onboard_led,button,sensor_generic. Each entry: pin number, type, consumer module ID,moddedflag.- Pin-claim API:
int pin = io->claim("led_data", id())— returns-1if unavailable; module logs error and disables itself. maxPowerenvelope (0–500 W, default 10 W) — stubbed; real clamping in R6 Sprint 6.
Definition of Done: IoModule visible in UI; pin claims deterministic; double-claim + unknown-type covered by tests.
Sprint 3 — Board Presets + ESP32-S3-N16R8 Preset¶
Pending.
Goal: one click in the UI fills IoModule with the right default pin assignments for the target board.
- Preset format: JSON blob with board name, chip family, PlatformIO env, default pin assignments. Stored in
docs/boards/*.json, bundled intofrontend_bundle.h. - Initial presets:
generic_esp32,esp32_s3_devkit,esp32_s3_n16r8(primary R3 target — GPIO 16 for LED strip, GPIO 48 for onboard WS2812). POST /api/boardapplies a preset;moddedpins are preserved.- Board detection hint: suggest matching preset when chip model matches and
IoModuleis at defaults.
Definition of Done: picking esp32_s3_n16r8 from UI populates IoModule correctly; modded pins survive; preset JSON validated in tests.
Sprint 4 — PhysicalLayer + channelsD Buffer (3D-Ready) ✅¶
Done (different architecture). The pixel pipeline was rebuilt in actual Release 3 as EffectsLayer / DriverLayer with a Coord3D coordinate system and a 3D-ready w×h×d pixel buffer. See Release 3 Sprints 3b and 5.
The PhysicalLayer / VirtualLayer naming was superseded; the core ideas (3D-ready flat buffer, domain-agnostic base classes, coordinate-based setPixel) are fully implemented.
Still pending from this sprint: Core 1 FreeRTOS driver task (xTaskCreatePinnedToCore dispatch was wired in R2 Sprint 4 infrastructure but not yet dispatched); channelsDFreeSemaphore buffer-swap protocol.
Sprint 5 — VirtualLayer + Mapping Table ✅¶
Done (different architecture). Coordinate mapping delivered via Coord3D + GridLayout physical mapping in actual Release 3. See Release 3 Sprint 6.
setPixel(x, y, z, rgb) equivalent via DriverLayer Coord3D blend loop; serpentine wiring in GridLayout. forEachLight iterator equivalent via effect loop over w×h×d.
Sprint 6 — 2D Panel Layout (Serpentine) ✅¶
Done. Delivered as GridLayout with physical coordinate mapping and serpentine wiring in actual Release 3 Sprint 6. See Release 3 Sprint 6.
Sprint 7 — FastLED Driver (WS2812)¶
Pending.
Goal: first driver module that drives real LEDs. PC build stubs it.
FastLedDriverModule(ESP32-only): claimsled_datapin fromIoModule; configuresFastLED.addLeds<WS2812B, PIN, GRB>;loop()callsFastLED.show()from Core 1 task.- PC stub: writes each frame to
build-logs/leds.binfor pipeline validation without hardware. - Config:
chip(WS2812/SK6812/WS2815),color_order(RGB/GRB/BGR). - Primary target validation:
SolidEffectModule(red)lights the full 16×16 panel on ESP32-S3-N16R8.
Definition of Done: panel lights on primary target; PC writes leds.bin; FastLED footprint ≤ 120 KB flash, ≤ 2 KB RAM.
Sprint 8 — Effect Framework + First Effects¶
Partially done.
Done: SineEffect, RipplesEffect, LinesEffect ported from MoonLight in actual Release 3 Sprint 1. BrightnessModifier added. See Release 3 Sprint 1.
Still pending:
SolidEffectModule— single color across all lights (r,g,buint8_t controls). Simplest possible effect; hot-path benchmark baseline for Sprint 10.RainbowEffectModule(2D) — hue cycles across the panel with configurable density per axis.- Effect authoring target: each effect ≤ 80 LoC.
Sprint 9 — Lights Control Module¶
Pending.
Goal: global on/off, brightness, and color tint applied after all effects, before the driver.
LightsControlModule— permanent singleton. Controls:on(bool),brightness(uint8_t 0–255, default 51 = 20%),r_scale/g_scale/b_scale(uint8_t),bpm(broadcast),intensity(broadcast).- Apply stage in
DriverLayer::apply(): multiply each channel bybrightness × rgb_scale / 255²; zero buffer whenon == false. maxPowerstub: logs warning if estimated power exceedsIoModule::maxPower; real clamping in R6.- Auto-created on first boot (parallel to
NetworkModule).
Definition of Done: brightness=0 → black; on=false → black; RGB scale test (SolidEffect(255,255,255) + r_scale=255, g_scale=0, b_scale=0 → pure red).
Sprint 10 — Module Simplification + 500 FPS Tuning + End-to-End Demo¶
Partially done.
Done: Module simplification audit and StatefulModule interface rationalisation delivered in actual Release 3 Sprints 5 and 7. Mandatory vs optional overrides clearly separated; setProps/setInput/loadState/saveState documented. See Release 3 Sprints 5, 7.
Still pending:
- 500 FPS hot-path tuning. Profile
SolidEffectModule+DriverLayer::apply()+LightsControlModuleon ESP32-S3-N16R8 (drivershow()skipped). Target: ≥ 500 FPS. Per-stage timings inScheduler::printTimings(). No-allocations assertion (heap delta = 0 over 1000 ticks). - End-to-end LED demo. Default board preset +
GridLayout(16×16, serpentine)+SineEffect+FastLedDriverModule; cold flash;monitor-check.pygreen; photo/GIF indocs/user-guide/first-demo.md.
Release 3 Backlog¶
- IoModule → FastLED wiring. FastLED driver claims its pin via
IoModule::claim("led_data"). - Reboot button.
button-type control onSystemStatusModule. (Done in actual Release 3 Sprint 9 — remove from backlog.) - Implicit parent injection via
setInput. When childparent_idmatches a parent with a canonical output key, skip theinputsspec. - ModuleManager rename → ModuleFactory / ModuleRegistry. (seed from R2 S7)
- Live Scripts (ESPLiveScript,
.sccompiler). Deferred to Release 8. - Encryption of sensitive state at rest. Pick up when security requirements are clearer.