Skip to content

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, W on 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 groups driven 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/22
  • quinled_dig_octa_v2.json — 8 LED outputs
  • se16_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
  • modded flag 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 environment
  • deploy/all.py iterates deploy/devicelist.json and captures per-device output into deploy/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.sh works 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 W on a 256-LED WS2812 strip at full white → brightness clamps to ~15% of target
  • Effective brightness scales smoothly with maxPower slider
  • Test: estimated_power_w reported 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.