Release 6 — Parallel Drivers & Scale¶
Theme: Releases 3–5 ran on one data pin at a time, topping out around ~500 LEDs before frame time suffers. Release 6 unlocks MoonLight's headline number: 12 K LEDs at 100 FPS on ESP32-S3, 16 K+ on ESP32-P4. Parallel driver backends per chip family, new board presets, and the first serious multi-board CI.
Release Overview¶
| Gap | Addressed by |
|---|---|
| FastLED single-pin driver blocks scale beyond ~500 LEDs | Sprints 1–3 (parallel drivers per MCU family) |
| Multi-pin board variants (Dig-Quad, Dig-Octa, SE16) have no presets | Sprint 4 |
| CI only covers classic ESP32 build | Sprint 5 (multi-board CI) |
maxPower envelope exists but doesn't clamp anything |
Sprint 6 |
| No ESP32-P4 support; P4 is MoonLight's highest-end target | Sprint 7 |
| WS2812 is the only supported chip | Sprints 8–9 (SK6812 RGBW, WS2815, APA102) |
| Brightness is RGB-only; CW/WW/CCT PWM channels unsupported | Sprint 10 |
Sprint 1 — Parallel LED Driver: ESP32 Classic (I2S)¶
Scope¶
Goal: drive up to 8 LED strips in parallel on classic ESP32 using the I2S peripheral in clockless mode.
Part A — ParallelLedDriverClassic. ESP32-classic-only; Arduino framework. Replaces FastLedDriverModule when selected. Claims N led_data pins (configurable, max 8).
Part B — Per-pin length. Pins can have different strip lengths. The driver handles zero-padding internally — shorter strips get padded to the longest length; the padding isn't written out.
Part C — Frame-sync with Core 1 driver task. Uses the existing semaphore from R3 S4; buffer swap happens between I2S DMA completions.
Definition of Done:
- 4 × 256-LED strips drive in parallel at 50 Hz on ESP32 classic
- Per-strip content independent — test pattern
R, G, B, Won 4 strips visually verified - Footprint: I2S driver adds ≤ 4 KB RAM, ≤ 30 KB flash over FastLED baseline
Result¶
To be completed after implementation.
Retrospective¶
To be completed after implementation.
Sprint 2 — Parallel LED Driver: ESP32-S3 (LCD_CAM)¶
Scope¶
Goal: ESP32-S3 equivalent using the LCD_CAM peripheral — the highest-bandwidth output on S3.
Part A — ParallelLedDriverS3. Up to 16 strips, DMA-driven. Implementation pattern from MoonLight's VirtualDriverS3 port.
Part B — PSRAM buffer option. If PSRAM is detected, allocate channelsD and per-strip transpose buffers in PSRAM. Allows 12 K LEDs at 100 FPS per MoonLight's benchmark.
Definition of Done:
- 8 × 256-LED strips at 100 FPS on ESP32-S3 DevKit
- 12 K total LEDs (16 strips × 768 LEDs) at 100 FPS with PSRAM
- Unit test validates transpose correctness on PC (no hardware needed) by reading the DMA-bound buffer
Result¶
To be completed after implementation.
Retrospective¶
To be completed after implementation.
Sprint 3 — Parallel LED Driver: ESP32-P4 (PARLIO)¶
Scope¶
Goal: ESP32-P4's PARLIO peripheral — 16 strips, variable lengths handled natively without zero-padding penalty.
Part A — ParallelLedDriverP4. Uses esp_driver_parlio component from ESP-IDF. Up to 16 parallel outputs.
Part B — Variable strip length. PARLIO's per-strip length register avoids the zero-padding overhead of I2S/LCD_CAM. 16 strips with dramatically different lengths all complete in the time of the longest.
Part C — Bench target: 98 K LEDs. MoonLight's published ceiling. Unrealistic in this sprint; record what we actually achieve, document the gap.
Definition of Done:
- 16 parallel strips driving on ESP32-P4-Nano
- Variable length verified:
[100, 500, 1000, 2000] × 4 groupsdriven without underrun - Achievable peak LED count captured in Result table
Result¶
To be completed after implementation.
Retrospective¶
To be completed after implementation.
Sprint 4 — Multi-Pin Board Presets¶
Scope¶
Goal: board presets for the common multi-output boards — Dig-Quad, Dig-Octa, SE16 — plus the board-picker UI updates to handle many-pin boards.
Part A — Board presets.
quinled_dig_quad_v3.json— 4 LED outputs, GPIO 16/17/21/22quinled_dig_octa_v2.json— 8 LED outputsse16_v1.json— 16 outputs via the SE16 shift-register board
Each preset specifies the chip family → which parallel driver to auto-select.
Part B — UI support for many pins. Board picker shows output count; pin list collapses to a summary ("8 LED outputs, pins 16–23") when all pins are default.
Part C — Preset-driven driver selection. When a board preset is applied, the correct driver (classic I2S / S3 LCD_CAM / P4 PARLIO / FastLED single) is auto-instantiated.
Definition of Done:
- All 3 presets apply cleanly
- Driver auto-selection verified per board
moddedflag still respected — user-edited pins survive preset reapplication
Result¶
To be completed after implementation.
Retrospective¶
To be completed after implementation.
Sprint 5 — Multi-Board CI¶
Carried from the original Release 3 plan — pulled forward to here where it fits the parallel-driver theme.
Scope¶
Goal: CI runs the full build + test cycle on multiple ESP32 variants.
Part A — PlatformIO environments.
[env:esp32dev]— existing[env:esp32s3]— added[env:esp32p4]— added (compile only; PARLIO headers must be available)
Part B — Multi-board pipeline.
deploy/flash.py -env <env>flashes all test:true devices of a given environmentdeploy/all.pyiteratesdeploy/devicelist.jsonand captures per-device output intodeploy/run/run-<env>-MM-<last4>.log
Part C — GitHub Actions matrix. .github/workflows/ci.yml adds a matrix over the three envs; compile-only in CI (no hardware flash). A nightly job on a self-hosted runner (when available) flashes and captures output.
Definition of Done:
- All 3 envs compile in CI on every push
multi-board-run.shworks locally when 2+ boards are connected- Nightly workflow documented (even if runner isn't provisioned yet)
Result¶
To be completed after implementation.
Retrospective¶
To be completed after implementation.
Sprint 6 — Power Budget Enforcement¶
Scope¶
Goal: the maxPower control introduced in R3 actually clamps brightness instead of just warning.
Part A — Per-frame power estimate. Before the driver writes, PhysicalLayer::clamp() sums channelsD weighted by a per-chip mA-per-channel-at-full constant. Default WS2812: 20 mA per channel at 255 = ~60 mA per pixel at white.
Part B — Proportional scaling. If estimated draw > maxPower (converted to mA at the configured voltage), scale the entire buffer by the ratio. Single multiplication, SIMD-friendly.
Part C — Power telemetry. LightsControlModule exposes estimated_power_w as a read-only display control, updated each frame.
Definition of Done:
maxPower=5 Won a 256-LED WS2812 strip at full white → brightness clamps to ~15% of target- Effective brightness scales smoothly with
maxPowerslider - Test:
estimated_power_wreported value within 5% of measured current on a bench PSU
Result¶
To be completed after implementation.
Retrospective¶
To be completed after implementation.
Sprint 7 — ESP32-P4 Board Support¶
Scope¶
Goal: first-class support for ESP32-P4 variants, including the one with on-board Ethernet and the Wi-Fi coprocessor setup.
Part A — PlatformIO env hardening. [env:esp32p4] and [env:esp32p4_eth] with correct partition tables, flash budgets, and PSRAM configuration.
Part B — Board presets. esp32_p4_nano.json, esp32_p4_eth.json, mhc_p4_nano_shield.json.
Part C — Wi-Fi coprocessor bootstrap. P4 uses an external Wi-Fi coprocessor via SDIO; NetworkModule needs a platform-specific bootstrap that initializes ESP-Hosted before Wi-Fi STA/AP attempt to connect.
Definition of Done:
- All 3 presets apply cleanly on an ESP32-P4-Nano board
- Wi-Fi STA connects via the coprocessor
- Ethernet working on P4-eth variant
Result¶
To be completed after implementation.
Retrospective¶
To be completed after implementation.
Sprint 8 — LED Chip Variants: SK6812 RGBW, WS2815¶
Scope¶
Goal: support chips beyond WS2812.
Part A — SK6812 RGBW. 4 channels per pixel (R, G, B, W). VirtualLayer gains channels_per_light derived from chip config. Effects that care (palette-driven) gain an optional white_blend control; effects that don't care auto-duplicate max(R,G,B) to W or leave W=0 per chip default.
Part B — WS2815 (12V, dual-wire data+backup). Same protocol as WS2812 but 12V supply + redundant data wiring. Driver change is config-only; documentation covers the wiring differences.
Part C — Per-layer chip independence. Different strips can run different chips simultaneously — LayoutPanel on WS2812, LayoutRings on SK6812 RGBW on separate pins.
Definition of Done:
- RGBW strip driven with visible white —
SolidEffect(white=255)lights only the W channel - WS2815 driven on a bench 12V strip
- Mixed-chip test: two layouts, two pins, two chips — both correct
Result¶
To be completed after implementation.
Retrospective¶
To be completed after implementation.
Sprint 9 — APA102 via FastLED v4¶
Scope¶
Goal: SPI-based LEDs (APA102/SK9822) for high-refresh, HD-capable fixtures.
Part A — APA102 driver. Uses FastLED v4 Channels API. SPI pins (SCK + MOSI) claimed via IoModule. Global brightness register exposed (higher dynamic range than PWM).
Part B — HD mode. FastLED v4 "HD" path with temporal dithering — useful for low-brightness gradients without stepping.
Part C — SPI bus coexistence. If W5500 Ethernet is active on SPI (from R7), add a bus-arbitration note + test.
Definition of Done:
- APA102 strip driven from SPI; HD mode visibly smoother at low brightness
- No SPI bus conflict when W5500 + APA102 share the bus on separate CS lines
- Footprint: APA102 path ≤ 10 KB flash over WS2812
Result¶
To be completed after implementation.
Retrospective¶
To be completed after implementation.
Sprint 10 — PWM Channels: CW/WW/CCT/IRGB¶
Scope¶
Goal: non-addressable LED types — constant-current PWM strips and bulbs.
Part A — PwmDriverModule. Drives individual PWM channels. IoModule pin types: pwm_cw, pwm_ww, pwm_r, pwm_g, pwm_b.
Part B — Light-type presets.
IRGB— intensity + R + G + B (4 channels per light, brightness * color)CW+WW— cool white + warm white (2-channel CCT)CCT— a single control (0–255) that linearly mixes CW and WW
Part C — PWM channels as VirtualLayer targets. Effects write normally via setPixel(RGB); the driver resolves color → CCT or IRGB as the config dictates.
Definition of Done:
- CW/WW strip drivable, CCT control slider mixes temperature smoothly
- Mixed-fixture demo: one RGB strip + one CCT bulb + one IRGB strip driven from the same effect stack
- All three PWM light types documented in
docs/modules/drivers.md
Result¶
To be completed after implementation.
Retrospective¶
To be completed after implementation.
Release 6 Backlog¶
- Virtual Driver (shift-register 120 outputs). Planned in MoonLight roadmap; deferred to R11.
- SDI/HDMI out. Professional-video-style output for stage use. Very out of scope for now.
- HUB75 panel support. Large RGB matrix panels via a separate driver. Deferred to R11.
- DMA completion metrics. Per-driver per-frame DMA cost in the Timing module. Pick up when profiling surfaces contention.
- Output group brightness curves. Gamma + white-balance per pin. Pick up when field calibration needs it.