From 8630de9d048f2380762dc7afdebeee76c7cccf63 Mon Sep 17 00:00:00 2001 From: ewowi Date: Thu, 29 Oct 2026 09:52:47 +0100 Subject: [PATCH 1/5] Reorganize the backlog into one file per Area, beside the analyses The to-build backlog is one Markdown file per Area of the GitHub Project, grouped by module, each item with its Project fields on one line, so it reads as a document today and moves to the Project by script. Analyses that informed shipped work are gone, their reasoning moved to the explanation, the code and the lessons; the open ones sit beside the backlog as analysis files, and every plan holds its own backlog. **Docs/CI** - docs/work/future: backlog-core, -light and -mixed become ten Area files (Effects, Modifiers, Layouts, Drivers, Services, Core, Platform, UI, MoonLive, Process and docs), 213 items moved verbatim by script with a field line each (Kind, Platform, Trigger, Stage, Process); titles cleaned of status tags, em-dashes and truncation. - Review pass over the items against the code: merges (next-boot config with the re-entrant bring-up, the WiFi retry pair, the audio-sync tests, the script-switch pair, the deferred-UI family, the P4 WiFi cluster), splits (Release 2.0, the MoonBase follow-ups), and deletions with evidence (WiFi runtime disable's stale premise, the empty test-coverage heading, the decided-against records, the size-estimate table). - Analyses: renamed analysis-.md; the LED driver pair and the PSRAM ring merged into analysis-leddriver.md, nRF into analysis-small-boards.md; the MoonLive roadmap split out as analysis-moonlive.md with one backlog item for its ordered steps; the shift-register, livescripts, pins, system-modules, power-functions and generative-fields analyses deleted once their conclusions had a home. - New backlog items carried from the analyses: a scripted driver, a for loop that never advances (traced in the code), the Memory view, BouncingBalls onto the particle kernel, the catalog sweep, an Aurora desktop hitch, SIMD noise loops, the pooling Layer, the '595 expander on Parlio and the Teensy, clocked strips over SPI, frames over USB serial. - The future, present and past indexes carry no process rules; the MoonWalk model moves to docs/explanation/architecture/moonwalk.md; the audio-dsp roadmap and the processes inventory are dissolved into their plans; the archive images are deleted. - Plans: Audio that answers the music (0 of 5) added; the audio, processes, Teensy, input-mapping and Home Assistant plans updated, each plan with its own backlog; Processes plan steps 20 and 21 marked in progress with what is built. - lessons.md: the 16-bit Layer reversal with its measurements, and the floor test before optimizing. - Checks: check_specs, check_prose (0 errors), docs build --strict on the working tree. check_docgen fails on findings in the follow-up commit's paths only (MoonLiveIr.h, platform.h, 09-plan-setup.yaml), none in this diff. Reviewer skipped: docs/work only, moved text verified by script. Co-Authored-By: Claude Opus 5.5 --- docs/explanation/architecture/moonwalk.md | 45 + ...comparison.md => analysis-code-hosting.md} | 0 ...ysis-effects-power-functions-inventory.md} | 2 +- ...-analysis.md => analysis-input-mapping.md} | 167 +- docs/work/future/analysis-leddriver.md | 164 ++ docs/work/future/analysis-moonlive.md | 224 ++ ...t-analysis.md => analysis-small-boards.md} | 86 +- ...tom-up.md => analysis-ui-extensibility.md} | 2 +- ...md => analysis-virtual-layer-downscale.md} | 6 +- .../moonlight/drivers/Art-Net-In.png | Bin 28724 -> 0 bytes .../moonlight/drivers/ArtNetInControls.png | Bin 11104 -> 0 bytes .../moonlight/drivers/ArtNetOutControls.png | Bin 17961 -> 0 bytes .../moonlight/drivers/FastLED-Audio.png | Bin 31593 -> 0 bytes .../moonlight/drivers/FastLED-Driver.png | Bin 40724 -> 0 bytes .../moonlight/drivers/IMUDriverControls.png | Bin 28152 -> 0 bytes .../moonlight/drivers/MPU-6050.jpg | Bin 14181 -> 0 bytes .../moonlight/effects/E_Ball2D.gif | Bin 16867 -> 0 bytes .../moonlight/effects/E_Cosmic3D.gif | Bin 239806 -> 0 bytes .../moonlight/effects/E_Geq2D.gif | Bin 59289 -> 0 bytes .../moonlight/effects/E_Hello1D.gif | Bin 23952 -> 0 bytes .../moonlight/effects/E_Noise2D.gif | Bin 161905 -> 0 bytes .../moonlight/effects/E_Noise3D.gif | Bin 200735 -> 0 bytes .../moonlight/effects/E_Orbit2D.gif | Bin 11545 -> 0 bytes .../moonlight/effects/E_Oscillate2D.gif | Bin 50818 -> 0 bytes .../moonlight/effects/E_Random1D.gif | Bin 43115 -> 0 bytes .../moonlight/effects/E_Sweep2D.gif | Bin 26661 -> 0 bytes .../moonlight/effects/E_Vu1D.gif | Bin 71328 -> 0 bytes .../moonlight/effects/layers.gif | Bin 181128 -> 0 bytes docs/work/future/audio-dsp-roadmap.md | 132 - docs/work/future/backlog-core.md | 2346 +++-------------- docs/work/future/backlog-drivers.md | 590 +++++ docs/work/future/backlog-effects.md | 194 ++ docs/work/future/backlog-layouts.md | 25 + docs/work/future/backlog-light.md | 1150 -------- docs/work/future/backlog-mixed.md | 80 - docs/work/future/backlog-modifiers.md | 24 + docs/work/future/backlog-moonlive.md | 777 ++---- docs/work/future/backlog-platform.md | 667 +++++ docs/work/future/backlog-process-and-docs.md | 406 +++ docs/work/future/backlog-services.md | 124 + docs/work/future/backlog-ui.md | 124 + .../generative-fields-analysis-bottom-up.md | 237 -- .../generative-fields-analysis-top-down.md | 582 ---- docs/work/future/index.md | 18 +- .../future/led-driver-psram-ring-analysis.md | 175 -- .../future/leddriver-analysis-bottom-up.md | 631 ----- .../future/leddriver-analysis-top-down.md | 543 ---- .../future/livescripts-analysis-bottom-up.md | 237 -- .../future/livescripts-analysis-top-down.md | 445 ---- docs/work/future/moonwalk.md | 172 -- docs/work/future/pins-analysis-bottom-up.md | 119 - docs/work/future/pins-analysis-top-down.md | 159 -- .../power-functions-analysis-bottom-up.md | 192 -- .../power-functions-analysis-top-down.md | 288 -- docs/work/future/processes-inventory.md | 671 ----- .../future/shift-register-driver-analysis.md | 414 --- docs/work/future/small-boards.md | 48 - docs/work/future/system-modules.md | 96 - docs/work/past/index.md | 10 +- docs/work/past/lessons.md | 18 +- ...ght migration (multi-stage, superseded).md | 10 +- ... MoonLive scripts are classes (shipped).md | 2 +- ... and a filesystem change seam (shipped).md | 2 +- ...ows (x86_64 host-JIT backend) (shipped).md | 6 +- ...ve types for MoonLive scripts (shipped).md | 2 +- ...7 - Config backup and restore (shipped).md | 8 +- ...- Sprites and flying toasters (shipped).md | 6 +- ...0260915 - Native HUB75 output (shipped).md | 6 +- ...s, one report for simplicity (13 of 13).md | 4 +- ...s point as Network submodules (shipped).md | 2 +- ...oonLive Coord3D and RGB types (shipped).md | 2 +- ...ate by subscription, streamed (shipped).md | 2 +- ...Help in the UI, from the docs (shipped).md | 2 +- ... - Internal RAM on the ESP32 (10 of 10).md | 2 +- .../moonlight-improvements (superseded).md | 2 +- ... mapping and scripted sensors (7 of 12).md | 17 +- ...1001 - Gamepads and MIDI desks (2 of 7).md | 4 +- .../Plan-20261010 - Teensy 4.1 (3 of 6).md | 10 +- ...udio reactive live performance (0 of 7).md | 19 +- ...ms, every frame from outside (11 of 13).md | 2 +- ... - Processes, described once (17 of 23).md | 27 +- ...261025 - Home Assistant wall controller.md | 2 +- ...- Audio that answers the music (0 of 5).md | 129 + docs/work/present/index.md | 8 +- 84 files changed, 3464 insertions(+), 9202 deletions(-) create mode 100644 docs/explanation/architecture/moonwalk.md rename docs/work/future/{code-hosting-comparison.md => analysis-code-hosting.md} (100%) rename docs/work/future/{effects-power-function-inventory.md => analysis-effects-power-functions-inventory.md} (88%) rename docs/work/future/{input-mapping-analysis.md => analysis-input-mapping.md} (52%) create mode 100644 docs/work/future/analysis-leddriver.md create mode 100644 docs/work/future/analysis-moonlive.md rename docs/work/future/{nrf-zephyr-target-analysis.md => analysis-small-boards.md} (81%) rename docs/work/future/{ui-extensibility-analysis-bottom-up.md => analysis-ui-extensibility.md} (97%) rename docs/work/future/{virtual-layer-downscale-study.md => analysis-virtual-layer-downscale.md} (94%) delete mode 100644 docs/work/future/archive_images/moonlight/drivers/Art-Net-In.png delete mode 100644 docs/work/future/archive_images/moonlight/drivers/ArtNetInControls.png delete mode 100644 docs/work/future/archive_images/moonlight/drivers/ArtNetOutControls.png delete mode 100644 docs/work/future/archive_images/moonlight/drivers/FastLED-Audio.png delete mode 100644 docs/work/future/archive_images/moonlight/drivers/FastLED-Driver.png delete mode 100644 docs/work/future/archive_images/moonlight/drivers/IMUDriverControls.png delete mode 100644 docs/work/future/archive_images/moonlight/drivers/MPU-6050.jpg delete mode 100644 docs/work/future/archive_images/moonlight/effects/E_Ball2D.gif delete mode 100644 docs/work/future/archive_images/moonlight/effects/E_Cosmic3D.gif delete mode 100644 docs/work/future/archive_images/moonlight/effects/E_Geq2D.gif delete mode 100644 docs/work/future/archive_images/moonlight/effects/E_Hello1D.gif delete mode 100644 docs/work/future/archive_images/moonlight/effects/E_Noise2D.gif delete mode 100644 docs/work/future/archive_images/moonlight/effects/E_Noise3D.gif delete mode 100644 docs/work/future/archive_images/moonlight/effects/E_Orbit2D.gif delete mode 100644 docs/work/future/archive_images/moonlight/effects/E_Oscillate2D.gif delete mode 100644 docs/work/future/archive_images/moonlight/effects/E_Random1D.gif delete mode 100644 docs/work/future/archive_images/moonlight/effects/E_Sweep2D.gif delete mode 100644 docs/work/future/archive_images/moonlight/effects/E_Vu1D.gif delete mode 100644 docs/work/future/archive_images/moonlight/effects/layers.gif delete mode 100644 docs/work/future/audio-dsp-roadmap.md create mode 100644 docs/work/future/backlog-drivers.md create mode 100644 docs/work/future/backlog-effects.md create mode 100644 docs/work/future/backlog-layouts.md delete mode 100644 docs/work/future/backlog-light.md delete mode 100644 docs/work/future/backlog-mixed.md create mode 100644 docs/work/future/backlog-modifiers.md create mode 100644 docs/work/future/backlog-platform.md create mode 100644 docs/work/future/backlog-process-and-docs.md create mode 100644 docs/work/future/backlog-services.md create mode 100644 docs/work/future/backlog-ui.md delete mode 100644 docs/work/future/generative-fields-analysis-bottom-up.md delete mode 100644 docs/work/future/generative-fields-analysis-top-down.md delete mode 100644 docs/work/future/led-driver-psram-ring-analysis.md delete mode 100644 docs/work/future/leddriver-analysis-bottom-up.md delete mode 100644 docs/work/future/leddriver-analysis-top-down.md delete mode 100644 docs/work/future/livescripts-analysis-bottom-up.md delete mode 100644 docs/work/future/livescripts-analysis-top-down.md delete mode 100644 docs/work/future/moonwalk.md delete mode 100644 docs/work/future/pins-analysis-bottom-up.md delete mode 100644 docs/work/future/pins-analysis-top-down.md delete mode 100644 docs/work/future/power-functions-analysis-bottom-up.md delete mode 100644 docs/work/future/power-functions-analysis-top-down.md delete mode 100644 docs/work/future/processes-inventory.md delete mode 100644 docs/work/future/shift-register-driver-analysis.md delete mode 100644 docs/work/future/small-boards.md delete mode 100644 docs/work/future/system-modules.md create mode 100644 docs/work/present/Plan-20261028 - Audio that answers the music (0 of 5).md diff --git a/docs/explanation/architecture/moonwalk.md b/docs/explanation/architecture/moonwalk.md new file mode 100644 index 000000000..928b111f3 --- /dev/null +++ b/docs/explanation/architecture/moonwalk.md @@ -0,0 +1,45 @@ +# MoonWalk + +MoonWalk is the model every MoonLight process is written in, and the map that shows them. The processes live in the stage files under [moonwalk/](../../../moonwalk/), validated by [the schema](../../../moonwalk/moonwalk.schema.json). How to use the map and write a process is [the MoonWalk reference](../../reference/moonwalk.md); why the model has its shape follows. + +## What it does + +- **One model** holds every process, the end user's and the contributor's, so the whole journey is one map. +- **The map is navigable**: on the website and in the app, zoomed out to the whole model, zoomed in by search and filters, and opened at the place the reader is, a docs page or the card open in the app. +- **A process already done is not offered again**: what the reader has done is known, and the map and the app's suggestions leave it out. +- **The pages named for an activity are generated from it**: building, testing, integrating, troubleshooting, updating the firmware, logging an issue and the rest of the how-tos. So a process carries its prose as well as its steps. +- **Every runner reads the same steps**: the docs, the app, the tests and the agents. + +## Three levels, one textbook each + +| Level | What it is | Textbook | Zoom | +|---|---|---|---| +| The journey | stages, left to right: install, first light, everyday use, advanced use, maintain, integrate, plan and set up, develop, run on hardware, test, document and commit, review and merge, release | the user story map (Patton) | zoomed out | +| A process | a goal, its actor, the processes it depends on, its steps, its branches | an Arazzo workflow | middle | +| A step | a precondition, an action, an expectation | Given, When, Then (Gherkin) | zoomed in | + +**Arazzo-shaped, not Arazzo.** Arazzo requires every step to call an API operation or another workflow, and most of the journey's steps are a click in the app, a command or a person plugging in a board. So the model keeps Arazzo's structure and names (`dependsOn`, `onSuccess`, `onFailure`, a workflow per process, several per document) with its own step shape. A process whose steps are all API calls can be exported to Arazzo, which is where Redocly's Respect and Spectral come in. + +## One YAML file per stage + +The number in a stage file's name orders the backbone. The files are YAML because a process carries prose: multi-line text, Markdown and comments read well in YAML and badly in JSON strings, and MkDocs already depends on PyYAML. The schema validates YAML and JSON alike. YAML 1.1 reads an unquoted `on`, `off`, `yes` or `no` as a boolean, so the generator rejects a boolean where a name belongs. + +## A step names an intent + +A `when` names one action by intent rather than gesture, so each runner picks its own gesture: the app points at the card's +, and a test applies a state document. The runner list is derived from the action, never written down. A runner meeting an action it cannot perform describes it, as the scenario runner prints SKIP. + +**A process is a how-to, never a tutorial.** Diátaxis keeps the two apart: a tutorial is a lesson with an outcome its teacher chose, a how-to serves the reader's own goal. A step says what to decide or do toward that goal; an example value goes in its detail, never in its check, and a check tests only what holds for any reader. The lessons live in MoonTube's clips and in the tutorials, and a step may embed a clip. + +Running the same script from several processes is a call, its explanation living once, in the script's MoonDeck section or the process that owns it. **A choice is an input**, a JSON Schema as Arazzo's workflow inputs are, and a step runs only for the option its `given` names. + +**Text and shape have caps, as the help's cards do.** Text past a cap goes to its Diátaxis kind rather than being squeezed: a table to a reference page, a reason to an explanation, a symptom to Troubleshooting, a second route to its own process. + +## Done is observed + +**A step is done when its `then` holds now.** The app reads its own state, so "WiFi is set up" is done the moment a network is known, however it got there. This is the onboarding checklist's textbook rule, completion observed rather than recorded, and it needs no storage. A process is done when every expectation the state can answer holds, and it has at least one: its last step alone is not enough, since a last step is often one the state cannot see. + +What the state cannot show is vouched for by whoever knows it. The installer marks the install done, since it knows the platform for certain where a running device cannot tell a desktop from a Linux board or a container. MoonDeck marks a process done when every script its steps run last passed, and a run counts only while it is newer than the sources it covers, as a firmware build does. Those marks stay in the browser. The website cannot read a device, since an https page cannot call a device's http address, so there only the reader's marks count. + +## The map + +`moonwalk.js` draws `moonwalk.json` with Cytoscape.js, from cdnjs and about 430 KB, so it never enters the firmware. The layout is the story map's grid, stages as columns and processes stacked in `dependsOn` order, computed in a few lines rather than by a layout library. The parts of a page that are reference rather than steps, such as a table of ports or a test strategy, stay on a reference page and are linked. diff --git a/docs/work/future/code-hosting-comparison.md b/docs/work/future/analysis-code-hosting.md similarity index 100% rename from docs/work/future/code-hosting-comparison.md rename to docs/work/future/analysis-code-hosting.md diff --git a/docs/work/future/effects-power-function-inventory.md b/docs/work/future/analysis-effects-power-functions-inventory.md similarity index 88% rename from docs/work/future/effects-power-function-inventory.md rename to docs/work/future/analysis-effects-power-functions-inventory.md index dc47efdd2..3e42330bb 100644 --- a/docs/work/future/effects-power-function-inventory.md +++ b/docs/work/future/analysis-effects-power-functions-inventory.md @@ -1,6 +1,6 @@ # Effects × power functions — inventory -> **Forward-looking backlog document — exception to CLAUDE.md present-tense rule.** One row per effect in the tree on 2026-09-03 (58), recording what each uses today and which power functions it could run on: the ones that exist ([power-functions.md](../../moonmodules/light/power-functions.md)) and the ones the [generative-fields top-down](generative-fields-analysis-top-down.md) builds (gradient noise, `PolarLut`, `Oscillators`, `advect` and the velocity rules, `decay`, `lineAA`/`disc`; the 16-bit Layer was proposed and then reversed, top-down § 11). It is the input for the catalog sweep that top-down names as a follow-on plan, under the product owner's rule: **every effect runs on the power functions for whatever they cover; an effect that does not is rewritten on them; algorithmic effects keep their logic; a rewrite lands only if at least as beautiful as the effect ran before.** Rows shrink as effects are rewritten; a rewritten effect's row is deleted, not ticked. +One row per effect in the tree on 2026-09-03 (58), recording what each uses today and which power functions it could run on, all of them in [power-functions.md](../../moonmodules/light/power-functions.md): gradient noise, `PolarLut`, `Oscillators`, `advect` and the velocity rules, `decay`, `lineAA` and `disc` among them. It is the input for [the catalog sweep](backlog-effects.md#the-catalog-sweep-every-natural-motion-effect-onto-the-power-functions), under [the migration rule](../../moonmodules/light/power-functions.md#migrating-an-effect). A rewritten effect's row is deleted, not ticked. **Kind.** N = natural motion (physics, fields, noise, trails, rotation, oscillation): rewritten on the kernels. A = algorithmic (a rule set or a game is the effect): keeps its logic, draws through the library. M = audio meter (bars, levels, spectra): the geometry and ballistics rows apply. U = utility or test: no rewrite owed. Anything in doubt is N. diff --git a/docs/work/future/input-mapping-analysis.md b/docs/work/future/analysis-input-mapping.md similarity index 52% rename from docs/work/future/input-mapping-analysis.md rename to docs/work/future/analysis-input-mapping.md index 62d8e3683..5472b1202 100644 --- a/docs/work/future/input-mapping-analysis.md +++ b/docs/work/future/analysis-input-mapping.md @@ -3,21 +3,6 @@ Analysis, not a decision. Written 2026-09-01 after `ButtonService` shipped with a single button and a single target, which does not scale to a board with four buttons or a remote with twelve keys. -## The problem with what shipped - -`ButtonService` maps **one** pin to **one** `Module.control` string, and `IrService` carries a fixed -table of five actions (on/off, brightness up/down, palette next/prev) with a learned code each. -Neither survives contact with real hardware: - -- A QuinLED Dig-Next-2 has three buttons; a Penta has three; a stage rig has a pedalboard. -- A remote has twenty keys, and which five are "the" actions is the firmware's opinion, not the - user's. -- Adding a sixth IR action today means editing `kActions` and reflashing, which is exactly the - configured-at-build-time model the project exists to avoid - ([MoonModule, live reconfiguration](../../explanation/architecture/moonmodule.md#live-reconfiguration-every-change-applies-on-the-next-frame)). - -Both modules have the same shape of defect, so they want the same fix. - ## The two-step interface The product owner's framing: **everything goes through ControlModule**. An input service learns to @@ -53,19 +38,6 @@ special case. The surface is the recommended path, not an enforced one. ## What the mapping table looks like -Both services grow **a list of mappings** in place of their single target or fixed action table. The -mechanism exists: `Control::addList` with an editable-row hook (`addListRow`), which -`ControlModule` (presets), `DevicesModule`, `PinsModule` and `TasksModule` already use. - -A row is roughly: - -| field | button service | IR service | -|---|---|---| -| source | pin, and its `activeLow` / pull | the learned code (hex) | -| event | press / release / click / long-press | (a code is one event) | -| target | `Control.switch1`, or any `Module.control` | same | -| mode | latching / momentary | set / toggle / nudge +N | - **The trigger vocabulary is worth borrowing.** ESPHome models each input as its own entry (not a central table, which matches the per-service list here) and its useful contribution is the *event* set: `on_press`, `on_release`, `on_click` with a length window, `on_double_click`, `on_multi_click`. @@ -73,22 +45,6 @@ A long-press and a double-press are what let three physical buttons drive nine a difference between a usable installation panel and a wall of switches. Start with press / release / long-press; double and multi-click are the same machinery with more timers. -## Rotary encoders - -Not a button, and worth being explicit: an encoder emits **quadrature** on two pins (A and B), where -the phase relation gives direction and each detent is a step. So it is a `+1 / -1` stream, not a -level, and it maps naturally onto an `encoderN` (a relative nudge), where a button maps onto a -`switchN`. - -Most encoders also carry a **push switch**, which is a plain button on a third pin, so an encoder -row is "two pins for rotation, optionally one for the press". Whether that is one `EncoderService` -with its own list, or an `input type` column in a single service's list, is the open design -question below. - -Reading quadrature reliably needs either an interrupt or a fast poll: the ESP32's PCNT (pulse -counter) peripheral does it in hardware and is the textbook answer, with a 20 ms poll of the count. -That is a new platform seam either way, so an encoder is more work than a button, not less. - ## Foot pedals: not USB Worth correcting an assumption recorded earlier in the backlog. Stage pedals are **1/4" jacks**, not @@ -155,26 +111,7 @@ is: a stream, with events derivable from it. The VL53L8CX (and its VL53L5CX predecessor) is not a distance sensor with extra decimals. It reports an **8x8 grid of zones at up to 60 Hz**, over I2C or SPI at 3 MHz: a low-resolution depth image at -render rate. That is a third shape beside event and stream, and it changes three things. - -**It is a frame, not a value.** 64 zones is a *layout* worth of data, and the natural consumers are -an effect that maps zones onto lights (a hand's shape moving across a wall) or a modifier that masks -by depth. A single `distance` control cannot carry it, and neither can a fader. - -**The read cost is the constraint, not the bandwidth.** 64 zones at 2 bytes is 128 B/frame, which is -7.7 kB/s at 60 Hz: nothing. But a blocking I2C read of 128 bytes at 400 kHz takes **~2.9 ms**, and -the render tick on a Dig-2-Go is 289 us. So this sensor must not be read on the render tick at all: -either SPI at 3 MHz (which is why the part offers it), a slower ranging rate matched to what the -effect needs, or the read moved off the render core the way the encode split already is -([MoonModule, parallelism](../../explanation/architecture/moonmodule.md#parallelism)). **This is the first sensor whose -platform seam has to be asynchronous**, and that is worth knowing before the synchronous -`i2cReadRegs` shape is treated as sufficient for everything. - -**It argues the published frame should be sized by the sensor**, not fixed. A `SensorFrame` with one -`distance` field would be wrong here; an 8x8 array is 128 bytes a consumer reads in place, which the -shared-struct pull handles fine (it is a POD overwritten in place), but the header has to declare -the shape. The honest answer is probably a frame type per sensor family, as `AudioFrame` is specific -to audio rather than a general `SignalFrame`. +render rate. That is a third shape beside event and stream; what it changes is step 4b of [the plan](../present/Plan-20260901%20-%20Input%20mapping%20and%20scripted%20sensors%20%287%20of%2012%29.md). **It carries an 84 KB firmware blob**, measured from ST's own driver ([stm32duino/VL53L8CX](https://github.com/stm32duino/VL53L8CX), `vl53l8cx_buffers.h`): @@ -186,29 +123,6 @@ to audio rather than a general `SignalFrame`. | driver RAM | ~2.3 KB (`VL53L8CX_Configuration`) | | result struct | ~1 KB per frame | -**The boot upload is the bigger problem than the per-frame read**, and it was not the number to -worry about first: a blocking 2-second transfer in `setup()` stalls the device. SPI at 0.23 s is the -answer, which gives a second independent reason to prefer SPI for this part. - -**The blob cannot be written ourselves.** It is executable firmware for the sensor's own processor, -not configuration that could be derived: ST publishes no register-level datasheet for this part, -only the ULD API, and the ranging algorithms and SPAD calibration are not documented. Reimplementing -it means reverse-engineering an undocumented DSP. - -**So it is compiled in per firmware, not per build.** 84 KB is about 2% of a 4 MB classic ESP32's -flash, and roughly 4.5% of the ~1.8 MB app PARTITION it actually competes for, which is the number -that matters; it is noise on an S3 or P4. This is the first *peripheral* to need -compile-time inclusion gated by the device catalog, the shape `MM_HLS` and `MM_NO_ETH` already -have. A Dig-2-Go never carries it; a P4 installation does. - -**MoonLive can still USE it, and that is the distinction to keep.** A script cannot upload 84 KB and -should never try, but it does not need to: the compiled module owns the blob and the bus, and the -script calls a builtin (`tofZone(x, y)`) exactly as it calls `beat()` or `paletteRGB()` without -implementing a beat or a palette. The power-function pattern is what makes a heavyweight sensor -scriptable. What a script cannot do is *be* the driver for such a sensor, which is the honest -boundary of the scripted-sensor claim: a script suits a sensor whose init is a handful of register -writes (MPU6050, BH1750, VL53L0X), and a blob sensor is a compiled module by necessity. - **No prior art to lean on, checked rather than assumed.** `troyhacks/WLED` (the tracked friend repo) carries `usermods/VL53L0X_gestures` on all three of its P4 branches, but that is upstream WLED's usermod for the *simple* VL53L0X, present identically in `wled/WLED`, and a code search for @@ -223,19 +137,7 @@ the hard case. See step 4b. A GPIO carries one bit. The sensors an installation actually wants mostly do not: a motion sensor worth having reports distance or an occupancy zone, not a level, and that arrives over a bus. -**I2C is the common case, and the seam is already written.** An unmerged commit -(`11f8eb76`, "Add GyroDriver (MPU6050) + generic platform I2C layer") adds exactly the three -functions a sensor module needs, with the register knowledge staying in the module: - -```cpp -bool i2cInit(uint8_t sdaPin, uint8_t sclPin); -bool i2cWriteReg(uint8_t devAddr, uint8_t reg, uint8_t value); -bool i2cReadRegs(uint8_t devAddr, uint8_t reg, uint8_t* buf, size_t len); -``` - -This branch has only `i2cScan` (which addresses ACK, the `i2cdetect` operation) and therefore -cannot read a register at all. So **merging that layer is the prerequisite** for every I2C sensor, -and it is prior art from a contributor rather than something to design. +**I2C is the common case, and the seam is already written**: step 4 of [the plan](../present/Plan-20260901%20-%20Input%20mapping%20and%20scripted%20sensors%20%287%20of%2012%29.md) names the commit and the three functions. What it unlocks, in the order an installation asks for it: a **ToF distance sensor** (VL53L0X: a hand's distance drives a parameter, the well-behaved version of "motion"), a **light-level sensor** @@ -253,17 +155,6 @@ category confusing: ESP32 is the same PCNT/RMT machinery an encoder wants, so the two features share a dependency. - **ToF** is I2C, so it rides the layer above. A **mmWave radar** such as the HLK-LD2450 is UART, so it needs the UART seam the [audio-reactive plan](../present/Plan-20261013%20-%20Audio%20reactive%20live%20performance%20%280%20of%207%29.md) names. -**I2S is audio-shaped, and should stay that way.** The platform's I2S surface is `audioMicInit` / -`audioMicRead`, deliberately named for what it does rather than for the peripheral, and -`AudioService` is its only consumer. A few non-audio sensors do speak I2S (some ToF and radar -modules stream samples that way), but generalising the audio seam to serve them would trade a clear -contract for a vague one. If such a sensor is ever wanted, it gets its own seam named for its own -job, exactly as `audioMic*` is. **No action: I2S is done, for audio.** - -The other reason to be careful: I2S is the mic's bus, and a second I2S consumer on the same board -competes for a peripheral instance. That is a real constraint on a classic ESP32 (two I2S -controllers), not a theoretical one. - ## MoonLive: scripted sensors and mappings The direction is that MoonLive gains driver scripts whose hello-world is "read from GPIO, write to @@ -272,16 +163,7 @@ because the two halves want different things from a script. **Half one: a script AS a sensor.** A sensor nobody has written a module for is exactly what a script is good at, since the module is small and the value is in the register knowledge, which is -data rather than architecture. Builtins, matching the seams above: - -```c -int gpioRead(int pin); // shipped seam -void gpioWrite(int pin, int on); // shipped seam -int i2cRead(int addr, int reg); // needs 11f8eb76 merged -void i2cWrite(int addr, int reg, int val); -``` - -A VL53L0X script is then a `defineControls` for the address and pins, a `setup` that writes the +data rather than architecture. A VL53L0X script is then a `defineControls` for the address and pins, a `setup` that writes the init registers, and a `tick20ms` that reads two bytes. That is a page of code, and a user with a datasheet can write it without a toolchain. **This is the strongest argument for scripted services**: the long tail of sensors is unbounded, and no firmware can carry all of them, but a script per @@ -292,34 +174,6 @@ becomes optional for anything unusual: "if distance < 50 cm then switch1 on, els lines, and expresses a rule no fixed table column could. The table stays the answer for the common case (a button drives a switch), because a table is inspectable and a script is not. -**The `setControl` decision is the gate**, and it is bigger here than it looked when first raised. A -script that can write any control on any module can also write a driver's pin list or a layout's -geometry. Options, in increasing order of restriction: expose it plainly and trust the author (a -script is already native code on the device, so this is not a sandbox boundary); restrict it to the -`Control` module, which makes the surface the script's only reach and matches the two-step model -above; or give a scripted service a declared list of targets it may write, which is the most work -and the most inspectable. **The middle option is the one that fits this analysis**: a script drives -the surface, the surface drives everything, and the same two steps hold whether the mapping came -from a table or a script. - -**What it needs beyond builtins:** a `MoonLiveService` host module, the service twin of -`MoonLiveEffect`, carrying a `script` control and the compile/status path. Small, mostly a copy of -the existing binding, and it is what makes a scripted sensor addable from the picker like any other -module. - -## USB game controllers - -Unchanged from the earlier entry, and confirmed as its own project: a HID **report descriptor -parser** is the work (each controller lays its bytes out differently; the descriptor is a nested -tag/value blob that must be parsed to learn which bit is which button), plus USB Host bring-up. -S3/P4 only, since the classic ESP32 has no USB Host peripheral at all. The *mapping* half is free -once reports arrive: buttons to switches or pads, axes to encoders and faders, through the same -list. - -DIY is the alternative worth naming: a gamepad's buttons wired to GPIOs is `ButtonService` with a -longer list, and an analog stick is two ADC channels. For an installation where the enclosure is -custom anyway, that may be the better answer than parsing a commercial controller. - ## What the rules say - **[CLAUDE.md, industry standards](../../CLAUDE.md)**: take the textbook construct. Quadrature via @@ -332,18 +186,3 @@ custom anyway, that may be the better answer than parsing a commercial controlle the concrete defect in `IrService` today. - **[MoonCore, services](../../explanation/architecture/index.md)**: "Direction is per-module, not a role: a service may read (gyro), write (relay), or both." Input services are already the sanctioned shape. - -## Open questions for the product owner - -1. **One service or several?** `InputService` with a `type` column per row (button / encoder / IR / - analog), or `ButtonService`, `EncoderService`, `IrService` each with their own list? One service - is fewer modules and one table to learn; several keep each module's controls honest (an encoder's - two pins do not belong on a button row). The catalog leans toward several, since a board declares - what it has. -2. **Does `IrService` keep its action table** as a convenience default, or become purely - code -> surface mappings? Purely mappings is cleaner and loses the out-of-the-box remote. -3. **What happens to the shipped `ButtonService.target`?** A single row is the same thing as a - one-row list, so the migration is mechanical, but it is a breaking change to a control that has - shipped (in this branch only, so far). -4. **How far into the trigger vocabulary** to go in the first pass: press/release only, or - long-press too? Long-press is what multiplies a small panel's reach. diff --git a/docs/work/future/analysis-leddriver.md b/docs/work/future/analysis-leddriver.md new file mode 100644 index 000000000..20655367a --- /dev/null +++ b/docs/work/future/analysis-leddriver.md @@ -0,0 +1,164 @@ +# LED drivers: what is not built yet + +What shipped and where it lives: [Choosing and wiring an LED driver](../../reference/hardware/led-drivers.md) for the drivers and their limits, [MoonLight, drivers](../../explanation/architecture/moonlight.md#drivers) for the pipeline, [What bounds a parallel driver](../../explanation/architecture/moonlight.md#what-bounds-a-parallel-driver) and [performance.md](../../reference/performance.md#multi-pin-led-driving-all-three-peripherals-128128-grid) for the measurements, and the headers under `src/light/drivers/` and `src/platform/esp32/` for each peripheral's design. This file holds the research behind four things that are not built, each with its backlog item. + +## LED output from a Raspberry Pi's own pins + +Backlog: [LED output from a Raspberry Pi's own pins](backlog-platform.md#led-output-from-a-raspberry-pis-own-pins-robwomp-2026-10-11). The prerequisite is a `src/platform/rpi/` backend; today a Pi runs the desktop build and reaches LEDs through the network drivers. + +**Pi 4 and earlier (BCM2711 and before).** rpi_ws281x (BSD-2, ) drives the header by DMA through PWM (2 channels), PCM (1) or SPI (1), up to about 2,700 lights a string on PWM and 5,400 on PCM. It is the reference for that route; its last release is v1.0.0 of March 2023. + +**Pi 5 (BCM2712 with the RP1 southbridge).** GPIO, PWM, DMA, SPI and I2C sit behind RP1, so every driver that mapped BCM283x registers from userspace stops working (rpi_ws281x issues #528, #554, #555, #564). Three routes exist: + +- **SPI MOSI**, one lane per bus, each WS2812 bit encoded in SPI bits at 2.4 MHz: the rpi5-ws2812 package (). The one path every Pi shares, and the one to start with. +- **PWM through a kernel module**, rpi_ws281x's Pi 5 branch (): `rp1_ws281x_pwm.ko` plus a device-tree overlay, one channel working, concurrent channels not. +- **RP1's PIO**, the forward path. RP1 carries one PIO block of the RP2040's design, 4 state machines and 32 instruction slots. Its registers are not reachable over the PCIe link, only the state machines' FIFOs through DMA, so userspace goes through the kernel's `rp1-pio` driver, which piolib wraps (, BSD-3). WS2812 is piolib's own example (), one lane. Parallel lanes would feed pre-encoded slots through a FIFO by DMA, the shape `GpioDmaPeripheral` uses on the Teensy, and a '595 expander would hang off the same slots; nobody has shipped either on RP1. + +**HUB75 on a Pi.** On a Pi 1 to 4, hzeller's rpi-rgb-led-matrix () writes the GPIO registers directly, and its options catalog the panel variety a backend has to cover: multiplexing schemes, row-address types, driver-chip init sequences such as FM6126A, chained and parallel panels, PWM depth, a pixel mapper. On a Pi 5, Adafruit's PioMatter () drives up to 3 panels through the PIO, alpha. Both are study material: MoonLight writes its own from the chip datasheets, with credit. + +**Not a route.** WiringPi (a community fork since its author stopped) and libgpiod make a call per pin change, far too slow and jittery for WS2812's 150 ns edges or a HUB75 refresh; they serve buttons and relays. + +## The classic ESP32 past 2,048 lights + +Backlog: [Classic-ESP32 shift-register ring on raw I2S](backlog-drivers.md#classic-esp32-shift-register-ring-on-raw-i2s-the-high-light-count-classic-driver). + +### Verdict + +**Yes: PSRAM can lift the classic-ESP32 ceiling, and the mechanism is a memory-model change, not a shift-register feature.** Our 2048-light wall is a direct consequence of the *whole-frame* DMA model: `esp_lcd`'s i80 backend materializes every light's WS2812 waveform in one contiguous DMA-capable block before the transfer starts, and on the classic ESP32 that block **cannot be PSRAM**: the IDF source rejects it outright (`esp_lcd_panel_io_i2s.c`: `ESP_RETURN_ON_FALSE((caps & MALLOC_CAP_SPIRAM) == 0, NULL, TAG, "external memory is not supported")`), because the classic chip's I2S DMA physically cannot address the external-RAM window. So the DMA buffer scales linearly with the light count against a ~76 KB internal wall. + +The ring shipped as the `MoonI80` peripheral beside `i80`: [Choosing and wiring an LED driver](../../reference/hardware/led-drivers.md#which-one). + +**A scoping fact that shapes the work: the ring is not available off the shelf.** This is not a matter of adopting a library. The mainstream ESP32 LED libraries use the same **whole-frame DMA** we do and therefore share our internal-RAM ceiling; the refill-ring model is effectively unique to hpwit's drivers, which is why § 2 studies **his source** rather than surveying alternatives. Two consequences: (1) building it is genuinely new capability for a classic-ESP32 LED controller, not catching up; (2) per [*Industry standards, our own code*](../../CLAUDE.md#principles), we study his implementation hard and then **write our own against our architecture**: carry the idea forward, not the code. + +### The PSRAM question + +#### Two memory models + +**Whole-frame (MoonLight today, via `esp_lcd` i80).** The DMA buffer *is* the frame: every light's expanded WS2812 waveform, 3 slots per bit, one byte (8-lane) or two (16-lane) per slot, materialized in one contiguous DMA-capable block, handed to the peripheral in a single chained transfer. The CPU is out of the loop for the whole frame, which is why this model is **underrun-immune by construction**: nothing remains to refill, so no ISR storm can starve it. The price is linear scaling: + +```text +internalBytes(whole-frame) = maxLaneLights × channels × 3 slots × slotBytes +``` + +**Refill ring (hpwit, under the raw I2S registers).** The DMA buffer holds only a small rolling window: a ring of `nbDmaBuffer` buffers, each **one pixel-row of slots** wide. On each buffer-EOF the I2S ISR calls `loadAndTranspose()`, which reads the next pixel row from the source array and SWAR-transposes it into the buffer the DMA is about to reach. Internal RAM: + +```text +internalBytes(ring) = (nbDmaBuffer + 2) × (nbComponents × 8 bits × 3 slots × 2 bytes/word) + = (nbDmaBuffer + 2) × 144 B [RGB] +``` + +**The formula has no LED-count term.** That is the decoupling, and it is the entire point. + +**Crucially, "ring buffer" names two different things and only one solves the ceiling.** The distinction is the whole decision, and it is easy to lose: + +| | **(A) Refill ring** | **(B) Ping-pong / double buffer** | +|---|---|---| +| What's in a buffer | one pixel row of slots | a **whole frame** | +| Who fills it, when | the **DMA-completion ISR**, mid-transfer | the CPU, **before** the transfer | +| Source array touched by | **CPU only** → **PSRAM-eligible** | DMA → must be internal | +| Internal RAM vs LED count | **constant** | **scales linearly** | +| Buys you | **lifts the memory ceiling** | hides wire time behind DMA | +| In our tree | *(this proposal)* | **`asyncTransmit`** (already shipped) | + +We already have (B). It is genuinely valuable, it is what took the S3 from ~13 to ~34 fps at 16 K, but **it does not and cannot lift the memory ceiling**, which is exactly why halving the DMA buffer by turning `asyncTransmit` off didn't help the WROVER. + +#### What hpwit's source actually says + +Read directly from [`src/I2SClocklessLedDriver.h`](https://github.com/hpwit/I2SClocklessLedDriver/blob/main/src/I2SClocklessLedDriver.h) / `.cpp` (branch `main`): + +- **A ring of linked `lldesc_t` descriptors.** `struct I2SClocklessLedDriverDMABuffer { lldesc_t descriptor; uint8_t* buffer; };`. `initDMABuffers()` allocates `nbDmaBuffer + 2` buffers of `nbComponents * 8 * 2 * 3` bytes each (**144 B RGB / 192 B RGBW**) plus one 4×-size terminator. Chained `descriptor.qe.stqe_next = &next->descriptor`, last wrapping to `[0]`: a genuine ring. +- **`uint8_t nbDmaBuffer = 6;`**: default 6, and **runtime-tunable** via `updateDriver(..., uint8_t dmaBuffer, ...)` → `nbDmaBuffer = dmaBuffer;`. (This is why the product owner's StarLight-era setting of **75** was reachable: it's a plain count, not a compile-time constant.) +- **The ISR refills and transposes.** `static void IRAM_ATTR interruptHandler(void*)`: on `I2S_OUT_EOF_INT` it increments `ledToDisplay` and calls `loadAndTranspose()`, which reads `driver->leds + ledToDisplay * nbComponents` per strip, applies brightness/gamma maps, and SWAR-transposes (`transpose16x1Noinline2`) into `dmaBuffersTampon[dmaBufferActive]->buffer`. **One buffer = one LED slot, so the ISR fires per LED (~30 µs at 800 kHz).** +- **Ring buffers are internal; the framebuffer is the caller's.** Ring: `heap_caps_malloc(bytes, MALLOC_CAP_DMA)`. Framebuffer: `initled(uint8_t* leds, ...)` takes a raw pointer the driver only ever reads **with the CPU**. Nothing DMA-touches it → **a PSRAM pointer works by construction.** +- **The ISR is deliberately arranged to be allowed to read PSRAM.** `esp_intr_alloc(interruptSource, ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LEVEL3, ...)` with the in-code comment that `ESP_INTR_FLAG_IRAM` was *"removed to avoid Cache Disabled but Cached Memory Region Accessed"*, i.e. the refill ISR is explicitly permitted to touch cache-mapped (PSRAM) memory. That is the design decision that makes the PSRAM framebuffer legal, stated in hpwit's own source. +- **The whole-frame mode exists too, and is the one that scales.** `FULL_DMA_BUFFER` allocates `numLedPerStrip + 2` buffers, and is precisely the mode behind [issue #11, "DMA mode takes up 60k of ram just for a single 300 led strip"](https://github.com/hpwit/I2SClocklessLedDriver/issues/11). **hpwit's library contains both models, and the scaling complaint lands on the whole-frame one, the same wall we hit.** + +**Honest caveat:** hpwit never writes "put `leds[]` in PSRAM" in so many words *for the classic chip* (the explicit PSRAM-framebuffer statements are in the S3 variants). The classic conclusion is drawn from construction, CPU-only reads from a caller-supplied pointer, in a non-IRAM Level-3 ISR, which is sound but is **inference from source, not a vendor promise.** The bench spike in § 5 is what turns it into a measured fact. + +#### The memory math, concretely (8 lanes, RGB) + +Whole-frame, `slotBytes = 1` (8-lane bus), 3 channels, 3 slots/bit, our i80 model: + +| Lights/lane | Total (8 lanes) | Whole-frame DMA buffer | Fits ~76 KB internal? | +|---|---:|---:|---| +| 256 | 2,048 | ~18 KB | ✅ **measured working** | +| 512 | 4,096 | ~37 KB | ❌ **measured fail** (`i80 bus init failed`): the real wall sits below the arithmetic: ~76 KB is the *largest free block*, and `asyncTransmit` wants two, plus IDF descriptor overhead | +| 1024 | 8,192 | ~74 KB | ❌ | +| 2048 | 16,384 | ~147 KB | ❌ | + +Ring, same 8 lanes, RGB (hpwit's own sizing, 144 B/buffer): + +| `nbDmaBuffer` | Ring internal RAM | Lights supported | +|---|---:|---| +| 6 (default) | (6+2) × 144 = **1,152 B** | **any**: 2 K, 16 K, 130 K; the count does not appear in the formula | +| 32 | (32+2) × 144 ≈ **4.9 KB** | **any** | +| 75 (StarLight-era) | (75+2) × 144 ≈ **11 KB** | **any** | + +**Read those against each other.** The whole-frame model needs 147 KB of a ~76 KB budget to reach 16 K lights, so it cannot. The ring reaches 16 K, or 130 K, on **1.6–11 KB**, and even a *very* deep 75-buffer cushion costs ~11 KB. The frame itself moves to PSRAM, where 16 K lights × 3 B = 48 KB is a rounding error against 4 MB. + +#### What the ring gives the WROVER, and what binds next + +With the internal-RAM wall gone, the binding constraints become, in order: + +1. **Wire time, the hard physical floor.** WS2812 is ~30 µs per light per lane (24 bits × 1.25 µs). At 8 lanes × 2048/lane = 16,384 lights: 2048 × 30 µs = **~61 ms → ~16 fps**, before any CPU cost. A protocol constant no driver escapes; it is why "more LEDs" past a point is a *distribution* problem. +2. **Encode CPU in interrupt context, the ring's true price.** Our whole-frame model transposes once with a SWAR kernel on the main path out of internal RAM (measured **~7.6 µs/light** on classic). The ring does comparable work **inside an ISR, per LED, reading cache-mediated PSRAM**. hpwit fires that ISR once per LED (~30 µs apart): so the refill must reliably complete within the wire time of one pixel. This is the number to measure first. +3. **PSRAM bandwidth / cache stalls**, the same story from the other side: an ISR that stalls on a cache miss is an ISR that is late, which is item 4. +4. **The underrun cushion** (§ 2.5). + +**Honest bottom line for a 4 MB-PSRAM WROVER with a ring driver:** the **memory ceiling disappears** (the frame is kilobytes against megabytes) and the practical ceiling becomes **wire time and ISR headroom**: comfortably into the **8–16 K** range at single-digit-to-low-teens fps. That is a **4–8× lift over our measured 2048**, and the right framing is *"the ceiling stops being about RAM."* **This is not theory:** a prior firmware built on hpwit's ring (the product owner's own MoonLight / WLED-MM lineage) raises its light boundary to **130 K on PSRAM against 4096 without it**: a ~32× gap between the PSRAM and non-PSRAM configuration of the *same* firmware, which is the empirical shape of ring-vs-whole-frame. + +#### The cost: underrun, and why `nbDmaBuffer` is tunable + +The ring's weakness is the exact mirror of whole-frame's strength. Every buffer completion needs the ISR to refill *before* the DMA consumes the next one. If a higher-priority ISR (WiFi), or a flash-cache-off window, or a PSRAM stall delays it, the DMA runs off the end of valid data and **the strip glitches**: WS2812 latches on a gap of only ~150 µs. + +`nbDmaBuffer` **is** the cushion: it buys `nbDmaBuffer × (one pixel's wire time)` of slack. hpwit says so directly in the shift-register driver's README, *"Sometimes interrupts can disturb the pixel buffer calculations hence making some artifacts. A solution … is to calculate several buffers in advance. By default we have 2 dma buffers. this can be increased"*, and notes the ceiling case: pushing it to `NUM_LEDS_PER_STRIP` is the full frame, "but it requires too much memory" (i.e. it degenerates back into our model). Mitigations visible in his source: a deeper ring, `_DMA_EXTENSTION` idle padding, **core pinning** (`enableShowPixelsOnCore()`), and **Level-3 interrupt priority** so the refill preempts ordinary WiFi ISRs. + +This is not a niche worry: every ESP32 LED library that streams rather than whole-frames fights it, and they all expose the same remedy: a **tunable count of DMA buffers**, raised when interrupt load (typically WiFi) causes flicker. So the cushion depth is a **recognized, standard tuning parameter, not a bespoke hack**: which matters under [*Common patterns first*](../../CLAUDE.md#principles): if we build the ring, `nbDmaBuffer` is the knob users already expect. + +### Sources + +**hpwit (primary, source read line-by-line):** +- [I2SClocklessLedDriver](https://github.com/hpwit/I2SClocklessLedDriver) · [`src/I2SClocklessLedDriver.h`](https://github.com/hpwit/I2SClocklessLedDriver/blob/main/src/I2SClocklessLedDriver.h): `lldesc_t` ring; `nbDmaBuffer = 6` default, runtime-tunable via `updateDriver()`; `initDMABuffers()` → `nbDmaBuffer + 2` buffers of `nbComponents * 8 * 2 * 3` B (144 RGB / 192 RGBW) + 4× terminator; `IRAM_ATTR interruptHandler()` → `loadAndTranspose()` reading `driver->leds`; ring buffers `heap_caps_malloc(..., MALLOC_CAP_DMA)`; `initled(uint8_t* leds, ...)` caller-supplied framebuffer; `esp_intr_alloc(..., ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LEVEL3, ...)` with `ESP_INTR_FLAG_IRAM` "removed to avoid Cache Disabled but Cached Memory Region Accessed"; optional `FULL_DMA_BUFFER` whole-frame mode. +- [issue #11, "DMA mode takes up 60k of ram just for a single 300 led strip"](https://github.com/hpwit/I2SClocklessLedDriver/issues/11): the whole-frame mode's scaling complaint. +- [PR #47, `__NB_DMA_BUFFER` as var & PSRAM-prefer](https://github.com/hpwit/I2SClocklessLedDriver/pull/47). +- [I2SClocklessVirtualLedDriver](https://github.com/hpwit/I2SClocklessVirtualLedDriver): "8 strips out of one single pin … 8x15=120 strips"; 74HC595 per virtual pin + 74HC245 level shifter; `__NB_DMA_BUFFER` default 2, buffers `(NUM_VIRT_PINS+1) * nb_components * 8 * 3 * 2` ≈ 1152 B; "Artifacts due to interrupts" → "calculate several buffers in advance"; `enableShowPixelsOnCore()`; 12,000-LED worked example; 75 → 129 fps second-core figures. +- [I2SClockLessLedDriveresp32s3](https://github.com/hpwit/I2SClockLessLedDriveresp32s3): the S3 lineage (PSRAM buffers; S3 EDMA can DMA from PSRAM). + +- [The predecessor MoonLight](https://github.com/ewowi/MoonLight) / [WLED-MM](https://github.com/MoonModules/WLED-MM): a prior firmware riding hpwit's ring on PSRAM: LEDs pre-allocated in PSRAM, light boundary **130 K** (vs **4096** non-PSRAM). The empirical demonstration that the ring's ceiling lift is real. + +**Espressif (the load-bearing hardware constraint):** +- [`esp_lcd/i80/esp_lcd_panel_io_i2s.c`](https://github.com/espressif/esp-idf/blob/master/components/esp_lcd/i80/esp_lcd_panel_io_i2s.c): `ESP_RETURN_ON_FALSE((caps & MALLOC_CAP_SPIRAM) == 0, NULL, TAG, "external memory is not supported");` +- [Support for External RAM, ESP-IDF (ESP32)](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/external-ram.html): external RAM "cannot be used as a place to store DMA transaction descriptors or as a buffer for a DMA transfer to read from or write into"; prescribed workaround = internal DMA-able buffer + copy. **Confirms: no IDF API feeds PSRAM into classic-ESP32 I2S DMA.** +- [Support for External RAM, ESP-IDF (ESP32-S3)](https://docs.espressif.com/projects/esp-idf/en/stable/esp32s3/api-guides/external-ram.html): the S3 *does* have hardware DMA-to-PSRAM (descriptors still internal): why our S3 i80 reaches 16 K and classic cannot. + +**MoonLight's own measurements:** +- [performance.md § Multi-pin LED driving](../../reference/performance.md#multi-pin-led-driving-all-three-peripherals-128128-grid): classic i80 2048-light ceiling + `esp_lcd_i80_alloc_draw_buffer` rejecting `MALLOC_CAP_SPIRAM`; S3 16,384 @ ~34 fps; P4 Parlio 4096, 139 fps @ 1024; the `multicore` +44 % table. +- [backlog-drivers.md](backlog-drivers.md): the superseded chunk-streaming-ring decision; the shift-register driver's **48 × 256 = 12,288** acceptance floor. + +## The pin expander on the other peripherals + +The 74HCT595 expander ships on `i80` and `MoonI80`, the LCD_CAM peripheral of the S3, P4 and S31 ([Wiring a 74HCT595 pin expander](../../reference/hardware/led-drivers.md#wiring-a-74hct595-pin-expander)). What it needs from a peripheral: a shift clock on a pin, the latch as one bit of every bus word, and eight bus words per slot, so the DMA frame grows eightfold. RMT has no clock line and never qualifies. The other two parallel peripherals could: + +- **Parlio (P4).** `ParlioPeripheral::supportsPinExpander()` is false. The transmit unit generates its pixel clock itself, and `platform_esp32_parlio.cpp` leaves `clk_out_gpio_num` unconnected because WS2812 ignores it; connected, it is the '595's shift clock. Whether Parlio's clock output and bit packing carry the expander's protocol is a bench question, and the eightfold frame meets the single-transaction ceiling the [chunked transfer](backlog-drivers.md#chunked-transfer-step-4-the-16k-lever) lifts. Backlog: [A '595 expander on Parlio](backlog-drivers.md#a-595-expander-on-parlio). +- **GPIO-DMA (Teensy 4.1).** `GpioDmaPeripheral::supportsPinExpander()` is false. The port write carries no clock of its own, so the shift clock has to come from the writes themselves, at a DMA rate not yet measured. FlexIO clocks by itself, and [the Teensy plan](../present/Plan-20261010%20-%20Teensy%204.1%20(3%20of%206).md) set it aside because no 8 contiguous FlexIO pins stay free beside the W5500. TriantaduoWS2811 () is the prior art: 32 strands from 3 Teensy pins through FlexIO clocking '595s at zero CPU cost. Backlog: [A '595 expander on the Teensy's GPIO-DMA bus](backlog-drivers.md#a-595-expander-on-the-teensys-gpio-dma-bus). +- **RP1 PIO (Pi 5).** Research grade, above. + +The expander multiplies slots, not pins, so each peripheral's frame or ring grows eightfold too: the memory question arrives with the clock question. + +## The sigrok cross-check + +Backlog: [sigrok/fx2lafw cross-check + MoonDeck LED driver test script](backlog-process-and-docs.md#sigrokfx2lafw-cross-check-moondeck-led-driver-test-script). + +The loopback test trusts the chip's own clock, and on an ESP32 it runs on the chip whose WiFi causes the flicker it looks for. An analyzer with its own clock crosses it: where the two disagree there is a real bug. The reference device is a NanoDLA v1.3 (CY7C68013A, fx2lafw firmware, 24 MS/s on 8 channels, 10 to 15 dollars); any board sigrok's `fx2lafw` driver enumerates works the same. 24 MS/s is 41 ns a sample, enough to tell a 350 ns pulse from a 700 ns one and marginal for a timing-margin test (320 against 380 ns). A DSLogic Plus (16 channels at 100 MS/s) is the upgrade when a flicker reproduces that 24 MS/s cannot resolve, and the scripts carry over unchanged. + +Wiring: the analyzer's GND to a board GND over the shortest jumper, and channel D0 (labeled CH0) to the data GPIO, beside the loopback jumper. The analyzer is high impedance and loads nothing; 3.3 V GPIO into a 3.3 V, 5 V tolerant input needs no shifter or resistor. Never power the board from the analyzer's rail, and give a real strand its own 5 V with its ground on the same common ground. + +```bash +sigrok-cli --scan # one fx2lafw entry +sigrok-cli -d fx2lafw -c samplerate=24m --samples 100000 \ + -P ws2812:data=D0 -A ws2812=rgb +``` + +The decoder (libsigrokdecode's `ws2812`) prints one line per pixel in transmit order, to compare against the bytes the firmware sent. At 24 MS/s, 100,000 samples is about 4 ms, a 30-light frame with its latch; a longer strand raises `--samples` or triggers on the first edge. Gotchas in the order they come: no common ground reads as random toggling; the wrong channel; a sample rate left at the driver's lower default, where a 350 ns pulse is three samples and zeros read as ones (always pass `samplerate=24m`); and when no frame decodes, PulseView on the same capture shows the missing idle gap or the stuck bit by eye. + +The real flicker test is a sustained capture of seconds with WiFi associated and a packet flood, every frame decoded for a slipped byte or a short latch gap. diff --git a/docs/work/future/analysis-moonlive.md b/docs/work/future/analysis-moonlive.md new file mode 100644 index 000000000..26fdaf7e0 --- /dev/null +++ b/docs/work/future/analysis-moonlive.md @@ -0,0 +1,224 @@ +# MoonLive: the language roadmap + +[MoonLive](../../explanation/architecture/moonlive.md) is the engine as built (grammar, IR, codegen), and [power-functions.md](../../moonmodules/light/power-functions.md) is the builtin surface it calls into. This analysis is the ordered plan for closing the gap between them: which language features to add next, and why that order. Every limit quoted was measured against the shipped compiler, not read off the source. The steps are one backlog item, [The next language steps, in order](backlog-moonlive.md#the-next-language-steps-in-order), and the code cap is [its own item](backlog-moonlive.md#the-code-cap-per-platform-2026-10-16). + +## What this is for + +MoonLive compiles a C-subset to native code on the device. The subset is deliberately small: it +started at "fill a buffer with a color" and grew a feature at a time. This document is the ordered +list of what to grow next, and why that order. + +**The bar is a corpus, not one effect.** The predecessor MoonLight publishes a body of live scripts at +[ewowi/MoonLight/livescripts](https://github.com/ewowi/MoonLight/tree/main/livescripts), +and the goal is that MoonLive can **run them**, not that we migrate them into this repo. They are +written against a language with floats, structs and a wide builtin surface, so they are an honest +external measure of how far the subset still has to grow: each one that compiles unchanged is a +feature below that landed, and each one that does not names the next gap. + +The measure throughout is **what a real effect needs**. A simulation effect (particles, physics, +anything where this frame depends on the last) is the honest test, because it exercises state, +arithmetic and control flow together rather than one at a time. `moonlive/effects/particles/balls.mle` is the +worked example: bouncing balls, written against the language as it stands today, and every +compromise it had to make is a line item below. + +## The ceilings + +The limits a script meets are the constants in [MoonLiveBuiltins.h](../../../src/core/moonlive/MoonLiveBuiltins.h) and [MoonLiveIr.h](../../../src/core/moonlive/MoonLiveIr.h), each with its reason beside it. + +## What a simulation effect gives up today + +Each row is a compromise the balls effect makes, and the language feature that would remove it: + +| forced to | because | wants | +|---|---|---| +| 4 objects, not 25 | 64-byte arena | a bigger arena, or a pool handle (shipped for particles) | +| one flat color | no `hsv()` builtin | `hsv()` | +| one array per field | no structs | structs | + +## The library is already built; the engine is what is missing + +The library recorded what it needs from the engine while it was built, deferred so the compiled +side could finish first. That list is the real roadmap, and it is more specific +than "add a language feature": + +5. **Two entry shapes:** `frame()` for composing kernels (the scalable path) and `pixel(x,y,z)` + for per-pixel ergonomics (honest ceiling around 32×32). +6. **Stateful handles** (a `Pool`, a `BeatPhase`): script-declared, arena-allocated at compile + time, passed as an opaque first argument. No script-side memory management. + +Item 5 is worth noting against the arena work below: a particle pool as a HANDLE means the script +does not spend its own 64 bytes on particle state at all. That is a better answer than widening +the arena, and it is already designed. + +## The type system: the settled design (2026-08-23) + +The types as shipped: [the six types](../../moonmodules/light/moonlive.md#the-six-types) and [fractional arithmetic with `fixed`](../../moonmodules/light/moonlive.md#fractional-arithmetic-with-fixed); what the design did not anticipate: [the five-types plan](../past/plans/Plan-20260823%20-%20Five%20types%20for%20MoonLive%20scripts%20(shipped).md). + +**The two edges of `fixed`, stated so they are conventions rather than traps:** +- **Time never goes into `fixed` raw**: 32,768 seconds is 9.1 hours. `t` stays `int` milliseconds + and rhythm reaches a script through `beat()`/`beatsin()`, which is already the idiom. +- **Deep fractal zoom bottoms out at ~16 bits of magnification**, where the step becomes a pixel. + The same class of limit Q13 has at 13 bits, and better than float32 past that point (its 23-bit + mantissa is shared with the integer part). Extreme zoom is a 64-bit-builtin problem, not a + scalar-type problem. + +**`float` stays out, with a clean conscience.** The whole compiled library, the particle kernel +and escape() are integer/fixed already; math16.h supplies sin/atan/dist/sqrt; the hot path makes +float promotion a fatal warning. The FPU story splits the boards (LX6/LX7/P4 have one, the RISC-V +line does not), so JIT float either runs wildly differently per board or softfloats, against the +one-language-four-ISAs-identical rule. Fixed-point is bit-identical everywhere, which is also what +makes an effect reproducible. If the predecessor's corpus (written with floats) ever forces the issue, +the answer is compile-time float-literal-to-fixed translation, not runtime float. + +**Per-type notes:** +- `bool[]` ships byte-backed first (elements are 0/1 either way), and the 8-per-byte packing lands + later as a pure memory optimization when an automaton effect pays for its read-modify-write + codegen. Same rule as every constant here: against a script that needs it, not on speculation. +- Where arrays LIVE: small arrays stay in the arena (internal RAM); grid-sized state goes through + ScratchBuffer handles (item 9b's model), which prefer PSRAM and fall back to internal heap on + the classic ESP32, whose fixtures are small by construction. `byte[]` is what keeps a classic + heat map at 1x rather than 4x. + +## The order, and why + + +Ordered by **what removing it buys**, not by implementation cost. + +### 3. A bigger arena and more members: *check the handle route first; storage rules now in the type-system design above* + +64 bytes of member data is why an effect holds four objects rather than twenty-five. + +**But check the handle route first.** The power-functions spec's item 5, a particle pool as an +arena-allocated HANDLE, means a simulation effect stops storing its own particle state entirely, +which removes the pressure without touching these constants. Widen the arena for the scripts that +genuinely hold their own state; do not widen it as a substitute for handles. + +Not purely a constants bump, and the blockers are known: + +- `kCtrlBytes` / `kMaxCtrls` are both `uint8_t`, so the arena caps at 255 bytes before any type + change. 150 bytes of particle state fits under that; much more does not. +- `static_assert(kCtrlBytes <= 64, "seeded_ is a 64-bit mask, one bit per script arena byte")` + (`MoonLive.h:270`) is the real gate. Past 64 bytes the seeded-member mask needs re-indexing, + and there is a worked example, because it was widened 16 → 64 once already. The assert exists + because the earlier `uint32_t` version silently aliased members mod 32. +- Watch `sizeof(MoonLive)`: why growing it grows every scripted module is on [MoonLiveParticles](../../../src/light/moonlive/MoonLiveParticles.h). + +### 4c. Insights from porting a per-pixel effect: *the cost is CALLS, not math* + +A second port (`moonlive/effects/waves/octopus.mle`, a polar spiral) measured very differently from the +first, and the difference is the useful part. + +| effect | shape | S3, 64x64 | +|---|---|---| +| balls | four small discs, ~500 lit pixels | **1278 us** | +| octopus | every pixel, every frame | **21762 us** | + +A third port (`metal.mle`, an SDF shader) put a number on the most expensive builtin, measured on +shiffy's 80x48: + +| variant | tick | vs plasma | +|---|---|---| +| plasma (9 calls/px, no sqrt) | **16031 us** | baseline | +| metal, 2 `polarR`, no `uv` | **32843 us** | 2.0x | +| metal, 2 blobs + `uv` | **46221 us** | 2.9x | +| metal, 3 blobs + `uv` | **59600 us** | 3.7x | + +**`polarR` costs ~13400 us per call site per frame at 3840 pixels: about 3.5 us per pixel, one +builtin.** It wraps `dist16`, a real square root, and `draw.h` already measures a sqrt-based SDF at +~108 cycles/px against ~14 for the squared form. A squared-distance builtin (`polarRSq`, or letting +a script compare against `r * r` as `ripples.mle` does) is the cheap fix, and it is the same trick +the compiled effects already use. + +Also measured and **disproved**: hoisting the four loop-invariant `beatsin` calls out of the inner +loop into members moved 59600 to 58459 us. Call overhead per se is NOT the cost here: the square +roots are. Worth recording because it contradicts the natural first guess. + +That is ~5.3 us per pixel, and it is not the arithmetic; it is **~32,000 host calls per frame**. +Each `polarA`/`polarR`/`sin`/`scale`/`beat` is a real call through the builtin ABI, and a +whole-canvas effect makes eight or so per pixel. + +Two things follow, and they sharpen the priorities above: + +1. **Per-pixel builtins want to be inline ops, not calls.** `setPaletteColor` is a Call and that is + fine at ~500 pixels; at 4096 the call overhead dominates. The `Inline` kind already exists + (`setRGB` uses it); the polar pair and the palette write are the candidates. +2. **A `frame()` entry shape would sidestep it entirely.** The power-functions spec's item 4, + compose kernels over a whole frame rather than script per pixel, is exactly the answer for + this class of effect, and octopus is the concrete case that argues for it. + +Worth stating plainly: 35 fps for a full-canvas 64x64 effect is usable, and the port needed no +new language features beyond two builtins. The ceiling here is call overhead, not expressiveness. + +### 4b. Predefined structs: what remains after `Coord3D` and `RGB` + +**No `HSV` struct.** A hue wheel is how an effect picks color while ignoring the user's palette, so `HSV` stays in `setPalEntryHSV`, which authors a palette rather than bypassing one. + +What remains: `setColorFromPalette(pos, index, brightness)` taking a `Coord3D`, and arrays of structs (§ 10). + +### 6. Function arguments and return values: *moderate, removes a real footgun* + +The forward call is the cheapest to build and the most confusing to meet, since the message is the +one an actual typo produces: a second pass over the class body resolves it, which the code comment +names as the reason it is refused rather than half-supported. + +### 10. User structs, and arrays of them: *readability, once the arena is bigger* + +`ball[i].x` instead of parallel arrays. Genuinely nicer and closer to how a precompiled effect +reads, but parallel arrays work the moment the arena is big enough. Last because #1 removes most +of the pain, not because it does not matter. + +Arrays of the five scalar types already ship (`byte heat[16]` in `ember.mle`), so what is missing +here is arrays OF STRUCTS, and it splits in two. An array of a PREDEFINED struct (§ 4b) is the +smaller half and the one an effect reaches for first: a script holding `Coord3D pos[8]` is exactly +the parallel-array flattening this item exists to remove, and the element width the array path +already carries (`idxPack`) is the machinery it needs. An array of a USER struct needs the general +declaration machinery above it. Worth building in that order if this is picked up. + +### 11. What the StadBeest legs and eyes hit (2026-10-07) + +Two effects written in one session on real hardware: `stadbeest-walk.mle` (a walk cycle on 10 tubes, dancing to music) and `stadbeest-frog-eye.mle` (a frog's eye on a 241-light ring disc). Besides the member and label limits above, each of these cost a workaround: + +| limit | what it cost | wants | +|---|---|---| +| "codegen failed: assembler overflow (branch range, slot, or immediate)" | names neither the limit nor the line; finding it took a desktop bisect with `disasm.py` | the message names the table that ran out and the function it was filling | +| "too many arguments to hold" | fires when the eye's `tick()` held too many live locals, so the wording sends the author to the calls | a message about live variables when that is the cause | +| a failed recompile leaves the effect dark | on a live installation every edit that does not compile switches the light off until a fixed one arrives; it happened three times in the session | keep running the last program that compiled, and show the error beside it | +| `setPaletteColor` clamps the palette index | a palette is circular, but `hue + offset` past 255 sticks at the last color unless the script writes `mod(..., 256)` | wrap the index, as the palette itself does | +| an effect visits every grid pixel | the eye computes 1600 pixels to light 241, and the legs' grid is mostly empty between 10 tubes | a way to iterate only the layout's lights, for sparse layouts | +| a `bool` control takes a min and a max | `addControl("pulse", pulse, 0, 1)` | `addControl(name, flag)` for a bool | + +### 12. A one-to-one port of hpwit's scripts, for a like-for-like comparison (2026-10-19) + +hpwit's compiler (`hpwit/new-parser`, `asmparser2`) runs whole C-like programs. His `multi_effect_controller.sc` (800 lines) and the five `ProceduralEffects` were ported to MoonLive on 2026-10-18 (`moonlive/projects/hpwit/`), but as the same LOOK rather than the same program, so code size and frame time do not compare like for like. A one-to-one port needs the language to accept his programs as written. Each construct was compiled on the desktop on 2026-10-19: + +| his scripts use | MoonLive today | what it takes | work | +|---|---|---|---| +| ~10 KB of state: struct arrays of 160 to 220 objects, a 40×80 trail, 12 KB heat and angle tables | 64 bytes of members, 16 members | an arena sized from the declarations, on the heap or PSRAM, past the `uint8_t` and 64-bit seeded-mask limits; item 3 has the blockers | L, 3 to 5 days | +| ~30 functions: struct methods and free functions | 8 per class (`kMaxEntryPoints`) | a function table sized per script; free functions outside the class | M, 1 to 2 days | +| `struct` with fields, methods and a constructor, arrays of them | refused | item 10, user structs, then their methods and arrays | XL, 1 to 2 weeks | +| `float`, `uint8_t`, `uint32_t`, casts like `(int)(x)` | refused | `float` (every target but the RP2040 has an FPU) or float mapped onto `fixed`; the sized integer names as aliases; casts | S for the names and casts, 1 day; M to map `float` onto `fixed`, 2 days; XL for a real `float` on five backends, 1 to 2 weeks | +| `grid[y][x]` (his `[y, x]`) | refused | 2D arrays over the existing element-width path | M, 1 to 2 days | +| `while`, `break`, `continue`, `i++`, `+=`, `?:`, `switch` | all refused | the statements and operators, each a parser and IR addition | S each for `while`, `++` and `+=` (rewritten into what exists), 1 day together; M each for `break`/`continue`, `?:` and `switch`, 3 to 5 days together | +| `#define` constants | refused | a constant declaration (`const int`) rather than a preprocessor | S, half a day | +| `main()` with `while (true)`, `show()`, `delay()` | `tick()` per frame | a shim: the loop body becomes `tick()`, `show()` and `delay()` become no-ops, `millis()` reads `t` | M, 1 to 2 days | +| `hsv()`, `rand(mod)`, `atan2`, `hypot`, `sin` on radians, `printfln`, `pinInterrupt` | palette, `random16`, `polarA`/`polarR`, `sin` on 0..65535, `print`, none | host bindings with his signatures, under his names, in a compatibility table | M, 2 days, once `float` is decided | +| code past 16 KB | `kCodeCap` 16 KB | the cap per platform (§ The code cap, per platform) | S to M, 1 day | + +Work is in days of agent implementation with the product owner's review, for the parser, the IR and all five backends, each with its tests; S is under a day, M one to two, L a week, XL two. In all about five weeks with `float` mapped onto `fixed`, about seven with a real `float`, the structs and the arena being most of it. + +Most rows are general language growth the MoonLive scripts want anyway; the `main()` shim and his host bindings exist only for the comparison, and stay behind a compatibility table rather than entering the standard vocabulary. **Decide first** whether the comparison is worth the compatibility layer, or whether comparing on the ported looks, now in `moonlive/projects/hpwit/`, is enough. + +## How to know a step landed + +Two measures, one local and one external. + +**Local:** re-port a simulation effect after each step and delete the row it removes from the +compromise table above. A port with no rows left (dozens of shaded, colored objects with +fractional velocities on a full-size grid, at a frame rate the bench can measure) means the +language carries a real simulation. + +**External, and the harder bar:** take the +[predecessor's livescripts corpus](https://github.com/ewowi/MoonLight/tree/main/livescripts) and +count how many compile and run unchanged. That number is the honest progress metric, because those +scripts were written without regard for our subset. Track it per step; a feature that moves it is +worth more than one that does not, whatever this document guesses. diff --git a/docs/work/future/nrf-zephyr-target-analysis.md b/docs/work/future/analysis-small-boards.md similarity index 81% rename from docs/work/future/nrf-zephyr-target-analysis.md rename to docs/work/future/analysis-small-boards.md index a051e4459..85d684acf 100644 --- a/docs/work/future/nrf-zephyr-target-analysis.md +++ b/docs/work/future/analysis-small-boards.md @@ -1,14 +1,44 @@ -# nRF (Nordic) + Zephyr as a MoonLight target — feasibility analysis +# Small boards: RP2350, RP2040 and Nordic -> **Forward-looking research document — exception to CLAUDE.md present-tense rule.** Feasibility study only, no implementation. Assesses whether the Nordic nRF52/53/54 BLE SoC families, running Zephyr (nRF Connect SDK), can join the supported-target list the way Teensy does — a constrained secondary target behind the existing `src/platform/` seam. Researched 2026-07; sources cited at the bottom. +The microcontroller boards MoonLight's next ports target, and where to buy them in the EU. **No MoonLight firmware runs on these boards.** They are the targets of the next ports, and a board moves from here to a page of its own when its port ships. The Teensy 4.1 has [its own page](../../reference/hardware/teensy-4-1.md). -## Verdict +Prices are from 2026-10-11 and include Dutch VAT unless marked ex. AliExpress is worth it only for Waveshare, Seeed and WeAct boards through their official stores, checking the seller per listing; Raspberry Pi and Nordic have no official store there. + +## RP2350 + +| | Board (maker) | Why | Where, price | +|--|--|--|--| +| Raspberry Pi Pico 2 W | [Pico 2 W](https://www.antratek.nl/raspberry-pi-pico-2-w) (Raspberry Pi) | The reference board, with WiFi | Antratek €12.04, [Kiwi](https://www.kiwi-electronics.com/en/raspberry-pi-pico-2-w-with-header-20342) €8.77 | +| W5500-EVB-Pico2 | [W5500-EVB-Pico2](https://thepihut.com/products/wiznet-w5500-evb-pico2) (WIZnet) | Wired Ethernet | The Pi Hut £11.20 | +| Pimoroni Pico Plus 2 W | [Pico Plus 2 W](https://shop.pimoroni.com/products/pimoroni-pico-plus-2-w) (Pimoroni) | 8 MB PSRAM, 16 MB flash, WiFi | about €25, pre-order | +| Waveshare RP2350-Zero | [RP2350-Zero](https://www.tinytronics.nl/en/development-boards/microcontroller-boards/others/waveshare-rp2350-zero-mini-development-board) (Waveshare) | The tiny one | Tinytronics €6, [AliExpress](https://www.aliexpress.com/w/wholesale-waveshare-rp2350-zero.html) about €6 | + +## RP2040 + +| | Board (maker) | Why | Where, price | +|--|--|--|--| +| Raspberry Pi Pico W | [Pico W](https://www.antratek.nl/raspberry-pi-pico-w) (Raspberry Pi) | The only RP2040 with WiFi | Antratek €9.08, Kiwi €6.52 | +| Waveshare RP2040-Zero | [RP2040-Zero](https://www.tinytronics.nl/en/development-boards/microcontroller-boards/others/waveshare-rp2040-zero) (Waveshare) | The mini one people own | Tinytronics €5, [AliExpress](https://www.aliexpress.com/w/wholesale-waveshare-rp2040-zero.html) | +| Seeed XIAO RP2040 | [XIAO RP2040](https://www.kiwi-electronics.com/en/seeed-studio-xiao-rp2040-10969) (Seeed) | The XIAO form factor | Kiwi €4.46 | + +## Nordic + +No Nordic development kit is needed: the XIAO and the Debug Probe below cover the nRF52840, and the nRF5340 comes after it, on a module board. + +| | Board (maker) | Why | Where, price | +|--|--|--|--| +| Seeed XIAO nRF52840 Plus | [XIAO nRF52840 Plus](https://www.antratek.nl/seeed-studio-xiao-nrf52840-plus) (Seeed) | Tiny, UF2 bootloader, debugged through the Debug Probe | Antratek €12.09 | +| Raytac MDBT53-DB-40 | [MDBT53-DB-40](https://www.digikey.nl/en/products/detail/raytac/MDBT53-DB-40/16630695) (Raytac) | The nRF5340, once the nRF52840 port runs. Zephyr supports it. | DigiKey, about €15 ex. Raytac ships it in about 14 days, with its own shipping fee. | + +## Nordic and Zephyr as a target + +### Verdict **As a big-LED controller: poor fit. As a portable-core + BLE-UI showcase: a genuinely interesting spike, gated on one load-bearing question (BLE-UI feasibility), which the research below finds to be *clean, not hacky*.** Strongest yes: Zephyr is a first-class C++20 + BSD-sockets + LittleFS RTOS whose primitives map onto almost every function in `platform.h` cleanly *and* MoonLight's core is deliberately domain-neutral, so "MoonLight's runtime runs on a tiny BLE chip" is a real, achievable, marketable claim — the port is mechanical, not architectural. Strongest no: the chips have no WiFi, no PSRAM, and no wide parallel LED DMA peripheral, so nRF loses badly at the large-installation job MoonLight's light domain is tuned for. The reframe from the product owner (see [§ Reframing](#reframing-core-as-library-and-the-ble-ui-question) below) changes the weighing: the question is not "can nRF drive 16 K LEDs" (it can't) but "is MoonLight's **core** a compelling showcase on a small BLE device, and can its **UI** reach that device over BLE with no WiFi/HTTP?" On the first, the core is ~90% portable as-is. On the second — the crux — a BLE GATT channel comfortably carries our JSON control/state model (BLE 5 sustains ~100–700 kB/s, orders of magnitude above what the UI needs), and config-over-Web-Bluetooth is well-trodden prior art. The one honest caveat is client reach: **Web Bluetooth is Chrome/Edge/Android/desktop only — never iOS Safari** — so a no-install browser UI works everywhere except iPhone, where a companion app or a third-party BLE browser is required. That caveat shapes the spike; it does not sink it. Recommendation moves from "defer" to **"a scoped showcase spike is justified regardless of the LED limits, if the iOS-client gap is acceptable for a demo."** -## The nRF hardware +### The nRF hardware | Chip | Core(s) | Clock | RAM | Flash | Radio | Notes | |------|---------|-------|-----|-------|-------|-------| @@ -20,7 +50,7 @@ The reframe from the product owner (see [§ Reframing](#reframing-core-as-librar **No PSRAM, anywhere in the family.** There is no external-RAM interface comparable to ESP32's PSRAM (the nRF52840 QSPI does execute-in-place flash, not a mappable data heap). The RAM ceiling is the table above minus the BLE controller/stack (~30–60 KB), Zephyr kernel, and network buffers. Rough usable-for-buffers estimate: ~150 KB on a 256 KB part, ~350 KB on the 5340, ~700–800 KB on the 54H20. Against MoonLight's memory-scaling table (16 K lights proven on the ESP32's ~320 KB internal; Teensy's 1 MB rated "comfortable headroom"), the 52840/54L15 sit **below the no-PSRAM ESP32**, the 5340 slightly above it, and only the 54H20 reaches Teensy class. -### WS2812 output — the DMA story +#### WS2812 output — the DMA story Nordic has no RMT, no LCD_CAM/i80, no Parlio, no FlexIO — no one-shot wide-parallel DMA peripheral at all. Three documented single-lane routes exist, each abusing a serial peripheral + EasyDMA: @@ -30,7 +60,7 @@ Nordic has no RMT, no LCD_CAM/i80, no Parlio, no FlexIO — no one-shot wide-par Realistic total: **~4–6 heterogeneous lanes** (4× PWM + 1× I2S + 1× SPIM3), each behind a *different* peripheral driver — versus 8–16 *uniform* lanes on one peripheral on ESP32-S3/P4, or 40+ on Teensy via FlexIO. Combined with the RAM ceiling (a 4-lane PWM setup burns 48 B/LED/lane just for encode buffers: 4 × 500 LEDs ≈ 96 KB of the ~150 KB usable), the honest capability estimate for a 256 KB part is **~1–2 K LEDs total**, ~4 K on the 5340, maybe 8–10 K on the 54H20. One more hazard FastLED's issue tracker documents: the BLE controller runs at interrupt priorities application code can't mask, so any *non*-DMA (bit-banged) output glitches under radio load — DMA routes are mandatory, which the three above all are. -## Zephyr fit with the `platform.h` seam +### Zephyr fit with the `platform.h` seam Zephyr fundamentals check out: **C++20 is a supported standard** (`CONFIG_STD_CPP20`, full GNU libstdc++ available), with the usual embedded restrictions — no `std::thread`/`std::mutex` (Zephyr `k_thread`/`k_mutex` instead — irrelevant to us, the seam already hides threading behind `spawnPinnedTask`), no static-object *destructors*, exceptions/RTTI opt-in. BSD sockets (subset), LittleFS, and a devicetree-driven driver model are all first-class. @@ -38,7 +68,7 @@ Zephyr fundamentals check out: **C++20 is a supported standard** (`CONFIG_STD_CP |---|---|---| | Time (`millis`/`micros`), yield/delay, `reboot` | `k_uptime_get`, `k_cycle_get_32`, `k_yield`, `k_msleep`, `k_busy_wait`, `sys_reboot` | **Easy** — direct 1:1 | | Heap (`alloc`/`free`, introspection) | `k_malloc` / `sys_heap` + runtime stats | **Easy**; `hasPsram = false`, one heap — the `maxAllocBlock`-style diagnostics need `sys_heap` stats plumbing | -| `allocExec`/`writeExec` (MoonLive JIT) | Cortex-M executes from RAM; no W^X obstacle | **Easy mechanically, but pointless today**: MoonLive has Xtensa/RISC-V/arm64 backends, no ARM Thumb-2 emitter — scripted modules fail cleanly (run dark) until a Thumb-2 backend exists, exactly the per-ISA cost the MoonLive IR-seam design anticipated | +| `allocExec`/`writeExec` (MoonLive JIT) | Cortex-M executes from RAM; no W^X obstacle | **Easy**: the Thumb-2 backend the Teensy uses (`src/platform/teensy/moonlive_asm_thumb2.cpp`) emits Thumb-2, the instruction set the nRF52840's Cortex-M4 shares; unverified there | | RTOS introspection (`taskSnapshot`), `spawnPinnedTask`/`notifyTask` | `k_thread_foreach`, `k_thread_create` + `k_event`/`k_sem` | **Easy** — but every viable nRF is **single application core** (the 5340/54H20 second core belongs to the radio stack), so the multicore Step 2 render/encode split has no core to pin to. The seam already handles this: `spawnPinnedTask` returns false → the caller runs inline, the documented allocate-and-degrade fallback. Same stance as Teensy ("desktop is OS-threaded, Teensy single-core" — measured on the P4, see performance.md) | | GPIO capability/live state | Zephyr `gpio` API + hand-written per-pin caps table | **With effort** — same per-chip table work every target needs | | **LED DMA seams** (`rmtWs2812*`, `i80Ws2812*`, `parlioWs2812*`) | **None of these peripherals exist** | **New seam required**: an `nrfPwm`/`i2s` WS2812 function pair in the same init/buffer/transmit/wait shape. The capability-flag pattern absorbs this cleanly (`rmtTxChannels = 0`, `lcdLanes = 0`, `parlioLanes = 0`, a new lane constant) — the existing drivers go inert exactly as they do on desktop, and a new driver guards on the new flag. Loopback self-test possible via GPIOTE+timer capture, but bespoke | @@ -54,7 +84,7 @@ Zephyr fundamentals check out: **C++20 is a supported standard** (`CONFIG_STD_CP Summary: the seam itself is no obstacle — Zephyr is the most MoonLight-shaped RTOS outside ESP-IDF, and the `platform_config.h` flag pattern (everything inert-by-`if constexpr` when a capability constant is 0/false) absorbs the missing peripherals exactly as it does for desktop. The three *structural* gaps are: **single app core** (multicore split degrades to inline — acceptable, Teensy has the same), **no PSRAM** (hard memory ceiling, the 256 KB parts sit below the no-PSRAM ESP32), and **no native IP networking** (below). -## The architectural question: is nRF an IP-networked LED controller? +### The architectural question: is nRF an IP-networked LED controller? Honestly: **no, not natively.** MoonLight's product surface is IP end to end — the HTTP config server + WebSocket UI, ArtNet/DDP/WLED-sync over UDP, mDNS discovery, OTA over HTTP, Improv provisioning *of WiFi credentials*. An nRF SoC has BLE + 802.15.4 and nothing else: @@ -64,33 +94,28 @@ Honestly: **no, not natively.** MoonLight's product surface is IP end to end — So the honest framing: **as an IP node it fits our architecture but loses to ESP32 on every axis we care about; as a BLE node it doesn't fit our architecture.** The genuinely nRF-shaped use case — a battery-powered, BLE- or Matter-controlled wearable/small-strip product, phone-app-controlled, sub-500 LEDs — is real, but it is not the product MoonLight is (large installations, web UI, network protocols, no-reboot live reconfig of a mains-powered controller). -## Precedent +### Precedent - **FastLED** supports nRF52 (Adafruit nRF52 boards): the `clockless_arm_nrf52` path drives WS281x via the **PWM peripheral + EasyDMA**, effectively one strip per PWM instance, with documented friction (BLE interrupt priority vs timing, per-board pin/compile issues — GitHub issues #595, #747). It proves single-to-few-lane WS2812 works; nobody uses it at installation scale. - **WLED is ESP-only** (ESP8266/ESP32 families); no nRF port exists or is planned — a data point that the networked-LED-controller product category has not found nRF worth the port either. - **Zephyr itself** ships three single-lane `led_strip` WS2812 drivers (SPI default, I2S, GPIO-bitbang) with nRF DK sample overlays — good bring-up references for a platform-layer driver, all small-N demos. - **Nordic's own I2S-WS2812 blog** (2016, still the canonical reference) demonstrates the 4-bit-symbol I2S encode; community repos (electronut, cheehieu, edwios) replicate it at 12–240 LEDs. -## Reframing: core-as-library and the BLE-UI question +### Reframing: core-as-library and the BLE-UI question The sections above answer "is nRF a good LED-installation controller" (no). But MoonLight is **more than its light domain** — its `src/core/` is a domain-neutral runtime that the architecture explicitly holds apart from lights (see [architecture.md § Core and light domain](../../explanation/architecture/index.md#core-and-light-domain): *"Core: MoonModule base, controls, scheduling, persistence, platform abstraction... Domain-neutral. Knows nothing about lights."*). Offered as a **library**, that core is a compelling thing in its own right, and the interesting fit question becomes: does MoonLight's *runtime* — not its LED pipeline — run cleanly on a tiny BLE device, and can its UI reach it? This reframes nRF from "loses the big-installation race" to "a showcase of how far MoonLight's core stretches." -### 1. How much of the core is portable as-is? +#### 1. How much of the core is portable as-is? -Nearly all of it. Walking `src/core/` against the domain line: - -- **Portable, domain-neutral, zero LED/network dependency**: the MoonModule base + lifecycle (`setup`/`tick`/`release`, child propagation, the enabled-gate), the Scheduler, ModuleFactory, the Control system (bind-to-member, defaults, conditional `hidden`), the persistence system (FilesystemModule → LittleFS flat POD image), the state/JSON model, and the **generic UI-from-modules contract** (the UI renders whatever a module *declares*; there is no per-module UI code). This is the bulk of the core, and it needs nothing nRF lacks — Zephyr gives it a heap, a filesystem, and a clock, which is all the runtime asks. -- **Portable but inert-by-flag**: the network/service modules (HttpServerModule, NetworkModule, the ArtNet/DDP/WLED-sync drivers, mDNS, OTA) are core-resident but *gate on capability flags already* (`hasNetwork`, `hasWiFi`, `hasOta`). On a `hasNetwork = false` build they compile to nothing via `if constexpr` — exactly the desktop-stub pattern. So they don't *block* the port; they simply aren't present, and the UI transport that would normally ride HTTP/WS has to come from somewhere else (§ 3). -- **MoonLive** (the live-script engine) is portable in structure but needs an **ARM Thumb-2 backend** to actually emit code (it has Xtensa/RISC-V/arm64 today); until then scripted modules fail cleanly and run dark. Not a blocker for a core showcase — every *non*-scripted module works. -- **Entangled with the light domain (stays behind, correctly)**: layouts, layers, mapping/blending, effects, modifiers, the LED drivers. None of this is core; it's `src/light/`, and a core-as-library showcase simply doesn't ship most of it (it ships one small LED driver as the visible output, § 2). +Nearly all of it. What the core owns, and how its network modules go inert on a capability flag: [MoonCore](../../explanation/architecture/mooncore.md). Zephyr gives the runtime a heap, a filesystem, and a clock, which is all it asks, and the UI transport that would normally ride HTTP/WS has to come from somewhere else (§ 3). The honest figure: **the domain-neutral core ports with essentially no architectural work** — it's the same code the desktop build already runs headless-of-LEDs. The port cost is the platform seam (§ Zephyr fit above) plus one new thing the desktop never needed: a **UI transport that isn't HTTP** (§ 3). That single item is the whole ballgame. -### 2. A few LEDs is a feature, not only a shortfall +#### 2. A few LEDs is a feature, not only a shortfall The ~1–2 K-LED ceiling on a 256 KB nRF part reads as a shortfall against 16 K-on-ESP32 — but for the device class nRF actually *is* (battery-powered, BLE-remote, wearable, retail/prop lighting), a handful to a few hundred WS2812 is the whole point, and MoonLight's pipeline runs identically at that scale (the architecture's *"effects must run at every grid size"* rule guarantees a 16-LED strip or an 8×8 matrix is a first-class configuration, not a degraded one). The single-lane I2S-EasyDMA route (≈ 1365 LEDs, § WS2812 output) is *more* than enough for this niche, and the PWM route gives a few independent short strips. So the small-device story isn't "MoonLight crippled" — it's "the same full pipeline, small, battery-friendly, phone-controlled," which is a coherent product the ESP32 line doesn't naturally serve (ESP32 + WiFi is mains-power-shaped). Re-weighed: the LED limit is a *positioning*, not a *defect*, for this specific niche. -### 3. THE crux — can MoonLight's UI run over BLE (no WiFi/HTTP)? +#### 3. THE crux — can MoonLight's UI run over BLE (no WiFi/HTTP)? MoonLight's UI ↔ device link is, at its heart, a **JSON control/state channel**: the browser sends control *writes*, the device *notifies* state back (today over a WebSocket). Nothing about that model is HTTP-specific — it maps directly onto a **BLE GATT custom service**: one **write** (or write-without-response) characteristic for control-set, one **notify** characteristic for state-push. This is the exact shape of well-established prior art, and the research says the path is **clean, not hacky**: @@ -101,11 +126,11 @@ MoonLight's UI ↔ device link is, at its heart, a **JSON control/state channel* **Verdict on the crux:** the BLE-UI path is **clean at the protocol/device level and a well-trodden pattern** — it is a weekend-to-two-weeks spike, not a research project, *for the device firmware + an Android/desktop Web Bluetooth client*. It becomes a larger undertaking only if the showcase must include a first-class iOS client (then: a small companion app, a separate effort). Framed as a demo/advertisement, the Android/desktop Web Bluetooth client is enough to make the claim land. -### The advertising value +#### The advertising value "MoonLight's full runtime — the same MoonModule lifecycle, controls, persistence, and generic UI you run on a 16 K-LED ESP32 installation — also runs on a coin-cell BLE chip, configured live from your browser over Bluetooth" is a strong, concrete credibility claim. It demonstrates the domain-neutral-core thesis better than any doc: the *same* core, top to bottom, from a battery BLE wearable to a mains ESP32 wall of light. That is a real reason to do the spike **independent of** whether nRF ever becomes a shipping LED target. -## Recommendation +### Recommendation **Do a scoped showcase spike; do not commit to nRF as a shipping LED target.** The two questions have opposite answers and must not be conflated: @@ -119,9 +144,9 @@ MoonLight's UI ↔ device link is, at its heart, a **JSON control/state channel* That spike answers the three real unknowns — how much RAM survives Zephyr + the BLE stack (does the core fit a 256 KB part?), BLE-radio-vs-DMA timing under load, and end-to-end BLE control/state round-trip latency in the actual UI — for about two weeks, and produces a genuinely marketable artifact whether or not nRF ever ships as an LED target. -**What would additionally have to be true to promote it from showcase to a shipping target:** a concrete product pull toward BLE/Matter-controlled small-strip devices (wearables, retail, battery props); a decision on the iOS client (companion app vs. Android/desktop-only); and, for scripting on-device, the ARM Thumb-2 MoonLive backend (already the named cost for Teensy, so nRF rides along once Teensy lands). +**What would additionally have to be true to promote it from showcase to a shipping target:** a concrete product pull toward BLE/Matter-controlled small-strip devices (wearables, retail, battery props); a decision on the iOS client (companion app vs. Android/desktop-only). -## Sources +### Sources - Nordic product pages: [nRF52840](https://www.nordicsemi.com/Products/nRF52840), [nRF5340](https://www.nordicsemi.com/Products/nRF5340), [nRF54L15](https://www.nordicsemi.com/Products/nRF54L15), [nRF54H20](https://www.nordicsemi.com/Products/nRF54H20), [nRF7002 Wi-Fi 6 companion IC](https://www.nordicsemi.com/Products/nRF7002) - Family comparisons: [nRF52840 vs nRF54L15 (BLEFYI)](https://blefyi.com/compare/nrf52840-vs-nrf54l15/), [nRF5340 vs nRF54L15 (BLEFYI)](https://blefyi.com/compare/nrf5340-vs-nrf54l15/), [nRF54H20 announcement (CNX)](https://www.cnx-software.com/2023/04/13/nrf54h20-cortex-m33-risc-v-wireless-soc-bluetooth-5-4-le-audio-bluetooth-mesh-thread-matter/) @@ -132,3 +157,18 @@ That spike answers the three real unknowns — how much RAM survives Zephyr + th - BLE-UI throughput: [Punch Through — Maximizing BLE Throughput](https://punchthrough.com/ble-throughput-part-4/), [Memfault Interrupt — A Practical Guide to BLE Throughput](https://interrupt.memfault.com/blog/ble-throughput-primer), [Nordic GATT Throughput Service (the ~97 kB/s @ 251-byte PDU figure)](https://nrfconnectdocs.nordicsemi.com/ncs/2.1.4/nrf/libraries/bluetooth_services/services/throughput.html) - Web Bluetooth support: [caniuse — Web Bluetooth](https://caniuse.com/web-bluetooth), [MDN — Web Bluetooth API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Bluetooth_API), [WebBluetoothCG implementation status](https://github.com/WebBluetoothCG/web-bluetooth/blob/main/implementation-status.md), [Chromium issue — Web Bluetooth on iOS (unimplemented)](https://issues.chromium.org/issues/41280428) - Config-over-BLE / Web Bluetooth prior art: [Zephyr + Web Bluetooth getting started](https://dev.to/denladeside/getting-started-with-zephyr-and-web-bluetooth-355d), [Moddable JSON-defined GATT services](https://www.moddable.com/documentation/network/ble/ble), [Nordic custom BLE service tutorial](https://github.com/NordicPlayground/nRF5x-custom-ble-service-tutorial), [ESP32 WiFi provisioning over BLE (Espressif/community)](https://randomnerdtutorials.com/esp32-wi-fi-provisioning-ble-arduino/), [UriShX esp32_web-ble_wifi_config (Web-BLE config portal)](https://github.com/UriShX/esp32_web-ble_wifi_config) + +## Tools + +| | Tool (maker) | Why | Where, price | +|--|--|--|--| +| Raspberry Pi Debug Probe | [Debug Probe](https://www.kiwi-electronics.com/en/raspberry-pi-debug-probe-11345) (Raspberry Pi) | SWD for the RP2040, the RP2350 and both Nordic chips, through pyOCD or OpenOCD | Kiwi €13.18 | +| SN74AHCT125N | [SN74AHCT125N](https://www.tinytronics.nl/en/components/ics-and-microcontroller-chips/ics/sn74ahct125n-4-channel-level-converter-3.3v-to-5v-14-pin-dip) (TI), two or three | 3.3 V to 5 V for WS2812 data, since every board here drives its pins at 3.3 V | Tinytronics €1 each | + +## Where to order + +- Antratek: the Pico 2 W, the Pico W and the XIAO nRF52840 Plus, together with any Teensy items. +- Tinytronics: the level shifters and both Zero boards. +- Kiwi: the Debug Probe. +- The Pi Hut: the W5500-EVB-Pico2. +- DigiKey: the Raytac board, when the nRF5340 comes up. diff --git a/docs/work/future/ui-extensibility-analysis-bottom-up.md b/docs/work/future/analysis-ui-extensibility.md similarity index 97% rename from docs/work/future/ui-extensibility-analysis-bottom-up.md rename to docs/work/future/analysis-ui-extensibility.md index 30629700c..443145664 100644 --- a/docs/work/future/ui-extensibility-analysis-bottom-up.md +++ b/docs/work/future/analysis-ui-extensibility.md @@ -93,7 +93,7 @@ The convergent answer is **a small registry + a per-module widget contract, both There are **three tiers** the top-down should name, so a module reaches for the lightest that fits (minimalism): (a) **generic controls** — no custom UI, the default; (b) **generic-with-richer-list-detail** — a module whose need is just nested/tabular detail; (c) **full custom widget** — a Custom Element for genuinely bespoke UI (file tree, board diagram). FileManager is (c); TasksModule today is (a)-with-a-workaround that (b) would fix; Memory/Pins are likely (b) or (c). -This is directly tied to the [System Modules design](system-modules.md): Tasks/Memory/Pins are the *next* modules that will want tier (b)/(c) UI, so the extension architecture should land before (or alongside) building Memory and Pins — otherwise each repeats FileManager's inlined-in-app.js mistake. +This is directly tied to the system modules: Tasks and Pins ([core system](../../moonmodules/core/system.md)) and [Memory](backlog-core.md#memory-the-largest-buffers-by-name) are the modules that will want tier (b)/(c) UI, so the extension architecture should land before (or alongside) building Memory and Pins — otherwise each repeats FileManager's inlined-in-app.js mistake. ## The acceptance test (from the governing statement) diff --git a/docs/work/future/virtual-layer-downscale-study.md b/docs/work/future/analysis-virtual-layer-downscale.md similarity index 94% rename from docs/work/future/virtual-layer-downscale-study.md rename to docs/work/future/analysis-virtual-layer-downscale.md index bb95e75cb..1b48dbc67 100644 --- a/docs/work/future/virtual-layer-downscale-study.md +++ b/docs/work/future/analysis-virtual-layer-downscale.md @@ -17,8 +17,7 @@ Concretely: a `Fire` effect on a 32×32 grid, driven onto 10 Hue bulbs, makes ea of its ~102-pixel slice of the fire — the flames' *summary*, not one stray pixel. The mirror case (**physical ≥ virtual**, e.g. a 128×128 wall from a 32×32 effect) is **already -solved** — the existing `MappingLUT` fans a small logical grid out across a larger physical set (1:N -upscale). The genuinely new capability is the **downscale** direction (physical < virtual): render +solved** by the [1:N mapping](../../explanation/architecture/moonlight.md#mapping-and-blending). The genuinely new capability is the **downscale** direction (physical < virtual): render big, summarize small. ## Why this is a Layer feature, not a modifier @@ -58,8 +57,7 @@ lights. Care points a future plan must resolve: - **`rebuildLUT` assumes logical ≤ physical.** It backward-maps each *physical* light to *one* logical cell (a fan-out / scatter). A virtual grid *larger* than physical needs the inverse: many logical - cells → one physical light. `MappingLUT` today has 1:1, 1:0 (unmapped), and 1:N (fan-out) mapping - types but **no N:1-averaged type** — that's the new mapping the feature introduces. + cells → one physical light. The [four mapping types](../../explanation/architecture/moonlight.md#mapping-and-blending) hold **no N:1-averaged type**: that's the new mapping the feature introduces. - **Memory:** the virtual buffer is `virtualW × virtualH × virtualD × channels`, which can dwarf the physical light count (32×32×3 = 3 KB is fine; larger virtual grids need the same PSRAM-first allocation + degradation-status discipline the Layer already uses). diff --git a/docs/work/future/archive_images/moonlight/drivers/Art-Net-In.png b/docs/work/future/archive_images/moonlight/drivers/Art-Net-In.png deleted file mode 100644 index 2cbeaaa4c39262586ec5c9de62f0197eec1fa2bc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28724 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 b|A$BFJ*KGDG(34<$L|3`RU1~TY#sGKvLw!! diff --git a/docs/work/future/archive_images/moonlight/drivers/ArtNetInControls.png b/docs/work/future/archive_images/moonlight/drivers/ArtNetInControls.png deleted file mode 100644 index 26276898e3bae255ec85606e90d4e5c309774512..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11104 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 diff --git a/docs/work/future/archive_images/moonlight/drivers/FastLED-Driver.png b/docs/work/future/archive_images/moonlight/drivers/FastLED-Driver.png deleted file mode 100644 index 3f521622f88428b6ea466d5d6b9883341ae08ae0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40724 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%0Das-WOiw%>yXfDMG| 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 diff --git a/docs/work/future/archive_images/moonlight/drivers/MPU-6050.jpg b/docs/work/future/archive_images/moonlight/drivers/MPU-6050.jpg deleted file mode 100644 index 8f50cc7a0ae8fc3c8855b55f2bcdcfc296f7e3a2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14181 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

uUCZ5EbQYZ>HeF+@s@6VnR= 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

}} zwegzzYN|Zvk~S;?+DFuFx#wnMldwN(oOm_h;ae6oRI89nyoHr(uW~kNuxsPXn3F!6 z)+_(a4LGcHuN=I(F&z=|YMx|)4jMyOk^GUnjD^-`jx zrqY>iImhSzHrY80Xjz}Hb1J{srhCRd%VB{FMiI%PBVemBd-y!IwV;DH2J;z;`y1wT ze@L;W)Rqg+wq(`m@LY{<$u_=32Zww724UAk2*!EO;m{T%f%#a-H_BWy)aRzt&O$X* z`D%fTSX%yWmCU%ojJ2}!wo0UP9*{TC$1cc?STSuPxkH1aud)YdBnxb zbZMk5oT7eTOT{%=Kx@0-F`YG9F5hT-p%GQiMzI0~7H-EfvcSH~+X)Sa=)O6M+qvpm~00$HyyJs66ySfoQyR5TG_z! z`DY4@3nKL?OCLdGu-#I##r!l#DOBFM0)TLb{6xzMx@wW+W013(VqsN3DAUX9rZu^biP*ZDJ(lZMrF_y zWv7U`>!C;zO+#koSM6u^YE=b*W>llbRFI<7fvNhlVJhT`he9ZU=g5aGuaKxg8Lffy zvZk?gamcmR)gbT5)o=2LIN(3kqr#M<-8@Q1e0*4|59T3+^9*-)fqe534+; z-dlLL1J$xUqBp=%Hw^w-d8x7CYA2@0FHcH`eYpzH21svM_VWcYv+pdJ2oQO^6-%xo z%5XwyEFW$92DTaw%eP;vmDvdc`hWSeuJ!X24bMZuL#7Gdj8vS;5`e}k$_yFXgYoTKNbd9=x;teZjg)V zWa-Bc+YD|56sZeeNMxzG&&}GHvuV6vhDo42u)G7JhBigWh>9Sv%~y5dqXrD8#ry_D ztX$;r?d=e_)XOBpl)q|wRZqo*ldWwRoD#uI2e<$%i`-(ki)--lt63jFIU(ZC^87$? z*_rwG!!)8C!eZ}BXB*cpIYLUT7))5;ieR=HXxot7HXq~;_a%Vp%XYg>^Pv^7r8-hq zqfuG+QUyJ66`m`LD{4>hEUe>|UiwDrP@brs+-rm4%8b!94BgpCw4Od?3mOQmAmvO?rtTq-HK(_> z+c|Q6pX+W83XxM3t*G7sxqSfDeI5n_6%TrDqtT7~_N?rheSUGlJOF8FTToR^JAZ7?M&X;YB6TEyNqh+x-c__j%dmi%=9Ysix=m}>r@1Rxp1pEsi^<8-@+T}%7XwJ+I>3daue#;_uXQ+w4ADQ${^O@?3LJlC zq2Q?(ZFvBa$0I6(;^UlkK=rtwPYdLUuLpdP>3V47mj zMHOn<0&}2ELIf972tG_W9K$Y*sf7L;W}kM;TgMImU1hWWbH_Qm<@qZVzkm4bM@{(r zL;E+^H*1`K`ziY3#~0d&s%gWr!qrJ868R`V)W!YbFn%2a+ZJi+9(W;uWjB08@T}(U zQUXJEMW?M}iB7Kr7>015zzUxSNfslIPs7v~!v1my_PwKeNrf5~n=dn6aZR1k%2k

  • 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

    hEK2_T2OrS`Hz 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 +>ztjX4yjD9E7O#d*=MdikXuTq|HI0`Q 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 diff --git a/docs/work/future/archive_images/moonlight/effects/E_Hello1D.gif b/docs/work/future/archive_images/moonlight/effects/E_Hello1D.gif deleted file mode 100644 index b75ce65b876314956af22a2206d91dcdc6a63518..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23952 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 diff --git a/docs/work/future/archive_images/moonlight/effects/E_Noise2D.gif b/docs/work/future/archive_images/moonlight/effects/E_Noise2D.gif deleted file mode 100644 index 64dd706c59d0e2e4be3b8c1c3ce2acce9268520d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 161905 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

    R8 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 Forward-looking design study (backlog, present-tense-exempt). The *shipped* audio path is -> documented present-tense in `src/core/services/AudioService.h`'s `///` (and its generated moxygen -> page); this study holds the **prior-art analysis** and the **not-yet-built** extensions the -> module's `///` credit points at. - -## Prior art studied (credit by name) - -Audio-reactive lighting is a long-standing idea in the LED-controller world (WLED-MM and MoonLight -are the closest lineage). MoonLight's audio path is its own implementation, designed from the INMP441 -datasheet and standard DSP — not traced from any one project — but three people's thinking is studied -here with respect and credited by name (the *Industry standards, our own code* principle: study hard, -write fresh). - -**Frank (softhack007)** — main author of the WLED-MM audioreactive usermod (the most-used open-source -audio-reactive LED implementation), a direct ancestor of the ideas this module learns from. The -product owner worked alongside Frank for years on WLED-SR / WLED-MM before MoonLight and MoonLight. -His concept is the worked example in the *Adaptive noise gate* section below: his idea, our analysis, -written fresh against our architecture. - -**Troy (troyhacks)** — MoonModules team; keeps a WLED-MM fork where he reworked the audioreactive DSP -onto Espressif's **esp-dsp** library ("stupid fast compared to ArduinoFFT"), very low latency on S3/P4. -His contribution has two parts: -- *esp-dsp FFT.* Troy uses esp-dsp's **radix-4** real FFT (`dsps_fft4r_fc32`) with a Blackman-Harris - window. This validates the path MoonLight is already on — **we use esp-dsp too**, the **radix-2** - float real FFT (`dsps_fft2r_fc32`) in `platform_esp32_i2s.cpp`. Same library; the one open - optimisation is **radix-4 vs radix-2** (fewer butterfly stages, log₄N vs log₂N — a measure-then- - maybe tune-up, not a gap; today the float FFT on the FPU is well inside one tick). Two adjacent, - *not-yet-adopted* options for the record: (a) esp-dsp's **int16 / fixed-point** path uses the - **built-in FFT instructions on S3/P4** — the lever for low-power FPU-less chips (C3/S2); we run - float today because our targets have an FPU; (b) Espressif's standalone **`dl_fft`** component does - *only* FFT without esp-dsp's shared twiddle tables — the right pick if a future build wants the FFT - without the rest of esp-dsp (we take the whole esp-dsp because we also want its DSP primitives). -- *Biquad pre-filters.* Before the FFT, Troy runs the samples through **biquad** HP/LP/peaking filters - via esp-dsp's `dsps_biquad_f32`, coefficients designed in EarLevel's Biquad Calculator v2. Squarely - industry-standard (the biquad / second-order section is *the* canonical EQ building block; the Audio - EQ Cookbook is the reference). Our pipeline does one fixed DC-blocker HP (~40 Hz); Troy's work shows - the next step — a **configurable biquad chain** (HP to kill rumble, LP to tame aliasing, optional - peaking to lift the mids the FFT under-reports). **Assessment: the biquad pre-filter chain is the - higher-value idea to adopt** (improves spectral accuracy with off-the-shelf primitives), and it - composes cleanly with the adaptive gate below (a learned gate on a cleanly-filtered signal beats one - on a raw signal). - -**LedFx** (Python, host-side) — a network LED effect engine whose whole purpose is audio reactivity, -running on a PC and streaming pixels to WLED-class devices. Different architecture to ours (the host -renders, the device receives; we already interoperate through Art-Net / E1.31 / DDP in both -directions), so most of it does not transfer. Two pieces of its *analysis* do, and both are studied -in § Band spacing below: its **mel/bark band spacing** with hand-tuned variants, and its -**per-band asymmetric smoothing**. Worth naming because it reached those two independently of the -WLED lineage above, and its own source comments are unusually candid about which variants work. - -**Damian Schneider (DedeHai)** — WLED core dev; WLED's audioreactive usermod carries an integer / -fixed-point FFT path (~1.5 ms on a C3, >10× ArduinoFFT on FPU-less chips). The consensus (Troy + Frank) -is that with esp-dsp FFT + biquads, **fixed-point is not necessary on FPU chips** (S3/P4) — MoonLight's -exact position: float on FPU targets, the int16 / `dl_fft` hardware path reserved for low-power chips. -DedeHai's current audio experiment is a PoC MSGEQ7-based path (offloading the spectrum to a dedicated -analyser chip) — a different point in the same space, noted for completeness. - -## Source-seam extensions (widen what feeds the pipeline) - -All of the following widen the **source seam** — what feeds the pipeline — leaving the DC-blocker / RMS -/ FFT / band analysis untouched. In roughly increasing hardware complexity: - -- **I²S with MCLK, for line-in — largely SHIPPED.** The INMP441 is self-clocked (no MCLK); a line-in - codec needs a master clock the ESP32 drives. The module already carries an `mclkPin` control (the - I²S peripheral drives MCLK for a line-in ADC; a codec board uses the codec's own MCLK). Remaining - source-seam work is the other three types below. -- **PDM mics** — a different I²S sub-mode (the IDF `i2s_pdm` driver), a variant behind the same - platform read. -- **Analog line-in.** DedeHai got analog input working on the S3; Troy got it working in ParrotRadio. - Troy's testing-confidence nuance worth recording: he considers his ParrotRadio analog path - better-exercised (he actually recorded + played back through it), whereas an unlistened-to analog - path "may not be as accurate as it looks." **If MoonLight adopts analog line-in, validate by - listening**, not just by watching the level meter. -- **I²C-configured codecs (e.g. ES8311).** Do **not** hand-roll each codec's register config — pull in - Espressif's **`esp_codec_dev`** component (carries option tables for many codecs), supporting "a - bunch more codecs for free." The *Industry standards, our own code* call applied to codec bring-up. - -Troy also has **DSP boards** (I²S front-ends "way beyond regular codecs") — recorded so the line-in / -codec work leaves room for that class of source. - -## Adaptive noise gate (softhack007's concept, our analysis) - -**Partly built.** `floor` is now a real silence threshold on both the level and the band paths: below -it a band reads zero AND is not learned from, which is what stopped the learner amplifying an empty -room to full scale. What remains from the design below is the ADAPTIVE half: hysteresis, the -asymmetric open-fast/close-slow timing, and a learned threshold rather than a set one. - -The original framing, kept because the remaining work is judged against it: replace the borrowed -`squelch`/`noiseFloor` knob ("a WLED-SR workaround, not a real gate") with a proper adaptive gate. From softhack007 (granted permission to analyse); the assessment is ours. - -**The concept:** a standard [noise gate](https://en.wikipedia.org/wiki/Noise_gate) (below a threshold -the signal is silenced, above it passes), **asymmetric bang-bang timing** (open fast, close slow; -hysteresis avoids threshold chatter), driven by a **new "detect silence" function** (the explicitly -unfinished part). Leave the GEQ/FFT bands untouched (the gate acts on the time-domain signal). The -closing pre-condition should be **relative** ("percentage of average signal"), not an absolute count. -Optionally feed the gate **compressed samples** (sqrt/log) so the threshold behaves perceptually. - -**Five design constraints (the load-bearing part):** (1) samples are signed, arbitrary magnitude — -scaling to an effect range is AGC's job, not the gate's; (2) every `abs()` must be justified (a -rectify discards sign/phase); (3) prefer relative factors to absolute thresholds (the one allowed -absolute: changes < 2 counts are sampling noise); (4) smooth before thresholding; (5) every filter -adds delay — total audio delay must stay < 30 ms. - -**Verdict: yes, directionally — it's squarely industry-standard** (a hysteresis gate with -fast-attack/slow-release is how studio gates, radio squelch, and voice-activity detectors all work), -and moves us off the borrowed `squelch` constant. The relative-threshold insight (constraint 3) is the -valuable core: a gate keyed to a *learned* floor self-calibrates to whatever source is connected. -**Two cautions:** (1) timing is tight — a 512-sample block at 22050 Hz is already ~23 ms, leaving -< ~7 ms of the 30 ms budget; any smoothing must be cheap (one-pole) and proven on hardware; (2) it -overlaps the already-scoped per-band floor, so decompose and adopt in steps, don't overhaul. - -**Per-band floor overlap:** the backlogged per-band noise floor learns each band's idle baseline -(frequency-domain floor — kills a steady tone like the bench's ~258 Hz hum); the proposed gate answers -"is there any sound at all" (time-domain floor). Complementary halves, not competitors. - -**Decomposition (cherry-pick, most value early):** -1. **Per-band noise floor (already backlogged)** — ship first; frequency-domain half, smallest change, - kills the concrete hum. Independent. -2. **Relative thresholds reusing the RMS we already compute** — `computeLevel` already produces a - per-block RMS (an envelope estimate, and the one justified `abs()` under constraint 2). A - learned-floor follower over that RMS with open/close as *factors* of it is a small, host-testable - addition needing no new DSP stage and no extra delay. **The cherry to pick.** -3. **Hysteresis + asymmetric timing** — two time-constants on that follower plus a close-hold; where - the < 30 ms budget gets measured for real. -4. **Defer until proven:** log/dB-domain thresholds (downstream `magToByte` already compresses - perceptually), a true soft gate (0..1 gain vs hard 0/1). - -**Eventually retires:** the `floor` knob's hard-squelch role — `floor` becomes the *display* -noise-floor only (the dB-window bottom in `magToByte`), while the learned gate decides "is there -sound." A clean subtraction, but the *end* of the path, not the first step. diff --git a/docs/work/future/backlog-core.md b/docs/work/future/backlog-core.md index 562eadee9..cd6a09271 100644 --- a/docs/work/future/backlog-core.md +++ b/docs/work/future/backlog-core.md @@ -1,1783 +1,518 @@ -# Backlog: core +# Backlog: Core -Forward-looking to-build items for the **core / infrastructure** domain (`src/core/`, `src/platform/`, build, CI, network, persistence, UI). The light-domain counterpart is [backlog-light.md](backlog-light.md); items that genuinely span both are in [backlog-mixed.md](backlog-mixed.md). Index + overview: [README.md](index.md). Completed items are removed. +To-build items for the domain-neutral core, grouped by module, each with its fields on one line. Other areas: [the future index](index.md). -### The `.deb` ships the systemd unit, raw Ethernet included (2026-10-22, from robwomp on Discord) +## ControlModule -The package holds only the binary, so a Pi or NanoPi user writes `/etc/systemd/system/moonlight.service` by hand from MoonWalk's "Run at boot" step. That unit grants no `CAP_NET_RAW`, so the panel card driver's `AF_PACKET` socket fails unless MoonLight runs as root, and the `setcap` alternative is lost whenever apt replaces `/usr/bin/MoonLight`. `CAP_NET_RAW` is all MoonLight needs, since it sets no interface flags. The fix: `package_desktop.py` installs the unit with `AmbientCapabilities=CAP_NET_RAW` and a dedicated `moonlight` user, so installing is `apt install` plus `systemctl enable --now moonlight`. MoonWalk's unit step shrinks to that enable, and the `setcap` step stays for a binary run by hand. +### A slot whose target is gone sends 0 to its followers (2026-10-08) -### An HTTPS request on a board without PSRAM (2026-10-24, from the JSON plan) +Kind: Bug -A board without PSRAM never fetches over HTTPS: `tlsRoom` asks for the 100 KB internal floor plus a 45 KB TLS session, and a classic ESP32 has 55 KB free at idle. `CONFIG_MBEDTLS_DYNAMIC_BUFFER` allocates mbedTLS's buffers as a connection needs them rather than the worst case up front, which may fit one request on an idle board. To measure on `MM-Olimex` (.207): the peak a session takes with it, what a smaller floor costs WiFi and the HTTP server, and whether a feed then fetches with lights running. Until then such a board gets its data pushed from one with PSRAM. - -### A StadBeest eye reset once by the task watchdog (2026-10-24) - -On 2026-10-23 eye2 (`MM-Eye2`, an ESP32-S3-Zero) reset once by the task watchdog, shortly after its OSC service was added, with no USB attached to read the panic. It has not recurred: both eyes ran the crossfading autopilot without a reset afterwards, and on 2026-10-24 they report `POWERON` after the beast was switched on. Recorded so a recurrence is recognized: `bootReason` on the System card is the one reading the network gives, since the board keeps no crash record; the panic itself needs the eye on USB with `monitor_esp32.py`. - -### What the WiFi buffer copy costs (2026-10-24) - -`CONFIG_LWIP_L2_TO_L3_COPY` copies every received frame so a socket the blocked loop stops draining cannot hold the 32 WiFi receive buffers; without it, every firmware upload stalled at 150 to 860 KB. Its cost is unmeasured: the frame rate on a busy DDP stream with and without it, same scene. If it costs much, firmware uploads move onto the transfer slots so nothing blocks the loop, and the copy goes. - -### An update's connection that would not open until a reset (2026-10-22, from the internal RAM plan) - -On 3.0.0 a board could not open the update's connection (`ESP_ERR_HTTP_CONNECT`) until it was reset, with 80 KB of internal RAM free. TLS has lived in PSRAM since then (`CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC`), which may be what cured it; it has not recurred. Recorded so a recurrence is recognized: the free internal RAM and largest block at that moment are the first readings to take. - -### The HTTP server is audited for the two PHP request-handling bug classes (2026-10-21) - -PHP's critical advisory of 2026-10-21 (CVE-2026-109618, CVE-2026-109619) names two bug classes any C++ server can have. **Out of memory mid-request**: an allocation fails while a request is half built, and cleanup or a later step treats the partial state as valid. That path is live here: a classic ESP32 fell to 916 bytes free during a page load. **State outliving its request**: a reused connection slot (`wsClients_`, `streams_`) keeps a pointer, length or buffer from its previous client. The audit is a read-only pass over `HttpServerModule` and the platform TCP layer, every allocation-failure path and every slot reuse, and each finding is pinned by a desktop test that forces the failure. - -### Headers link the docs by page, not by host (2026-10-21, from the PR 150 review) - -Seventeen headers under `src/` carry absolute `https://moonmodules.org/MoonLight/...` links, so a docs move edits firmware sources and marks every firmware stale, as PR 150's move from `how-to/` to `reference/` did. A header names the page site-relative (`reference/hardware/panel-cards.md#...`) and `gen_help.py` resolves the host once, so the next docs move touches no source file. - -### A Progress total rides the value patch (2026-10-19) - -A Progress control's total travels only in `writeControlMetadata` (Control.cpp), so a changed total needs a schema resync, which `rebuildControls()` triggers because `aux` is in the schema hash. -One line in `writeControlValue` and one in `app.js`'s patch handler would let the total ride the value, and the hash could then skip it, so a track change in FseqPlayerEffect stops resyncing every browser. -Pin it with a test that changes the total, patches, and finds the browser-side max moved with no resync. - -### An SD card inserted after boot mounts only when its module restarts (2026-10-12) - -A mount attempt on an empty slot runs the SDMMC card-init sequence to its timeouts, so `SdCardModule` never retries one on the render thread: a card inserted later mounts when the module is switched off and on, on a pin edit, or at boot. A pull is still noticed within a second through a cheap presence probe. Detecting an insertion without that stall needs a card-detect pin where the slot has one, or a mount attempt off the render thread. The timed retry PR #136 carried was dropped for this reason before its merge. - -### A config written for next boot is overwritten by the next save (2026-10-04) - -Network and Ethernet return false from `appliesConfigLive`, so a file written through `POST /api/file` waits on disk for the next boot. -Any save before that boot writes the module's in-memory values over it, so the upload is lost without a word. -Seen on the S3: a restored `NetworkModule.json` came back with the old `txPowerSetting` until the restore was followed straight away by a reboot. -The fix: `applyConfigFile` marks a module it does not apply live as pending on disk, and the flush skips that module's file until the next boot. -The uploaded file then wins with no UI change; the alternative, answering "applies at next boot" and offering a restart, still loses the file when the restart is declined. -Pin it with a test that writes the file, dirties the module, flushes, and finds the file unchanged. - -### Move more devices to MoonLight over WiFi (2026-10-09) - -The migration image (`moonbase/migrate/`) moves a board running WLED to MoonLight through WLED's own update page, with no cable: verified on WLED 0.15.0 on an ESP32-C3, on a bench board with a power cut at every step and on an Athom bulb. -An image is built for one chip and carries one variant's partition table, so each further reach is its own piece: -- **Every MoonBase variant:** `migrate: True` on `esp32`, `esp32-16mb`, `esp32-wrover`, `esp32-pico` and `esp32s3-zero`, each rehearsed on a bench board of that chip and shipped as a release asset. The variants without MoonBase (the larger S3s, the P4) need another design, the image writing the app itself. -- **Other WLED versions:** 0.13 and 0.14 keep the same files and layout, untested. 0.16 checks an upload's release name, so the user ticks WLED's option to skip it, untested. A WLED build with one app slot cannot move the image aside. -- **Tasmota:** its layout already has a recovery app in the factory slot and one app slot, so the image arrives running where MoonBase goes with nowhere to move aside; it would write over the recovery app instead. Its WiFi lives in a binary settings block in NVS, which needs its own reader. -- **ESPHome:** its update accepts the image, but its WiFi is compiled into the firmware, so MoonBase opens its access point. -- **A generic WiFi source:** ESP-IDF's WiFi driver can keep the last network in NVS under a namespace every framework shares; tried when no WLED files are found, it may carry the network from the others. Bench first, since whether it is there depends on how each firmware starts WiFi. - -### The access point never blocks a WiFi retry (2026-10-09) - -After losing its network, a device opens its access point and retries its known networks every minute (`retryKnown` in `NetworkModule.h`), but skips the retry while a client is on the access point. -A phone that stays on it, or rejoins it by itself, then keeps the device off its network indefinitely. -WiFi has preference, so the retry runs whoever is on the access point: drop the `wifiApClientCount() > 0` check. -- **The cost:** someone on the access point while a known network exists is interrupted for a few seconds once a minute; a first setup has no known network, so it never retries. -- **A test:** a client on the access point, the known network back, and the device joins it within the retry interval. - -### Some devices do not rejoin their WiFi (2026-10-09) - -Now and then a device stays off its network after the router came back, without anyone noticing why. -Two suspects: a client on its access point blocking the retries (the item above), or the path for losing the network while connected, where the WiFi driver can get stuck and need the radio restarted. -Next time one hangs, capture before touching it: -- **The Network card's status:** "No network", "AP: MM-…" or "WiFi STA: …" says which state it is stuck in. -- **The access point's `clients`**, which says whether a phone is holding it. -- **The serial log**, through `monitor_esp32.py`, whose `NetworkModule:` lines say what it tried last. - -### Refresh a multicast membership on a fixed pace (2026-10-09) - -A socket in a multicast group joins again after 3 seconds of silence, which repairs a router that forgets a member, with a gap of about 2 seconds each time. -The GL.iNet Slate AX forgets after about 18 seconds, so audio on the StadBeest drops every 20.6 seconds, measured on the bulb. -The standard fix is the router's multicast querier; this item is for a network that cannot be configured, such as a venue's. -- **The rule:** join again 10 seconds after the last join as well, so a membership is renewed before such a router forgets it. -- **The cost:** a leave and a join, two IGMP packets of about 60 bytes per socket every 10 seconds, and at most one lost packet in the moment between them. -- **Where:** `UdpSocket::keepMembership` in `platform.h`, one more condition, and a test beside the silence re-join's. - -### Back to WLED over WiFi (2026-10-09) - -The migration image moves a WLED device to MoonLight without a cable, but the way back needs one: MoonBase installs MoonLight images only, so a bulb going back to WLED is opened for USB. -Owners and makers of these devices care most about this: a firmware change that cannot be undone without a cable is a bigger step than one that can. -It is ours to build rather than WLED's, since the layout it undoes is MoonLight's, about one to two sessions: -- **A small image that downloads WLED itself** from WLED's GitHub releases while it runs, so MoonLight never redistributes WLED's binary and the image stays far from what it writes. -- **WLED goes into WLED's second app slot**, past the image's own position, so nothing moves aside; then WLED's table, and the boot pointed at that slot. WLED starts, formats its filesystem and opens its own access point. -- **MoonBase installs it** only once the image is stamped as one of MoonLight's, a deliberate change to the one rule that refuses other images (`src/core/util/FirmwareImage.h`). -- **A "Back to WLED" button** in the Firmware card, a release asset per chip, and the bench rehearsal with a power cut at every step that the way in had. -- The WiFi stays behind: WLED opens its access point, where the user sets it. - -### Logical operators in MoonLive conditions (2026-10-08) - -Glow wrote `kick && !kicking` as `kick > kicking`, and the StadBeest effects hit the same gap ([the language roadmap, section 11](backlog-moonlive.md)). -An `if` or `for` condition is one comparison compiled straight into a branch, so `&&`, `||` and `!` are the textbook short-circuit translation into branches, on the branch instructions every backend already has: -- `a && b` sends each failing test past the body, with no extra label; -- `a || b` sends each passing test but the last into the body, one label per group; -- `!a` swaps the two targets. - -The work, in `MoonLiveCompiler.cpp`: -- one condition parser for `if` and `for`: `||` over `&&` over `!` over a comparison, with parentheses, the lexer's saved position deciding whether a `(` opens a condition or arithmetic; -- the positive form of each comparison, which `||` needs, `!=` taking two branches as it does today. - -Tests on the desktop: the truth tables, that the right side does not run when the left decides, precedence, parentheses, and running out of labels. Then the operator section of `moonlive.md`, the roadmap row removed, and a check on the S3 and the C3, where Glow takes `kick && !kicking`. - -Not in this step: a comparison as a value (`bool b = x > y && z;`), which needs a set-on-condition instruction in every backend. - -### Remove the 6.0 network adoption (2026-10-04) - -`NetworkModule::adoptLegacySettings` and the two `adoptLegacy` it calls move 6.0's top-level network keys onto the Ethernet and WiFi cards at the first boot, so an in-place update keeps its network. -Remove all three, their test `unit_NetworkModule_legacy.cpp`, and the "update to this release before a later one" line in MIGRATING's WiFi and Ethernet entries, once a release has carried it and the field has moved past 6.0. - -### Remove the flat config conversion (2026-10-08) - -A config file in the flat format builds before 2026-10-08 saved is read as the state document it describes at the first boot and saved as one, so an in-place update keeps its config. -In the release after next, remove the conversion and everything that only serves it: -- `flatToStateDocument`, `isFlatConfig` and their helpers in `StateDocument.cpp` (`writeFlatNode`, `writeFlatValues`, `takeLive`, `flatName`, `distinctName`, `flatRoot`); -- the conversion in `FilesystemModule::loadSubtree`; -- the flat fallback in MoonBase's `ConfigScrape.h` (`flatChildKey`, `flatChildValue`); -- `configToDocument` and `isFlatConfig` in `src/ui/migrate.js`, after which an older backup no longer restores; -- the test "A config file in the flat format older builds wrote loads at boot and is saved back as a document", and the MIGRATING entry's lines on the conversion. - -`NetworkModule::adoptLegacySettings` calls `isFlatConfig`, so the 6.0 network adoption above goes first or in the same change. - -### The update overlay's Cancel button does nothing on a plain OTA (2026-09-14) - -`showUpdateOverlay` shows a Cancel button on every firmware install and wires it to -`POST /api/firmware/cancel` (`src/ui/app.js:1479`). Only MoonBase implements that route -(`moonbase/main/moonbase_main.cpp:532`); the application's dispatch table has none, so the request -404s and the `.catch(() => {})` swallows it. The button therefore works during the MoonBase phase of -an install and is inert for a plain app OTA on a device without MoonBase. - -A file upload still cancels there, but by a different mechanism: `uploadCtl.abort()` drops the -connection on the line above. So the two install sources behave differently behind one button, and a -URL install on a non-MoonBase device is the case where pressing Cancel does nothing at all. - -Two ways out, and the choice is the question. Implement the route in the application, which means an -abort flag the OTA task polls between chunks, the shape MoonBase already uses. Or hide the button -where it cannot act, which is honest but leaves a long download uninterruptible. The first is the -better system and the larger change; the second is a few lines. Found while writing -[Update MoonLight](../../moonwalk.md#update-firmware), which deliberately does not mention Cancel: -documenting a button that half-works is worse than leaving it undocumented until it is one thing or -the other. - -### The audio-sync test waits on the wall clock (2026-09-06) - -`test/unit/core/unit_AudioService_sync.cpp` drives the quiet-packet case with `platform::delayMs(1)` -inside a 100-iteration polling loop, so the assertion that `level` reaches zero depends on real -elapsed time and on loopback UDP delivering within that window. It passes today and has not been -seen to flake, but it is the shape that produces a rare CI failure nobody can reproduce, and it -spends real milliseconds in a suite that otherwise runs on a test clock. - -The fix is the deterministic UDP seam plus `platform::setTestNowMs`, the same pattern every other -timing test here uses: feed the packet, advance the clock explicitly, assert. Raised by CodeRabbit -on PR #96 and deliberately not taken in that pass: it is pre-existing rather than part of that -diff, and swapping a test's transport is a change that wants its own verification. - -## A request body shares the two upload slots (2026-10-25, from the pre-merge review) - -A request whose body trails its headers waits in a `FileTransfer` slot (`deferRequest`), of which there are two. With two uploads in flight, a `POST /api/control` whose body comes in a second segment answers 503 "every send slot is busy", and a slow-bodied control write holds an upload slot for the 10 s stall window. A slot of its own for `Kind::Request`, or a count kept apart, separates them; it waits for a user who meets it, since two uploads at once is rare. - -## The tests' own clock resets, now the listener's job (2026-10-25) - -A doctest listener beside the test main in `test/unit/test_main.cpp` puts the test clock back on the real one before every test case and subcase rerun, so the 88 resets of 49 files (`setTestNowMs(0)` in a guard struct or at a test's end) do the same job twice. Remove them: a guard struct and its uses go, a guard that also restores the heap, the WiFi scan, the palette or the boot record keeps that half, and a bare reset in the middle of a test stays wherever the rest of that test reads the real clock. About 150 lines, proven by the full suite. - -## Three decisions left open by the code ratchets (2026-10-25, from the Code ratchets plan) - -- **Name spread's limit**, 10 files beyond a type's own (`MAX_SPREAD` in `check_code.py`), which no tool defaults: confirm or move. Recommended: confirm, since the count only ratchets down. -- **A gate runner for agentless development**: the gate lists are run by an agent today, and a local-model or agentless workflow wants one runnable command per event. `precommit.py` went on 2026-10-01; whether one returns, and in what shape. -- **Undocumented public declarations**: a generated page shows an empty entry for each, which argues for a docgen rule, while cut-first argues against comments for their own sake. The middle way counts only public declarations in headers that generate a page. - -## What disable means: release the memory, or keep the module hot (2026-10-20) - -The rule says a disabled module keeps its settings only, and deleting a module already releases everything, so the question is whether disable should release too or keep the module hot for an instant resume. The contract test (`unit_ModuleContract.cpp`) lists the modules that keep runtime memory while disabled, Layer and Drivers with their pixel buffers the largest, and fixing them would settle the answer, so they wait for it. The analysis weighs: presets and DemoReel switching many modules, where kept memory grows with what is added rather than what runs; a quick toggle, where hot means an instant resume; the measured re-enable cost of every module, its `prepare()` time, gathered with the contract test's placement; three shapes, release on disable, keep everything hot until delete, or keep the settings and what is slow to rebuild while releasing the large buffers; and how comparable systems treat a disabled or paused element. The product owner decides, and the rule in coding-standards, the contract's lists and the Layer and Drivers fix follow. - -## Playlist in core: one sequencer for FSEQ, effects and DemoReel (2026-10-19) - -A playlist is a list of items with an optional duration each, played in order or shuffled once per pass, repeated off, one or all, under play, pause, stop, previous and next: the media player's model. Two copies of that sequencing exist: `DemoReelEffect::advance()` cycles effects, and `FseqPlayerEffect` cycles files with its own cursor, shuffle pass and loop mode, written as the same shape. An effects playlist would be the third, so it starts by putting the model in core once, a `Playlist` over core's editable `ListSource` whose owner supplies how to play item *i* and when it ends: the FSEQ player's items are files that end with the track, an effects playlist's are presets applied through the state-document engine that end after their duration, as the presets plan foresees, and DemoReel becomes an effects playlist of effects. The change that builds the effects playlist moves FSEQ's and DemoReel's sequencing into it. - -## A catalog entry extends another (2026-10-18) - -A shield is a carrier board plus its own pins, as a Serg shield is a D1-Mini32 with more wired. An optional `extends: ""` in the [device catalog](../../../mooninstaller/README.md#the-device-catalog) lets an entry take another's `state`: levels nest (MCU, carrier, device), a child overrides its parent at the same module and control, and modules merge by name. -**Build it** at the first two entries that share a base, as a pass in the installer and MoonDeck before they send the document, with `check_devices` resolving it too. - -## A slot whose target is gone sends 0 to its followers (2026-10-08) - -**Found** on the StadBeest after a repower: the legs' walking layer was saved disabled, so the script that `fader2` to `fader6` drive had no controls. -The slots could not follow their targets, stayed at 0, and the OSC feedback sent those zeros to the eyes, whose `bpm` and `sensitivity` then sat at their minimum. -Live slots are not saved, so after a boot only a target or a writer gives them a value. - -**The fix to build:** a slot with no target to follow holds no value rather than 0, and the mirror sends nothing for it until the target exists. -The followers then keep what they last had, and a refresh repairs them once the leader's target is back. -Pinned by a test: a disabled target's slot produces no feedback datagram. - -## A MoonLive script's branch tables are fixed and on the stack (2026-10-08) - -**Found** on the StadBeest eyes: one more `if` in a helper turned a working script into `codegen failed: assembler overflow`. -Each function takes three labels (entry, depth guard, exit) before its own `if`s, loops and divisions by a non-power-of-two. -The assembler's label table holds 96 two-byte code positions in the stack the 48 four-byte ones took (2026-10-10), which let Sauron blink. -The nearer limits are now the compiler's own `kIrLabels` at 40, which reports "too many branches", and the 96 fixups. - -**The fix to build:** the label and fixup tables of all three assemblers move from members to the heap, as the code buffer already did, then `kAsmFixups` and `kIrLabels` rise (to 255 and 96); `Label` stays a byte. -The spill pass and the lowering hold `kIrLabels`-sized arrays on the stack too, so they move with it. -Verified by compiling the shipped scripts on a classic ESP32, an S3 and a P4, whose compile stacks are the ones at risk, and pinned by a host test with a script past 40 branches on each backend. - -## Some devices stop answering mDNS over WiFi (2026-10-08) - -**Found** on the StadBeest: after the legs restarted, their MIDI service could not resolve the bridge's `MM-MIDI-rtp.local` for five minutes, and by address it connected at once. -Browsing from a Mac on the same network listed `_http._tcp` for MM-Eye2 alone: the legs, the left eye and the bridge announced nothing, and no `_apple-midi._udp` record appeared. -The left eye announces nothing new, so the cause is wider than the RTP-MIDI announcement. -The WLED native app finds devices by browsing `_wled._tcp`, so it is the likely reason it does not always list every device. - -**The fix to build:** find why a device stops answering, starting from what differs between MM-Eye2 and the others: WiFi power save dropping multicast between beacons, transmit power (the bridge runs at 8), the responder losing its interface after a reconnect, or the announcement order in `mdnsInit`. -Watch it with a working `dns-sd -B` from a computer and the device's serial log, and confirm the bridge's `_apple-midi._udp` record appears. -Pinned on the bench: every device answering its `.local` name and listed in the WLED app after a restart and after a WiFi reconnect. - -## A MoonLive control past a limit vanishes without a word (2026-10-08) - -**Found** in review: an `addSelect` with a 17th option, or one whose names overflow the 96-character option store, adds no control at all, and nothing tells the script's author why. -The other limits of `addDeclaredControl` behave the same way: past `kMaxCtrls` controls, a member already bound, a slot running past the arena, or names past the engine's 128-character name store (`nameText_`, about sixteen controls of seven characters). - -**The fix to build:** each refusal names itself on the effect's status line, as a compile error does, such as `control "shape": more than 16 options`, so the author sees which declaration lost and why. -Pinned by a test per limit that reads the status. - -## RISC-V emits 1.7 times the code Xtensa does (2026-10-08) - -**Found** on the StadBeest eyes: the script is 7 KB on an S3 and 12.4 KB on RISC-V, where one `smoothstep` call added to `tick` cost 432 bytes on its own. -A call inside a function with many live values saves and restores each of them, so every addition to a drawing loop costs RISC-V 400 to 500 bytes, and large scripts reach the 16 KB code buffer first on the P4 and the S31. - -**The fix to build:** measure where the RISC-V lowering spends its bytes on the shipped scripts (`disasm.py --isa riscv`), starting with the save and restore around host calls, and keep values that live across a call in callee-saved registers rather than saving them per call. -Pinned by a size test per backend on the largest shipped script. - -## The desktop build advertises no mDNS (2026-09-06) - -`mdnsInit` on the desktop platform is a stub returning false, so a desktop instance is invisible to -discovery: it neither announces itself nor answers `.local`. On a board mDNS is what makes a -device findable without knowing its address. - -Nice-to-have rather than blocking, because instances address each other by IP and that is what the -network drivers take (`NetworkSendDriver`'s `ips` list). What it costs today is convenience: a -container on a NAS has to be looked up rather than named, and a fleet of them cannot be enumerated -the way a fleet of boards can. - -**What it costs:** a platform implementation per OS, which is the awkward part. macOS has Bonjour -built in, Linux would want Avahi (a dependency the container deliberately does not have), and -Windows has its own. A small self-contained responder is the alternative, since the announce side is -a handful of multicast records and we do not need the resolver half. - -Worth revisiting if instance-to-instance sync grows: discovery is the difference between configuring -a fleet by address and having it assemble itself. - -## The desktop build renders as fast as the machine allows (2026-09-06) - -Nothing paces the desktop render loop. On a board the LED refresh does; on desktop the loop runs -flat out, measured at 83,000 fps in a container and ~110% of a core per instance. Memory is not the -constraint (about 5 MB), so the practical ceiling on running several instances is one per core. - -That matters now the desktop build ships as a container: an array of instances is a plausible -deployment (a fleet under test, several zones of a show), and today each one costs a whole core for -frames nobody consumes. Nothing downstream benefits from more than the output rate: Art-Net, DDP and -E1.31 all clock at their own wire rate, and the preview socket is throttled separately. - -**What it costs:** a target-fps control on the desktop platform, defaulting to something like 120, -with the loop sleeping the remainder of each frame. The tick path already measures its own duration -(`tickTimeUs`), so the sleep is a subtraction rather than new measurement. Roughly the shape of the -`--port` flag: a desktop-only concern, invisible on a board. - -**Also worth knowing:** it would cut power draw on a single instance too, which matters for anyone -running MoonLight permanently on a NAS or a Pi. - - -## The prose sweep, now counted and ratcheted (2026-09-28) - -`check_prose.py` already named `.js` and `.py` among the files it checks, but `.vale.ini` gave Vale -no parser for either, so both were skipped in silence: the intent was there and the mechanism was -not. A tree-sitter view per language closes that, and the count is written to -`docs/reference/metrics/prose.json` on every run, beside the other metrics, and shown on [the test page](../../reference/tests.md#check_prose). - -The gate stays scoped to ADDED lines, so a change is held to the rules on what it writes. -The report is the debt behind it, and it ratchets: a rise fails the check. - -The em-dashes dominate and are the mechanical part: a comma, a colon or a full stop replaces each, -and the same sweep already ran once over `src/**/*.h` when `CComments` was added. The rest is -judgment per line. Do it as its own change with its own review, a directory at a time, rather than -inside a branch about something else: a blanket find-and-replace over comments is how a code -identifier gets rewritten by accident. - -## `check_docgen` reads declarations from the clang AST (2026-10-15) - -The "public member has no `///`" rule reads a declaration's shape with a regex and tracks access by counting braces. It missed 25 functions carrying `MM_NONBLOCKING` until the pattern learned that macros follow a parameter list, and its comments list the other shapes it once misread. `check_clang_query.py` and `gen_help.py` already parse every header with libclang, which knows each declaration's access, kind and attached doc comment. Moving the rule onto that parse removes the brace counting, the depth stack and the shape patterns, and it reaches `.cpp` files, which the regex cannot read. - -## Distribution - -### The arm64 `.deb` is untested on Raspberry Pi OS bookworm (2026-09-13) - -**Shipped** (`a229be6d`): `build-linux-arm64` publishes `moonlight_X.Y.Z_arm64.deb` and a tarball, -the container image is a multi-arch manifest, and the SBC route is `apt install`. Verified on a -NanoPi R28S (Debian 13 trixie): installed, ran, and reported to MoonStats as `linux-arm64`. - -One piece of that verification was never done, because the bench has no such board. The job runs on -`ubuntu-22.04-arm` **deliberately**, for glibc 2.35: Raspberry Pi OS bookworm ships 2.36, and a -build on `ubuntu-24.04-arm` (glibc 2.39) would install there and then die at startup with -`GLIBC_2.38 not found`, the same failure the Dockerfile records hitting on debian12. The floor is -what makes bookworm work, and **bookworm is the one target nobody has run it on**. - -Two halves are untested together: that the binary starts (the symbol check says the highest -requirement is `GLIBC_2.35`, which is the proxy), and that `Depends: libcurl4 | libcurl4t64` -resolves there. Trixie provides only `libcurl4t64`, bookworm only `libcurl4`, and only the first -half has been exercised. A wrong alternation fails visibly at `apt install` with an unsatisfiable -dependency rather than installing something broken, which is the good failure mode. - -**A user with a Pi settles it in one command.** Closing this needs a report, not a code change. - -### An example: MoonLight in a pod beside the music server (belrik, 2026-10-21) - -belrik runs MoonLight in a Kubernetes pod with PulseAudio, squeezelite and sendspin in containers of their own, playing 96 kHz / 24-bit files through RAOP, each part upgraded on its own. -That is audio-reactive light fed from a music server, and the reference docs do not yet show how containers share a PulseAudio server. -An example page under Reference → Examples, built from belrik's manifest, shows it; asked for on Discord. - -### A flashable SD image with MoonLight already on it (robwomp, 2026-09-08) - -Suggested on Discord while bringing up a NanoPi R28S: most Pi users want to write an `.img` to a -card and be running, not to install a toolchain. Armbian's own build tooling supports exactly this -(`armbian/os` carries per-application "extensions", OMV being a small worked example), so the image -is a spin of a maintained distribution plus our package rather than a distribution to maintain. - -Wants the arm64 `.deb` above first: with it the extension is roughly "install this package, enable -this service", which is the shape those extensions already have. Without it, the image would have to -carry a source build, which is the thing it exists to avoid. - -### OTA upload refuses a normal client: the body must arrive within ~50 ms (2026-09-02) - -`POST /api/firmware/upload` answers `400 {"error":"incomplete request body"}` to an ordinary -`curl --data-binary @firmware.bin`, in 37 ms, before reading the image at all. - -The streaming branch sets `bodyNeeded` to the whole prefix buffer and then polls for it with a -50-iteration, 1 ms budget (`HttpServerModule.cpp`, the read loop). A client that writes its headers -and pauses before the body, which curl does, trips that timeout and is rejected. The browser path -works because `fetch` hands the whole body to the socket at once. - -Worked around by writing headers plus the first 8 KB in a single `sendall` from a small Python -client, after which a 1.97 MB image uploaded in 6.3 s and the device rebooted correctly. So the -transfer is fine; the acceptance test is what is wrong. - -Worth fixing because OTA is the only route to a board whose USB port is unavailable, which is -exactly when a firmware update matters most. The fix is to wait for the CONTENT-LENGTH the client -declared rather than for a buffer to fill, and to time out on stall rather than on total elapsed. - -### Release 2.0 — distribution catches up to the source tree - -1.0 ships ESP32 firmware (4 variants) + macOS arm64 + Windows x64. Still to add: - -- **ESP32-P4** firmware variant: **`esp32p4rev1-eth` (Ethernet-only) shipped**: in `build_esp32.py`'s `FIRMWARES`, the `deviceModels.json` catalog (Waveshare P4-NANO), and CI builds + publishes it to the web installer + releases. **`esp32p4rev1-eth-wifi` now ships too** (2026-08-19): it booted and associated on IDF v6.1-rc1, so it is out of the experimental set in the installer and carries a normal description. On IDF v6.1 it no longer associates (2026-10-12): see § ESP32-P4 round 3. -- **ESP32-P4 v3.x silicon variant (backlog)** — `esp32p4rev1-eth` is built for pre-v3 P4 (`CONFIG_ESP32P4_SELECTS_REV_LESS_V3` + `REV_MIN_0`), because the v6.1 IDF default (v3.1) refused to boot on the bench/field v1.x P4 and rev <3.0 vs >=3.0 are "huge hardware difference" (one binary can't cover both). **DONE (2026-08-19): `esp32p4rev3-eth` and `esp32p4rev3-eth-wifi` ship** (`REV_MIN_300`, which covers v3.0-v3.99), reusing the rev1 board fragment so the partition table and EMAC config stay in one place. **Still open: neither has ever been booted** — both bench boards are v1.3 engineering samples, so the rev3 images are flagged experimental in the installer and need a v3 board to verify. Espressif does not recommend v0.x/v1.x for new designs, so a board bought today is v3.x and needs these. -- **Retire the OTA fallback repository, once a device has another recovery route.** `FirmwareUpdateModule.h` names two: `kReleaseRepo` is tried first and `kFallbackRepo` catches a device whose firmware predates the move. It is tempting to call the second dead code after the rename, and it is not: `platform_esp32_ota.cpp:195` retries the alternate address whenever `esp_https_ota_begin` FAILS, which covers DNS, TLS, an outage and a variant whose asset is missing, rather than only a repository that has moved. So a v6.0.0 device on the happy path never reaches it and a v6.0.0 device whose first request fails still does. Removing it deletes that second chance for every board, which wants a replacement rather than a deletion: a configurable update URL, or a retry against the same address, would give a device somewhere to go. The one cost of keeping it is a wasted request on the failure path, which is already the slow path. -- **Raspberry Pi** — ARM64, cross-built or native. -- **macOS code-signing (Developer ID)** — the release `.dmg` is now ad-hoc signed, which turns Gatekeeper's outright refusal into the "unidentified developer" prompt a user can accept via right-click Open. A paid Developer ID certificate plus notarization would drop that prompt too. -- **Windows code-signing** — drops the SmartScreen warning on first run of `MoonLight.exe`. Same shape as macOS signing; needs an EV / OV code-signing certificate (Microsoft Trusted Signing is the cheapest current option). Until then, the README notes the SmartScreen prompt. -- **Live RMII Ethernet reconfigure** — runtime PHY/pin config shipped (`ethType` + pin controls in NetworkModule, per-board defaults in `deviceModels.json`, `platform::setEthConfig`/`ethInit` dispatch). W5500 (SPI) on S3 applies **live** — `ethStop()` tears down the SPI bus and `ethInit()` re-runs on the next `loop1s()` with no reboot. RMII (classic/P4 internal EMAC) still saves config and asks for a restart to apply, because the EMAC bring-up is fiddlier to hot-cycle cleanly. Make RMII live too: a hot `esp_eth_stop` + EMAC/netif teardown + re-init on config change, matching the W5500 path, so every interface honours the no-reboot principle. -- **GCC below 16 needs four warnings demoted, and nothing exercises those versions** - `-Wnull-dereference`, `-Wrestrict`, `-Wstringop-overflow` and `-Wformat-truncation` fire on provably correct code from GCC 12 through 15 (five of the twelve inside libstdc++ and glibc headers, unreachable from our source), so CMakeLists demotes them to non-fatal there and keeps them fatal on 16+. That unblocks CI and from-source builds on Debian and Raspberry Pi OS alike, but it is a suppression, not an understanding: nobody routinely compiles with 12-15, so a REAL instance of one of these on those versions is now a warning nobody reads. Revisit when the runner's default GCC reaches 16, at which point the whole block can be deleted. -- **Installer UX polish** — clear "Pre-release (beta)" warning on RC/latest picks, yank-by-asset-tag instead of yank-by-release-deletion. -- **Offer MoonLight/MoonLight as a library**: a downstream sketch where another firmware/app consumes the light pipeline (or a subset) as an embeddable dependency rather than running the whole binary. `library.json` is already a PlatformIO *library* manifest, so the seed exists. When this is designed, give it a small public **identity surface**: one runtime constant the consumer reads (a `kProjectName`, likely a `ProjectInfo` bundle of name + version + url) that the network wire-strings (ArtNet/E1.31 source-name + CID), the UI banner, and any "About" string all *derive from*: the one place a consumer queries "what am I embedding." This is the genuine home for the name-centralisation that the rename ([the MoonLight plan](../past/plans/Plan-20260922%20-%20MoonLight%2C%20from%20v5.0.0%20to%20the%20rename%20(shipped).md)) deliberately *didn't* do: the rename is a one-time sweep (a constant would just split it), but a library consumer references the identity ongoing and widely, which is the test a constant must pass. Build it *then*, against the real library API, not speculatively now. -- **HTTP: a request split across packets, or a body, that arrives a few ms late is dropped, intermittently (2026-08-20).** `handleConnection` runs SYNCHRONOUSLY inside `tick20ms`, so its waits are kept short to protect the render loop. - A connection with no bytes yet is parked and served on a later tick (2026-10-09), but a request whose headers arrive in two parts gets **~5 ms** for the rest, and a body **~50 ms** (`empties > 50`). - A client that misses either budget gets a request processed as truncated, or a `400 {"error":"incomplete request body"}`. Parking these too needs a buffer per parked connection. - - **Observed:** the MoonLive live scenarios fail roughly **1 run in 3** on the S3, always as a - failed `POST /api/file` that cascades (`module not found` for every step depending on that - script). A 40-write burst failed 4/40. Not reproduced on the classic (3/3 clean), so it may be - S3-specific or simply load/timing-dependent. - - **NOT render load — that theory is disproved.** Disabling the heavy driver took the tick from - 2889 us to 472 us (fps 346 → 2118) and made it WORSE: 14/40 failures instead of 4/40. So a - faster tick means more accept batches per second and more chances to hit the window, which - points at the budget itself rather than at contention. - - **Why it matters:** any client on a busy or distant network can lose a write with no useful - error, and the UI's own file saves ride the same route. The fix is not simply a longer wait — - that would stall the render loop, which is what the budgets exist to prevent. It wants the - connection handling moved off the render tick, or a state machine that parks a partial request - and resumes it next tick instead of dropping it. - -- **Desktop backend: a control-arena write is not seen by a second `run()` of an already-compiled - program (2026-08-20).** The live-edit path is "move a slider, the next frame reads the new arena - byte, no recompile". On the host JIT the first render is correct, the write lands (the arena - reads back `232,3` = 1000 at the control's own `offset`), and a second `run()` of the same - compiled program still renders the OLD value. - - **Ruled out — do not re-investigate these:** - - Not `LoadCtrl16` missing: it is implemented, lowers to `a.load16`, and `load16` exists in all - three assemblers including `platform/desktop/moonlive_asm_host.cpp` (`ldrh`, immediate scaled). - - Not uint16-specific: a `uint8_t` member behaves identically. - - Not `defineControls()` or the control sink: a class with an unrelated second function fails the - same way, and one with no members at all (a literal in `tick`) does too. - - Not the arena address or offset: `controlSlot(dc[0].offset)` is the byte the write reaches. - - **A large part of the original symptom was a TEST BUG, not an engine bug.** `run()` without an - entry name starts at the BLOCK START — the first function compiled — which in a class with - `defineControls()` is `defineControls`, not `tick`. So the script rendered nothing and looked - like a broken control read. Naming `kEntryTick` makes the initial render correct in every - variant tried (with/without `defineControls`, with an unrelated second function, `tick` first or - second, uint8 and uint16). Only the re-read after a slot write is still wrong. - - **Reproduce** (in `unit_moonlive_fill.cpp`, which has `kCtrlTable`/`kSys` to hand): compile - `class T { int big = 5; defineControls() { addControl("big", big, 0, 1000); } tick() { - setRGB(0, big, 0, 0); } }`, `run(..., kEntryTick)` → 5, write the slot to 7, run again → still 5. - - **Impact: desktop only, nothing ships broken.** A wide control is hardware-verified on both ISAs - (S3 Xtensa and S31 RISC-V drive ember's `cycle` to 2000 and back). What is missing is DESKTOP - coverage of the live-edit loop — the path users touch most — so no host test can pin it and the - next regression there would surface only on a board. Add the runtime assertion together with the - fix, not before: asserting the current behavior would encode the bug. - -- **ESP32-P4 DHCP hostname not shown by the router (recheck later)** — the device sets its DHCP hostname (option 12 = `deviceName`, default `MM-XXXX`) in the `ETHERNET_EVENT_CONNECTED` handler, verified working on two boards: the S3 over WiFi (router shows `MM-70BC`) and the Olimex over RMII Ethernet (`MM-BD3C`) — the *same* `ethEventHandler` code path the P4 uses. Yet the bench P4 (Waveshare P4-NANO, RMII) still shows as blank/"Unknown" in the GL.iNet client list, while serial confirms `set_hostname` succeeds with no error. Two unconfirmed suspects, neither our logic: (1) the router holds a **sticky lease** for the P4's MAC and won't relearn the hostname until it fully expires (the per-client "forget" isn't exposed in this GL.iNet UI, and a plain reboot didn't clear it); (2) a P4-specific IDF netif quirk serializing option 12 differently on the newer P4 Ethernet path. Since the shared code path is proven on two other boards, this is not treated as a code bug. Recheck after the P4's lease naturally expires, or on a different router, before spending more on it. - -### A Service that calls another device's API (robwomp, 2026-10-11) - -Suggested on Discord: panels driven by another stack take their pixels from MoonLight over DDP or Art-Net, but their settings stay out of reach. -A user-added Service holds a list of HTTP requests, each a method, a URL and a body, and fires one when a control it watches changes, so a button, a MIDI knob or an OSC message drives any device that answers `curl`. -Inbound control already exists (`/api/control`, MQTT, OSC, MIDI); this is its outbound twin, and the generic complement to the typed command half of DevicesModule below, which knows each ecosystem's API. -Requests leave the render thread, since a slow peer must never stall a frame. - -### DevicesModule — interop plugins + the command half (discovery shipped) - -DevicesModule discovers via **passive UDP presence** (UDP 65506) feeding a [`DevicePlugin`](../../src/core/util/DevicePlugin.h) seam (shipped: MoonLight + WLED plugins). mDNS is advertise-only so MoonLight appears in the native WLED apps + Home Assistant; the WLED-app interop (list + live color + brightness control) is shipped too. What remains is *growth on the seam*, each piece additive (one plugin file, no core change): - -- **More discovery plugins**: ESPHome, Tasmota, Hue (*hub-shaped*: a bridge whose Zigbee bulbs are children behind it, with link-button auth). Each is a new `DevicePlugin` declaring its `discoveryPort()` + classifying the datagram (or, for a system that only does mDNS, a re-introduced advertise-side browse scoped to *foreign* services only, never the ones we advertise). Hue is the canonical "more than a flat device" case the seam is shaped for. (Note: Hue *control* already ships as an **output driver**, see [HueDriver](../../moonmodules/light/drivers.md#hue): bulbs as effect pixels; the driver also *lists* its bridge in DevicesModule with the color-light count. Two complementary follow-ups remain: (a) auto-fill the driver's bridge IP from discovery so the user doesn't type it (the mDNS-browse plugin above); (b) **pair once, not per driver**: pairing + the app key currently live on each HueDriver, so two drivers on one bridge pair twice. The clean end-state moves the bridge identity (IP + key + Pair button + light list) into DevicesModule and makes HueDriver a pure output that reads the paired bridge by IP; do this together with the discovery plugin, since both hinge on DevicesModule owning the bridge.) -- **The command half** — `DevicePlugin::command()` (+ per-plugin capability/auth), so MoonLight can *control* a discovered foreign device, not just list it: set WLED brightness via its JSON API, a Hue resource via the bridge's authenticated CLIP API, a Tasmota via `cmnd`. Built when a control consumer exists; the discovery seam is already shaped to accept it (incl. hub plugins). This is the **multi-ecosystem selling point** — one UI controlling WLED + ESPHome + Hue. Commands split by need (the rule, not "all REST"): must-arrive config over REST; latency-critical sync over UDP (~0.5–1 ms vs REST's 10–50 ms — REST would visibly de-sync). -- **Live peer state** — a discovered peer's brightness / on-off shown in our list, refreshed by polling its REST `/json` after discovery gives the IP (discovery = UDP/mDNS, state = REST). The read-side complement to the command half. -- **Non-IP transports (board-gated, far future)** — Tasmota-MQTT / zigbee2mqtt need an MQTT client; **direct Zigbee/Thread** (S31/C6/H2 802.15.4 radio) makes MoonLight the *hub itself*, driving bulbs over the mesh with no gateway — the standout differentiator, the biggest lift. Same plugin philosophy, a transport addition + board gate. - -Full design + the reasoned transport split: `Plan-20260629 - UDP device discovery + mDNS advertise-only` (in the plans archive). - -## MoonBase follow-ups - -MoonBase v1 ([MoonBase](../../explanation/architecture/moonbase.md)) -ships exactly one action: install firmware (upload + URL). The name is deliberately broader than -"recovery", these are the candidate next actions, each solving something only a separate boot -image can solve. The budget rule from the partition table applies to all of them: the 896 KB slot -has ~150 KB headroom, sized for one new *component*, so each action must earn its bytes (a few KB -of code is fine; a new IDF component is the expensive kind). - -- **Factory reset**: erase the filesystem (and optionally NVS) from MoonBase's page: recovers a - device whose config crashes the app on boot, without a USB cable. -- **Boot with config disabled**: one-shot flag the app reads at startup to skip loading - `/.config`: diagnose "is it my config or the firmware?" without erasing anything. -- **WiFi re-provisioning**: edit the stored credentials from MoonBase's page (today it only - *reads* them; the AP fallback plus the app's provisioning already covers most of this). -- **Publish SHA-256 sums with release assets**: the Defender false-positive guidance can only - say "re-download over HTTPS" today; a checksums file per release lets a user verify a - quarantined installer before restoring it. One sha256sum step in the release staging. -- **Streamed HTTP responses drain across ticks**: serveFileContents/serveHlsFile block the - render tick for the whole transfer (a slow player fetching a ~1 MB HLS segment every second - can hold it for hundreds of ms), and fsReadAt reopens the file per 1 KB chunk. The named fix - is the writeSome/drain pattern preview already uses, plus a larger chunk. -- **Re-entrant network bring-up**: NetworkModule::setup() (netif create, driver install, the - eth→WiFi→AP cascade) runs once at boot and crashes if re-run live, so it opts out of the - live config apply (`appliesConfigLive() = false`) and a restored NetworkModule.json waits - for the next boot. The real fix is bring-up that reconciles instead of re-creates; until - then the restore dialog offers the restart. -- **Config backup / restore, tiers 2+3**: tier 1 (browser-side bundle over the file API, with - the rename map and restore report) ships in the File Manager. Remaining: tier 2, a - single-archive device endpoint (one request instead of a walk); tier 3, restore hosted on - MoonBase's page, the migration answer for future partition-table moves. -- **Restore clones a device's IDENTITY along with its config** (found answering - [#76](https://github.com/MoonModules/MoonLight/issues/76), 2026-09-04). Backup bundles every - file including hidden `.config`, and restore writes them all back unfiltered, so restoring one - device's bundle onto another copies four things that must differ per device: - - | field | why it must differ | - |---|---| - | `deviceName` | the single network identity: mDNS hostname, SoftAP SSID and DHCP hostname all derive from it (`SystemModule.h`). Twelve clones all answer to `.local`, resolution goes non-deterministic, MoonDeck's device list collapses to one row | - | WiFi `password` | travels in the bundle (the UI button warns), so a shared or attached backup leaks it | - | a static IP | if set, every clone claims one address | - | `universeStart` and the Art-Net/DDP window | exactly what must differ per device in the light-pole case #76 describes: cloned, every pole shows the same thing | - - This turns "clone this pole to the other eleven" from the feature the issue wants into a trap. - **The fix is small and is a prerequisite for the recipe idea rather than a separate job:** restore - treats identity as per-device, either skipping those fields or prompting once with the target's - current values prefilled. Worth deciding whether the bundle should carry the secrets at all, or - keep an identity section the restoring device is expected to supply. -- **A backup is a file bundle, not a recipe** (#76 step 2): nothing binds it to a `deviceModel`, so - restoring a Dig-Quad bundle onto an S3 writes pin maps that do not fit the board. The 28 profiles - in `deviceModels.json` are the missing half. -- **Firmware downgrade guard**: MoonBase installs whatever image it is given; a version display - (read from the incoming image's app descriptor) before flashing would make an accidental - downgrade visible. -- **Hardware diagnostics**: chip/flash/PSRAM identification and a minimal pin tester, for - triaging a board that misbehaves under the full app. -- **Ethernet: shipped for classic RMII (2026-08-26)**. MoonBase reads the eth wiring from the - same config file as the credentials (ethType gates it) and runs ONE interface at a time in - the app's own preference order (eth, else WiFi, else AP), so the browser keeps the address - the app had. Still open here: the P4's IP101/managed-component PHY and the S3's SPI W5500, - which matter only if MoonBase ever goes beyond the 4 MB classics. -- **Static IP for MoonBase**: MoonBase always uses DHCP; a venue network without a DHCP server - (fixed-address rigs exist) would reach the app (static `addressing`) but not MoonBase. Read - the addressing block from the same config scrape when a venue actually asks for it (the app - itself applies static addressing to eth already). -- **MoonBase as the only update mechanism, all boards** (PO, 2026-08-26): would delete the app's - whole in-place OTA path (a real subtraction) and grow every app slot, with the stronger - power-fail story everywhere. Three deciding factors first: MoonBase Ethernet beyond classic RMII (the P4 has no - WiFi of its own and uses a different PHY; the S3 uses SPI W5500), a migration that does not lose config (backup/restore above), and accepting - ~45 s of visible downtime per update where dual-OTA installs in the background. Trigger: - MoonBase proven in the field on the 4 MB boards. - -## Provisioning and live-reconfig gaps (bench, 2026-08-26) - -Both surfaced while bringing up MoonBase Ethernet on the migrated Olimex; neither is MoonBase's. - -- **A migrated device is not fully provisioned until the deviceModel catalog push is re-applied.** - After the MoonBase table migration the board ran for days with `deviceModel` empty and - `ethType` 0: Improv/AP provisioning restores WiFi credentials only, and nothing tells the user - the catalog half (eth wiring, per-board settings) is missing. Candidates: MoonDeck re-pushes - the catalog on discovering a device whose deviceModel is empty, or the UI badges the state. -- **`ethType` does not apply live.** Setting it to 1 over the API (cable in, pins valid) left - Ethernet down; the same config brought it up at the next boot. Every setting applies live is - a core principle (architecture.md, Live reconfiguration), so the eth init path is missing from - the control's apply/build-state sweep. - -## ESP32 performance and memory - -### LEDs on the Teensy flash white, then the board resets (2026-10-13) - -Five times in one session, the 64-light strip on the Teensy 4.1 bench board went full white, and the board restarted with reset reason `UNKNOWN`, no fault mark and no watchdog: a supply dip, which a full-white strip on USB power explains. -Each white flash came while the device built a large state document: switching SdCard's board with the old 46 KB full state, and subscribing to Drivers or the whole tree with the new one; none came with the LEDs disconnected. -A WS2812 strip reads a data line held high as all ones, so suspect the GPIO-DMA output stalling high, or its buffer moving under the DMA when the Layer reduces its buffer under memory pressure ("buffer reduced" was showing). -Reproduce with a logic analyser on the data pin while subscribing to Drivers, before changing code. - -### A Teensy sometimes drops a message the page sends (2026-10-13) - -Choosing Custom on SdCardService's board select reached the Teensy once in three tries in one session, and every time in later ones; the same clicks always reach the Dig-Octa. -A lost write leaves the page showing a choice the device never made, and nothing tells the user. -Suspects, in order: the `/wsp` preview stream filling the send side while a frame from the page waits, a long render tick delaying the poll that reads the socket, and power dips while the LEDs show full white. -Reproduce with the preview open and closed and the LEDs dark and lit, logging each frame the device receives, before changing any code. - -### One board-preset helper for Ethernet and the SD card (2026-10-12) - -EthernetModule and SdCardService each carry the same board-preset machinery: the per-chip filtered option list, selection by label, the applied tracker, applying a preset when the selection moves, and reading the preset back off the pins. -A `core/module/BoardPresets.h` helper would own the options, index, count, selection and applied state, with the module supplying its table and two functions: write a preset into the pins, and match the pins against one. -Do it before a third preset table appears (a display's or a microphone's wiring), so that one becomes a table rather than a third copy. - -### SD card writes over SPI stop the card on the Dig-Octa (2026-10-12) - -On the QuinLED Dig-Octa's SPI slot (SCK 14, MOSI 15, MISO 36, CS 16), a write past about 50 to 200 KB fails mid-transfer, and the card then answers no command until its power is cut. -Reads hold: a 5 MB file reads back byte-exact at 184 KB/s. -The same no-brand 32 GB card writes 5 MB cleanly on the native SD bus of the P4 and of the Olimex ESP32-Gateway. -Ruled out: the clock (20, 10 and 4 MHz fail alike), 30 ms between writes, and the wiring, which Quindor's schematic shows with 10 kOhm pull-ups, its own LDO and decoupling. Turning the LED driver off moved the limit a little without removing it. -Next: the same 5 MB test with an A-brand card, since SPI mode is the part of the spec newer cards treat as secondary. ESPixelStick, the one other firmware driving this slot, throttles its writes into 7 KB blocks with a 30 ms gap and pauses LED output during an upload (forkineye/ESPixelStick issue 802 and `src/FileMgr.cpp`). -Until it is solved, a card mounted over SPI shows a warning status. - -### A classic ESP32 on WiFi stalls while the state document builds (2026-10-12) - -A full state fetch serializes the whole tree into one buffer, 49 KB on the Olimex ESP32-Gateway with an SD card, LEDs and both network interfaces. -On WiFi a classic has about 80 KB free at rest, so a browser's fetch or resync drops the largest free block to 4 to 14 KB, and new connections stall until it is freed: the page stops answering and the LEDs pause. -On Ethernet the same board keeps 121 KB free and a 108 KB block, and never stalls. -The fix is to stream the document: write each module's JSON straight to the socket as it is produced, so the peak is one module rather than the tree. -The same applies to the websocket resync, which sends the same document. -Measured with this PR's SD card against `main`: 1.4 KB more static DRAM, 4 to 7 KB more heap while a card is mounted, so the stall exists on `main` too. -Watch IRAM on the classic as well: the SD and SPI drivers' interrupt handlers raised it from 98 KB to 107 KB of about 128 KB. - -### Size estimates for unbuilt features (reference) - -Estimates, not measurements, so they live here rather than in [performance.md](../../reference/performance.md) which carries measured numbers only. (The 4 MB flash-budget investigation these once fed is resolved: MoonBase's single-app-slot layout grew the classic app slot to 2496 KB, see architecture.md § MoonBase.) - -| Feature | Est. | Rationale | -|---|---|---| -| Mozilla cert bundle trimmed | −40 KB | `CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN` keeps common roots only. `_NONE` saves ~50 KB but breaks TLS. | -| Global IPv6 | small | lwIP's IPv6 is already in, for the link-local address mDNS advertises; global addresses add autoconfiguration. Only if a deployment needs it. | -| WebSocket TLS (`wss://`) | ~0 KB | Reuses linked mbedTLS; certificate handling adds <5 KB. | - -### `ListSource` methods compile twice: ~8.6 KB of thunks (2026-10-01) - -Every module that is `MoonModule` and `ListSource` gets its list methods emitted twice on ESP32: once as the method, once as a "non-virtual thunk" for the second base, and GCC copies the whole body into the thunk instead of a jump. -Measured on the S3 image with `xtensa-esp32s3-elf-nm -S`: 8.6 KB in all, the largest `GamepadService` 1.6 KB, `ButtonService` 1.3 KB, `FixtureProfilesModule` 1.2 KB, `ControlModule` 1.1 KB, `InfraredService` 0.9 KB, `AnalogService` 0.9 KB. -Candidates, to measure rather than assume: a `ListSource` that is a member the module hands out (no second base, so no thunk), or keeping the bodies out of line so the thunk can only jump. -Done when the thunk total in the S3 symbol table is near zero, with the delta in repo-health. - -### An early state push costs 3 to 5 ms on the S3 (2026-10-01) - -`notifyValuesChanged()` patches one module on the next 20 ms tick, at most 25 times a second; with a MIDI desk following a self-playing Pong it took the S3 from about 325 to 295 fps, 3 to 5 ms per push. -Building that patch walks one module, so the time is most likely the WebSocket write on the render thread, but the split is unmeasured: one write per frame instead of two saved nothing. -Time the build and the send separately on the S3, then decide between moving the write off the render thread and a lower cap. - -### E1.31 multicast receive (IGMP join) - -NetworkReceiveEffect accepts E1.31 via unicast only — the same scope MoonLight ships. Multicast senders address the per-universe group `239.255.{universe_hi}.{universe_lo}`, which a receiver must join via IGMP. **The platform half of this now exists**: `UdpSocket::joinMulticast()` shipped with the WLED audio-sync work (2026-08-29) and is used in anger there, on both desktop and ESP32. What is left for E1.31 is the join-per-accepted-universe bookkeeping, not the socket support. Add when a multicast-only sender actually shows up on a bench; until then the spec documents "point sACN senders at the device's IP". - -**The SEND half is the more interesting one, and it's the honest scale answer.** sACN puts the universe number *in the group address*, so with **IGMP snooping** the switch filters per-universe in hardware — each node's NIC sees only the universes it joined. That is broadcast's send-once efficiency *plus* unicast's selectivity, and it's the one addressing mode that beats per-node unicast when many nodes want overlapping universes. `NetworkSendDriver` already knows its universe range, so the group address is a pure function of `universe_start` — a small increment, not a redesign. **The catch that keeps it off the default path:** without IGMP snooping the switch floods multicast exactly like broadcast (and on WiFi it goes out at the lowest basic rate to every station), so it degrades straight back into the starvation regime — and firmware cannot detect whether the switch snoops. So: unicast stays the portable default; multicast is the opt-in optimization for a network the user controls. Do the receive join and the send group together when it lands. - -### A scripted module's DIMENSION chip still comes from its type, not its script - -`writeModuleJson` emits an instance's `tags()` (HttpServerModule.cpp), so a MoonLive module shows -the emoji its loaded script declares. Its DIMENSION does not follow the same path: `/api/types` -carries `dim` per TYPE, captured at boot from a probe with no script loaded, and the module state -carries no `dim` at all. So a script declaring `int dimensions() { return 3; }` renders as 🟦 on the -card while behaving as D3 through `Layer::extrude`: the behavior is right and the chip lies. - -The fix is not a line in the serializer. `MoonModule` deliberately has no `dimensions()`: -`ModuleFactory::registerType` detects one with `if constexpr (requires ...)` on the CONCRETE type -precisely so the light-domain `Dim` enum stays out of core (ModuleFactory.h). Core holds a -`MoonModule*` when it writes state, so emitting a per-instance dim means giving `MoonModule` a -virtual that returns a byte, which puts a light-domain concept on the domain-neutral base for the -sake of a chip. - -Options, cheapest first: a `uint8_t dimByte()` on MoonModule defaulting to 0 (the enum stays in the -light domain, only the number crosses, mirroring what the probe already does); or leave it and -accept that a scripted module's dimension chip reflects its type. Worth doing when someone is -annoyed by the wrong chip, not before. - -### British spellings and em-dashes predate the prose gate (319 files) - -`check_prose.py` reports on ADDED lines only, so the American-spelling rule has been enforced from -the day it landed forward, and everything written before it was never swept. 118 files still carry -`colour`, `centre`, `behaviour`, `recognise`, `initialise` and friends, in comments and in a few -identifiers. - -**Em-dashes are the same story and the larger half**: 10,326 of them across 319 files under `docs/`, -against the same rule and missed for the same reason. They are worse than the spellings to sweep -mechanically, because the right replacement depends on the sentence (a colon where it explains, a -comma for an aside, a full stop between two clauses), so a blind substitution produces prose nobody -proofread. Count them per file and do the biggest offenders by hand. - -The gate keeps it from growing, so this is a one-time sweep rather than a leak. It is deliberately -NOT folded into a feature branch: a whole-repo rename touches more files than any review can read, -and mixing it with real changes is how a review gets declined for size. Do it as its own commit, -mechanically, with the gate run over the whole tree afterwards rather than over a diff. - -Identifiers first and separately: a rename changes an API, where a comment does not. `centre()` in -the shipped `crosshair.mle` was one and is already fixed, since a shipped teaching script is the -highest-value case. - -**Now caught going forward for scripts too**: `.mle`/`.mll`/`.mlm` joined the checker's SUFFIXES -(they were unchecked, which is how `colour` reached ten shipped scripts), so the library cannot -drift again. - -### ESP32 UDP receive is bounded by PACKET COUNT, not bytes - -Reported from other projects working the same ground: the ESP32 family's incoming UDP limit behaves -as a **mailbox of packets** rather than a memory budget, with a hard numerical ceiling, and the P4 -is no better. The observed shape is perfect reception up to that count and then progressively worse -loss as universes climb, rather than a clean cliff. Raising it is tunable but bounded, since the raw -packet buffers are full-frame sized whatever the payload. - -Two consequences worth having in mind: - -- **Pixels-per-packet is the lever, not bandwidth.** Art-Net is DMX512 on the wire, so it is capped - at 512 values per packet whatever the frame size; DDP fills close to a whole MTU. For the same - pixel count DDP therefore needs far fewer packets, which is the resource that runs out first. - This matches our own measurement that ArtNet is where the WiFi limit bites. -- **A P4-specific escape exists.** Have the packet handler DMA the payload to PSRAM and release the - hardware buffer immediately, then parse from another task, so the mailbox drains at memory speed - rather than at parse speed. That is real work and speculative, but it is the shape of a fix rather - than a tuning knob. - -Unverified on our own bench: this is other people's measurement, recorded so the next receive-path -investigation starts from it rather than rediscovering it. Confirm before acting on it. - -### WiFi ArtNet performance (pending investigation) - -128×128 WiFi ArtNet measurements exist (see [performance.md](../../reference/performance.md) "ArtNet over WiFi" and "Build-variant WiFi comparison"). Remaining matrix: - -- WiFi STA 64×64 (4K LEDs, 24 universes) -- WiFi STA 32×32 (1K LEDs, 6 universes) - -This determines the practical LED limit for WiFi-only boards. Until the `sdkconfig.defaults` TX-buffer fix lands (identified in the build-variant table), **prefer wired Ethernet for any ArtNet workload on classic ESP32** — the default `esp32` build carries both stacks, so Ethernet is available even when the original measurements were taken on the old `esp32-eth-wifi` variant. - -### Network round-trip test — drop/reorder measurement (deferred) - -`moondeck/scenario/run_network_roundtrip.py` measures desktop→device→desktop **latency and jitter** per protocol (ArtNet/E1.31/DDP) by timing how long a sent color takes to appear in the device's preview stream. It deliberately does **not** measure per-frame **drops or reorder**, because the path can't track individual frames cleanly: `NetworkSendDriver` re-clocks at its own fps (decoupled from receive) and `NetworkReceiveEffect` holds-last-frame, so frames don't pass through 1:1 — a sequence number embedded in frame N may be re-sent 0, 1, or several times downstream. The min/median/max spread the test already reports *is* the jitter signal (it surfaced multi-second outliers on the classic ESP32). To measure true drop/reorder, the firmware would need a sequence-faithful echo path (e.g. a `NetworkReceiveEffect` echo mode that re-emits each received frame 1:1 back to the sender, bypassing the fps re-clock), then the desktop could match sent↔received sequence numbers. The test's docstring lists this under "extend later" alongside per-frame sequence matching and the device→device chain. - -### Async ArtNet send — decouple the wire from the render tick (PSRAM-only) - -The ArtNet send is synchronous: `NetworkSendDriver::loop()` blasts ~97 universes (a 48 KB frame at 128×128) inline, and the per-universe `send()` blocks on lwIP TX backpressure — the netif/EMAC (or WiFi) drivers throttle to wire throughput. Measured on hardware: **~35 ms over Ethernet, ~90 ms over WiFi**, charged straight to the render tick, so ArtNet alone caps the Olimex at ~15 FPS and the S3 (WiFi) at ~7 FPS at 128×128. This is a transport throughput limit, **not** something a non-blocking socket can shed — verified that neither `O_NONBLOCK` nor `MSG_DONTWAIT` makes lwIP return early for UDP (the block is below the socket API; both flags drop zero packets and cost the same ~35 ms). The earlier "non-blocking recovered it to ~2 ms" reading was a transient external condition (the receiver/switch draining the burst freely in one window), unreproducible under steady load with the exact firmware. - -The real fix is a **dedicated send task**: `loop()` snapshots the corrected frame into a handoff buffer and signals the task; the send task drains it to the wire at its own pace while the render task continues. The tick stops paying the ~35–90 ms — render runs at its own rate (~30 FPS on the Olimex), ArtNet streams independently at whatever the link sustains. - -**Gate this on `platform::hasPsram`. It does not fit non-PSRAM boards at the grid sizes where it would help** — the math is hard: -- The handoff buffer must hold one full frame: **48 KB at 128×128** (16384 × 3), plus a ~4 KB task stack. -- The Olimex (no-PSRAM) at 128×128 runs at **~46 KB free heap, ~18 KB largest contiguous block** (measured). The 48 KB buffer exceeds *both* the total free heap and (by far) the largest block — it can't allocate at all, the same fragmentation cliff the paged MappingLUT just had to work around. -- A double-buffer (so the task reads frame N while render writes N+1) doubles it to ~96 KB — even more out of reach. -- At 64×64 the frame is only 12 KB and *might* fit, but at 64×64 the synchronous send is already fast enough that ArtNet isn't the bottleneck — so the task buys nothing where it's affordable on no-PSRAM. - -So the PSRAM gate isn't conservative; it's a hard requirement. PSRAM boards (S3/S2, Olimex-with-PSRAM variants) have megabytes for the handoff buffer via `heap_caps_malloc(..., MALLOC_CAP_SPIRAM)`; non-PSRAM boards keep the synchronous send and the documented "use Ethernet / smaller grid for high FPS at large grids" guidance ([NetworkSendDriver.md](../../moonmodules/light/drivers.md#networksend)). - -Acceptance criteria: `if constexpr (platform::hasPsram)` (or a runtime `hasPsram()` check) selects the async path; the buffer lives in PSRAM; the send task pins to the core opposite the render task (the same pattern as the shipped render↔encode split's worker). Non-PSRAM keeps `loop()`'s inline send unchanged. The single handoff buffer needs an explicit ownership contract, exactly like the shipped render↔encode split's `outputBuffer_`: the render path may write frame N+1 only after the send task signals it has finished reading frame N (a binary semaphore/notification is the handoff), so the two never touch the buffer at once. When the sender still owns the buffer at the next render tick, the render path drops (or coalesces onto) that frame rather than overwriting a live read — a dropped frame is acceptable, a torn one is not. That single-buffer contract is the minimal shape; a second buffer (double-buffering) or a ring of frames is only warranted if measurement shows the drop rate hurts, or a second consumer appears. - -### `esp32-eth` slow Ethernet bring-up vs `esp32-eth-wifi` (investigation) - -On Olimex ESP32-Gateway flashed with `esp32-eth`, Ethernet sometimes takes **a minute or more** to acquire a DHCP lease at boot. The same hardware flashed with `esp32-eth-wifi` brings Ethernet up in seconds. The B1 Idle-recovery fix in `src/core/system/NetworkModule.h` masks the symptom (status correctly transitions to "Eth: " once the lease arrives), but the underlying slow bring-up is a real performance regression on the eth-only build. - -What we know: -- `build/esp32-esp32-eth/sdkconfig` and `build/esp32-esp32-eth-wifi/sdkconfig` are **byte-identical** (3,617 lines each, `cmp -s` confirms). So lwIP buffer pools, DHCP timeouts, and Ethernet driver settings are the same. -- Same hardware (Olimex ESP32-Gateway Rev G), same RMII pin/clock config (`EMAC_CLK_OUT` on GPIO17), same `ethInit()` code in `src/platform/esp32/platform_esp32.cpp`. -- The only difference at link time: `esp32-eth` passes `EXCLUDE_COMPONENTS=esp_wifi;wpa_supplicant;esp_coex` to ESP-IDF (see `moondeck/build/build_esp32.py:31`). -- `esp_coex` (WiFi/Bluetooth coexistence) was an early hypothesis: even though Ethernet doesn't share the radio, `esp_coex`'s init might warm a shared clock path that helps Ethernet auto-negotiation, and the eth-only build excludes it. **Disproven — see below.** - -**Firmware is ruled out (the evidence is contradictory across reboots with the *same* build, which by itself proves build-independence).** Over one session, on the same Olimex + cable: -- `esp32-eth-wifi` (keeps `esp_coex`) → flapped: `Ethernet link up` → `link down` repeating, never reached DHCP. -- `esp32-eth` (excludes it) → on one flash **came up immediately and worked**; on a later flash **flapped the same way**. - -So the *same* eth-only build both works and flaps at different times, and the eth-wifi build flaps too. The instability does **not** track the firmware build — it's intermittent. That kills the `esp_coex` theory and any WiFi-interference theory. It also confirms our code isn't the cause: `mm_net: Ethernet link up/down` is logged straight from the ESP-IDF `ETHERNET_EVENT_CONNECTED/DISCONNECTED` events (`platform_esp32.cpp:238-243`) — the **PHY hardware reports the drops**; `NetworkModule` only reacts, never stops/restarts the link. Memory is ruled out (boot heap 286 KB, steady 133 KB free / 110 KB block — abundant). - -**Signature = physical layer.** When flapping, the link holds for ~2 s, drops, comes back ~10 s later — a repeating cycle, not random. That fingerprints the PHY auto-negotiating, holding briefly, then losing sync: marginal **PHY power, clock, cable, or connector**, not firmware. - -**Correlates with board reset.** The flapping tends to *start* right after a flash/soft-reset (this session: a reset preceded each flapping window; a clean power-up tended to link cleanly). Fits the documented slow/flaky PHY re-link on the Olimex after a reset — on some resets the PHY settles, on others it cycles for a long time before (or instead of) holding. - -What we still don't know (all **physical** tests — no code change is warranted): -- Does a **clean power-cycle** (vs soft reset) reliably link? (Tests the reset-relink correlation.) -- Does **barrel-jack / stronger 5V** power stop it? (Tests PHY brown-out under USB-only supply, a known Olimex-Gateway weakness.) -- Does **swapping cable / switch port** stop it? (Rules out cable/connector.) - -Bottom line: intermittent, build-independent, reset-correlated → a hardware/PHY issue, not a firmware bug. The earlier "slow DHCP at boot" is likely the same root cause (the PHY cycling many times before one window holds long enough to complete DHCP). Pick this up with the physical tests above before touching any code. - -### MoonDeck doc-asset endpoint hardening (backlog) - -`moondeck/moondeck.py::_serve_doc_asset` accepts any ROOT-relative path and serves the file. Path traversal *is* blocked (`asset_path.relative_to(ROOT.resolve())`), but inside the repo any file is served — including local-only artefacts like `moondeck/build/wifi_credentials.json` if present. MoonDeck binds to all interfaces by design (the existing comment in `main()` explicitly enables LAN reach), so anyone on the LAN can hit the endpoint. - -Two improvements when this matters: -- **Subdirectory whitelist** — only serve under `docs/` (and image asset paths the markdown renderer needs). Reject `moondeck/build/wifi_credentials.json` etc. with 403. -- **Extension whitelist** — only image / CSS / JS mime types via a small allowlist. -- **Optional bind-to-localhost flag** — `--bind 127.0.0.1` for users who don't want LAN reachability. Default stays "" (all interfaces) since the LAN-reach is the documented design. - -Not blocking — MoonDeck is a developer tool, not a production server. Pick this up when MoonDeck is in scope for hardening. - -### A tagged release does not reach the web installer until the next main deploy (bug) - -`deploy-pages` in `.github/workflows/release.yml` is gated `if: github.ref == 'refs/heads/main'`, because the `github-pages` environment's protection rule only allows main. The installer's release list is **staged into the Pages site at deploy time** (`install.js` self-hosts the last 5 stable + 5 prerelease releases; the release-asset URLs redirect to a host that sends no CORS header, so the browser cannot read them from the Pages origin). Together those mean **pushing a `vX.Y.Z` tag publishes the release but never updates the installer** — the new version reaches the picker only when something later pushes to main. - -Hit on v4.0.0 (2026-08-24): the release published at 16:15:59, a main deploy ran at 16:16 and enumerated releases *before* it existed, and the installer offered v3.0.0 as newest for hours. Re-running the workflow with the tag fixed it, and that manual re-run is the current workaround. - -Note the device's own OTA picker is unaffected — it reads `api.github.com` live ([app.js](../../src/ui/app.js) `RELEASES_API`), which is why a device could offer v4.0.0 while the installer could not. Two independent paths to the same release list. - -`restage-pages-for-tag` in the same workflow already covers this: on a `v*` tag it re-invokes the workflow on `main` with `tag=`, so `deploy-pages` runs under the allowed ref and the manifests land in `releases//`. The open item is narrower than the symptom suggested — whether the dispatched run can still lose the race, since it is a *second* run whose release enumeration happens later, and whether relaxing the `github-pages` environment's branch policy (needs repo-admin) would remove the indirection entirely. - -### CI: pin GitHub Actions to commit SHAs (supply-chain hardening) - -`.github/workflows/release.yml` references all 9 action types by mutable `@vN` tag (`actions/checkout@v4`, `astral-sh/setup-uv@v3`, `softprops/action-gh-release@v2`, `espressif/esp-idf-ci-action@v1`, …). A mutable tag can be force-moved to malicious code by a compromised publisher; pinning each `uses:` to a full commit SHA (with a `# vN` trailing comment) removes that vector. **Done already (cheaper half):** `persist-credentials: false` on every checkout that doesn't push, so the `GITHUB_TOKEN` isn't left in `.git/config` for later steps to read (the `release` job keeps it — it force-pushes the `latest` tag). **Not done (this item):** SHA-pinning, because it carries an ongoing cost — pinned SHAs go stale and miss security patches, so it only pays for itself **alongside Dependabot** (or a Renovate config) to auto-bump them. Pick this up as a deliberate "CI hardening + Dependabot" pass, not piecemeal. Low risk today: every action pinned is a first-party `actions/*` or a well-known publisher (astral, espressif, softprops), not an obscure third-party action. - -### Static IP on WiFi STA — wire the existing fields to the network (backlog) - -NetworkModule exposes `addressing` (DHCP / Static) plus `ip` / `gateway` / `subnet` / `dns` fields, and they persist — but they are **not applied to the WiFi STA interface**. `wifiStaInit(ssid, password)` takes only credentials; the STA always runs DHCP (there is no `esp_netif_dhcpc_stop` + `esp_netif_set_ip_info` on `staNetif_` — that pattern exists only for the AP). So selecting Static and entering an IP currently does nothing: the device keeps its DHCP lease. The fields are display-only scaffolding ahead of the functionality. - -Implementing it needs to answer three UX/safety questions (these *are* the spec): - -- **When is it applied?** NOT per-keystroke — editing the fields must only update the stored values, never reconfigure the live interface mid-entry (a valid `ip` with a still-zero `gateway` would otherwise be applied and break routing). Apply on an explicit commit — safest is **on next connect / reboot**, not a live switch, because changing the STA IP drops the very connection the browser UI is talking to. -- **Validation before apply.** Require all of ip/gateway/subnet present and self-consistent; reject `0.0.0.0` gateway/ip. If invalid, stay on DHCP rather than half-apply. -- **Warn before a live change.** If applied live (not reboot-deferred), the UI must confirm ("about to change this device's IP to X — you'll need to reconnect at the new address") and surface the new URL, since the current socket dies the instant the IP changes. - -Platform work: extend `wifiStaInit` (or add `wifiStaSetStatic`) to take optional ip/gateway/subnet/dns and call `esp_netif_dhcpc_stop` + `esp_netif_set_ip_info` on `staNetif_` when addressing is Static and the config validates. Needs careful hardware testing — a wrong static config locks the device off-network (recovery is the AP-fallback path or a flash erase). Until landed, consider hiding the Static option so it doesn't read as functional. - -### Memory ceiling on non-PSRAM ESP32 with eth-wifi (backlog) - -On `esp32-eth-wifi`, default 128×128 grid, free heap at boot is ~28 KB — not enough for `esp_wifi_init` (needs ~16 KB RX buffers) after the light pipeline allocates ~210 KB. The device stays running but WiFi init fails silently. - -Fix options in increasing scope: -- **Cap the default grid** — drop to 64×64 on `esp32-eth-wifi` (Layer ~32 KB + LUT ~16 KB = 48 KB, comfortably under). Simplest. -- **PSRAM for Layer buffer + LUT** — ESP32-Gateway has 4 MB PSRAM unused on non-S3 builds. Moving the 49 KB pixel buffer + 64 KB LUT out of DRAM frees ~110 KB for radios. Cost: ~25% FPS hit (PSRAM bandwidth ~12 MB/s vs DRAM ~80 MB/s); needs measurement. See [lessons.md](../../work/past/lessons.md) "Adaptive memory allocation design" for the allocation rules. -- **Lazy WiFi init** — skip `esp_wifi_init` when `ssid_` is empty and no AP-fallback is pending. Helps only when credentials exist but the network is unreachable — niche. - -### Boot-time buffer degradation on non-PSRAM at 128×128 (investigation) - -On the Olimex (no-PSRAM) at 128×128 with a modifier, the Layer sometimes comes up **degraded** at boot — status `"buffer reduced — not enough memory"`, with a visibly wrong render (the reduced render buffer overflows what the LUT/extrude expects). **Toggling any layout control (forcing a fresh `onBuildState`) fixes it** — the rebuild allocates the full buffer and the display is correct. So this is a boot-time allocation *race*, not a code bug: the same rebuild path that fails at boot succeeds moments later. - -Measured: the full pipeline needs two ~49 KB contiguous buffers (Layer render buffer + Drivers output buffer, both 128×128×3), plus the LUT. At boot the largest contiguous block is only ~14–20 KB while the network stack / mDNS / HTTP-server buffers are still settling into the heap — so the Layer can't claim a contiguous 49 KB and degrades (halves its dimensions). A rebuild after the heap settles wins the contiguous block and allocates full. The device is at the fragmentation edge either way (~42 KB free / ~14 KB largest block at 128×128 with the full pipeline up). - -The annoyance is purely that the device boots degraded and needs a poke to recover — it should come up working. Fix options in increasing scope: -- **Allocation order** — claim the big Layer/Drivers buffers *before* the network/mDNS/HTTP buffers fragment DRAM (i.e. wire the light pipeline's `onBuildState` ahead of network bring-up in `main.cpp`). Cheapest if the ordering is safe. -- **Boot retry** — if `onBuildState` degrades, schedule one more `buildState()` after boot settles (a one-shot, e.g. after the first `loop1s` or once the network reports up). Self-healing without reordering init. -- **Cap the default grid** on no-PSRAM to a size whose two buffers fit the post-boot largest block (same lever as the eth-wifi memory ceiling above). -- **PSRAM for the buffers** on PSRAM-equipped variants — sidesteps DRAM fragmentation entirely (related: the Async ArtNet and [Memory ceiling](#memory-ceiling-on-non-psram-esp32-with-eth-wifi-backlog) PSRAM notes). - -Related: this is the render/output-buffer face of the same non-PSRAM fragmentation cliff the paged `MappingLUT` already addressed for the *LUT*. The buffers themselves still allocate as single contiguous blocks. - -## Advanced network settings (2026-10-03) - -Left out of the plan that makes Ethernet, WiFi and the access point Network submodules, and expected to be asked for: - -- **Roaming** between access points of one network while connected, by signal threshold (802.11k/v/r where the chip supports it). -- **Enterprise WiFi**: WPA2/WPA3-Enterprise with a username, a password or a certificate. -- **Pinning a network to one access point** (BSSID) or one band. -- **WiFi power save**: the modem-sleep mode, traded against latency for Art-Net and the UI. -- **The country code**, set by hand where adopting the router's country is not enough. -- **The access point's address**, fixed at 4.3.2.1. -- **IPv6 beyond link-local**: global addresses and a static one; the station and Ethernet carry a link-local address, which mDNS advertises. - -## Architecture - -### A second I2C bus, once a board wires one (2026-10-06) - -`I2cBusModule` drives one bus on controller 0, which covers every catalog board: one bus carries about 112 addresses, so a codec, a sensor and a display share it. -A board needs a second bus for two chips on the same fixed address, devices at different voltages or pull-ups, or a long cable run kept off the onboard devices. -The shape then: the module becomes addable with a controller-number control, the platform keeps one bus per controller, and a device on a bus (`AudioService`'s codec first) gains a bus selector defaulting to 0, so the catalog's boards keep working unchanged. -The trigger is a real board; a touch controller or camera on its own pins next to the codec is the likely first, the Guition M3's touch wiring being unchecked. - -### An offline test run takes 16 minutes instead of one (2026-10-03) - -With the host's network down, `test_desktop` ran 16 minutes and the scenarios stalled past the gate's hour; online, no test takes over 4 seconds, so something waits out network timeouts. -Next time the machine is offline, `build/macos/test/mm_tests --duration=true` names the test, which then gets a seam like `setTestJoinFails`. - -### `applyControlValue` should say whether the value changed (2026-10-03) - -The boot reapply in `FilesystemModule::reapplyNode` detects a changed value by serializing each control before and after the apply and comparing the bytes, a list control included. -`applyControlValue` reports only whether the value parsed and applied. The proposed shape is the apply also reporting whether storage moved, an `ApplyResult::Unchanged` or a flag, so `Scheduler::setControl` can skip the reaction to a write that changes nothing. -Cold path today, so a tidy-up rather than a cost. - -### Group src/core into folders, the way src/light already is (2026-09-17) - -`src/light` keeps nothing at its root: every header sits in `drivers`, `effects`, `layers`, `layouts`, `modifiers` or `moonlive`. `src/core` is the opposite, 61 files flat, with `moonlive` the one folder that exists. The documentation pages already name the grouping, so the folders would only make the tree say what the docs say: `system.md` fronts 13 modules, `services.md` 7, `supporting.md` 4, and `control.md` one. - -**The proposed shape.** `system/` takes the 13 modules on system.md plus `HttpServerModule`, which is a real module with its own `.cpp` and no page today. `supporting/` takes `MoonModule`, `Scheduler`, `Control` and `FilesystemModule`. `services/` takes the 7 service modules. That leaves 36 shared utilities, which want a `util/` of their own so the root ends up empty of headers like the light domain's: the packet and frame types, the maths and noise helpers, `JsonSink`, `JsonUtil`, `ModuleFactory`, `ScratchBuffer`, `Sort` and the rest. - -**What it costs.** 172 include lines across 96 files, 52 in `src` and 44 in `test`, plus the explicit `.cpp` list in CMakeLists and a handful of scripts and architecture pages that name `src/core/` paths. Mechanical, and a compiler error names every line that was missed, but it touches nearly every file in the tree. - -**Why it waits.** A pure rename of this size is unreadable as part of anything else, and it wants its own branch so the diff is one thing. It is also the kind of change that should follow the documentation sweep rather than ride inside it, since the sweep is what settled which page owns which header. The light domain has its own version of this: a `src/light/supporting` for the headers its own supporting page fronts. - -### Filesystem-change notification (live preset refresh) — undesigned - -ControlModule rebuilds its preset list by rescanning `/.config/presets`, and that rescan runs at startup and after every save, rename, delete and reorder. So a preset file **uploaded or deleted through the File Manager** appears only once the module next rescans (a reboot, or any preset action on the surface), not the instant the file lands. Documented as the actual behaviour in [the Control card](../../reference/presets.md). - -The fix is a **core-neutral filesystem-change notification**: FileManagerModule (or the `platform::fs*` write paths) signals "this path changed", and a module with a folder it cares about re-reads. Deliberately not built yet — it is a new core seam serving one caller today, which is the shape [architecture.md § Core primitives, not one-offs](../../explanation/architecture/index.md#core-and-light-domain) warns about. **Build trigger**: a second consumer appears (a scripted-effect folder for MoonLive is the likely one, since live scripts uploaded as files have exactly the same staleness), or the manual-refresh step proves annoying in real use. - -Whatever the design, it stays domain-neutral (a path + a change kind, no preset/light vocabulary in core) and off the hot path — the notification marks a flag, the rescan happens on the owning module's next tick, never inside the writer. (CodeRabbit flagged the staleness; deferred here rather than growing the seam for one caller.) - -### WiFi runtime disable — open design question (undesigned) - -Today the eth-only build profile compiles WiFi out (`MM_NO_WIFI`). Turning WiFi off *at runtime* instead is undesigned: whether the gate should key off detected hardware presence, an explicit control, or a deviceModel-catalog field isn't decided. The eth-only build covers the need until a concrete case forces the choice. (Moved from architecture.md § What we leave undesigned; it's a deferred design decision, not a settled 🚧 one.) - -### Consolidate the two module-by-name tree-walkers (backlog) - -`HttpServerModule::findModuleByName` (`findInTree` recursion) and `Scheduler::firstByName` (`firstInTree`) are two implementations of the same "find the first module in tree-walk order with this name" operation. The duplication predates the `setControl`-to-Scheduler extraction, but that extraction made `Scheduler::firstByName` public *and* added `Scheduler::instance()`, so HttpServer no longer needs its own copy: its ~9 remaining call sites (identify, addModule, removeModule, clearChildren, the WLED/system shims) can call `scheduler_->firstByName()` and the private `findModuleByName`/`findInTree` pair deletes. Purely a *No duplication* cleanup — no behaviour change — worth doing so the walk order/semantics live in exactly one place. (Reviewer note, IrService/setControl branch.) - - -### Pin-uniqueness check across modules (prevents conflicts; replaces a singleton hack) - -**Problem it solves.** Two modules must not drive the same physical GPIO. Today nothing stops it: add two `RmtLedDriver`s with `pins="18"`, or two `AudioService`s with the same `wsPin/sdPin/sckPin`, and they fight over the pin — at best garbage output, at worst (for I2S) endless `i2s_new_channel` driver-error spam every tick. This surfaced when a repeated catalog inject stacked duplicate AudioModules and the device spammed I2S failures (a clean install is fine; the duplicates were the artifact). - -**Why pin-uniqueness, not a per-type singleton.** The first instinct was "make AudioService single-instance" — but that's a crude proxy. The *real* invariant is pin non-overlap: a board legitimately can have **two LED drivers on different GPIOs** (multi-output rigs do exactly this), or even two mics on distinct pin sets. "One mic" isn't fundamentally true; "no two modules on the same pin" is. So check pin conflicts, which both prevents the breakage **and** allows legitimate multi-instance setups. (A per-type singleInstance flag was prototyped and rejected in favour of this.) - -**The clean mechanism — reuse `ControlType::Pin`.** Pins are already their own control type (the `addPin` work). So the check is domain-neutral and needs no per-module declaration: enumerate every `Pin`-typed control's value across the whole tree; a value of `-1` is "unused" (ignored); any other value appearing on two controls is a conflict. Handle the list case: `RmtLedDriver.pins` is a comma list (`"18,19,20"`), so the enumerator expands list-of-pins controls too. - -**Where it runs.** Two sites, because a pin can be introduced at add *or* edit: -- `POST /api/modules` (add): if the new module's catalog/default pins collide, reject. -- `POST /api/control` (pin write): if setting a `Pin` control to a value already used elsewhere, reject (or soft-flag — see below). - -**Open decision (UX).** Conflict on add → reject with a clear message (`"GPIO 18 already used by RmtLed"`). Conflict on a live pin edit → reject is safest but blocks mid-reassignment (you can't swap two drivers' pins without a free intermediate); a **soft-flag** (accept, set a status warning) is friendlier for live editing. Leaning: reject on add, soft-flag on live edit. Product-owner call. - -**Hardware-limit tail (not covered by the pin check).** Pin-uniqueness rejects the common case but not the controller-count limit: the S3 has **2 I2S controllers** regardless of pins, so a 3rd mic on distinct pins passes the pin check yet fails `i2s_new_channel` at runtime. That tail is already handled — the platform I2S init returns false on failure (no panic, module stays `inited_=false`); verified live (4 pinned AudioModules → error spam, no crash). So scope = pin-uniqueness check + the existing graceful-degrade; don't try to make the pin check also model controller counts. - -**Related:** the shipped "disabling releases resources" work (see [past plans](../past/index.md)) — a disabled module freeing its pins is what lets the same GPIO be reassigned live without a conflict-reject. - -### PinsModule — strict reject-on-add mode (the one remaining increment) - -[PinsModule](../../moonmodules/core/system.md) is shipped: the read-only ownership map, reserved/strap severity grading, the conflict **soft-flag** (a GPIO claimed by two controls shows both owners, flagged red, never rejected), live-state dir/level/drive, and the disable-frees-pins cascade all landed — see [pins-analysis-top-down.md](pins-analysis-top-down.md) for the shipped design record. The soft-flag choice obviated the reassignment broker (a live pin swap already works: set A→B's pin, then B→A's, the transient conflict clears). - -The one still-open item is an **optional strict reject-on-add mode** for the installer/catalog path: a "clean tree" that *refuses* a module add whose pins collide, rather than accepting it soft-flagged. This is add-path *validation* (a different feature than the live soft-flag reassignment), wanted only if a stricter installer UX is desired. Spec + `/plan` if picked; the soft-flag default stays for live editing. - -### Runtime board presets (multi-commit, partially landed) - -The firmware-vs-board separation is now in place across the codebase (see [MoonInstaller, firmware vs deviceModel vs board](../../explanation/architecture/mooninstaller.md#the-three-words)). `build_esp32.py --firmware ` picks the compiled binary; MoonDeck deduces the physical board where the firmware uniquely identifies hardware (`esp32-eth*` ⇒ `olimex-esp32-gateway-rev-g`) and lets the user pick from a short hardcoded list otherwise. Firmware variants stay separate — `esp32-eth` saves ~670 KB flash + ~30 KB DRAM vs the default `esp32` (WiFi+Ethernet, measured); merging would erase that win. - -What still needs separation: the eth variants hardcode Olimex Gateway RMII pins in `src/platform/esp32/platform_esp32.cpp::ethInit()`, so they only work on that one PCB. As we add boards with different pins (LOLIN D32 tested 2026-06-02, QuinLED variants planned), runtime pin configuration becomes the next step. - -Pin config moves to runtime (next, separate commit): -- Drop hardcoded `GPIO_NUM_17` from `ethInit()`. NetworkModule reads `Network.eth_rmii_clock_gpio` (new control) and similar pin values, defaulting to current Olimex hardcodes so behaviour is unchanged. -- Same for any other hardware-pin literal in the firmware. - -Board preset catalog + upload (later, when the runtime config has real consumers): -- Add structured per-board files (location TBD — not `docs/` since they're config not docs; `boards/` at repo root is the strong candidate, matches the PlatformIO convention contributors will recognise). -- Each file declares chip, flash, PSRAM, Ethernet PHY + pins, default module config. -- New `/api/board-preset` endpoint accepts the JSON; device persists to LittleFS; bootstrap applies pins + defaults on next boot. -- MoonDeck "Set board" picker reads the catalog to populate the dropdown. -- Pin reassignment requires reboot (ESP-IDF can't hot-reconfigure EMAC pins after `esp_eth_driver_install`); document the constraint. -- A first attempt at this catalog landed and was rolled back during the firmware-vs-board separation work — the catalog only earns its keep once the device reads it, otherwise it's a docs-shaped file in the wrong place. - -**Prior art: the predecessor's per-board pin database** ([ModuleIO.h](https://github.com/ewowi/MoonLight/blob/main/src/MoonBase/Modules/ModuleIO.h)). The predecessor MoonLight (our own earlier project) already models exactly this for ~25 boards across ESP32-D0 / S3 / P4: a `pins[]` array of `{GPIO, usage, index}` plus board-level `maxPower`, `ethernetType`, `ethPhyAddr`, `ethClkMode`. Don't copy the file or paste its tables here: read it when building the catalog and write our own. Its `usage` enum enumerates the hardware functionalities a board preset here *could* drive once the device-side consumers exist (each needs its own module/control before the corresponding `deviceModels.json` / catalog field earns its keep: none exist today beyond `System.deviceModel` + `Network.txPowerSetting`): - -- **LED output pins** — per-strip data GPIOs (1–16 outputs/board); the first real consumer (a Driver pin control) unblocks multi-output boards (QuinLED Dig-Quad/Octa, SE16, LightCrafter). **This consumer now exists** (the `pins` control on every LED driver; e.g. QuinLED Dig-Quad ships `"pins": "16,3,1,4"` in `deviceModels.json`), so the field earns its keep — but only up to **8 lanes** today (`kMaxLanes = 8` / `kMaxPins = 8`). The parallel drivers are moving to **16 lanes (choose 1..16)**; when they do, this becomes a real gap with two halves: - - **Per-model usable-GPIO map (the data).** Identify **which up to 16 GPIOs each device model actually exposes for LED output** — not the chip's full pin count, but the pins broken out to a usable header/connector AND safe to drive (exclude strapping, flash/PSRAM, input-only, and pins already owned by eth-RMII / I²C / the onboard LED). The codebase knows the chip *ceiling* (`MM_MAX_GPIO` from `CONFIG_SOC_GPIO_PIN_COUNT`, [Control.h:13](../../src/core/module/Control.h)) and the live *ownership/reserved* grading ([PinsModule](../../moonmodules/core/system.md)), but NOT the per-board *exposed-and-safe* set — that is board knowledge (schematic/pinout per model). The authoritative source is the **annotated pinout image per model under `docs/assets/deviceModels/`** (e.g. [`esp32-s3-n16r8-dev.png`](../../assets/deviceModels/esp32-s3-n16r8-dev.png)) (and MoonLight's `ModuleIO.h` `pins[]` as prior art — read it, write our own against `deviceModels.json`). **Worked example — ESP32-S3-N16R8 dev board** (from `esp32-s3-n16r8-dev.png`): the 16 safe LED-output GPIOs are **`4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,21`**, derived by excluding — octal flash+PSRAM (the R8/N16 part reserves `26–37`, the `SPIIO4-7/SPIDQS/SPICLK` pins), USB (`19,20`), UART0 console (`43,44`), the onboard RGB LED (`38`), and the strapping pins (`0,3,45,46`). That leaves exactly 16 clean I/O — enough for the full 16-lane target. Do the same read per model. - - **Encode as per-model defaults + coverage (the catalog).** The 16 usable lane GPIOs become the model's default `pins` string, so a fresh flash of e.g. "Serg UniShield V5" comes up with the *right* lane pins pre-filled, not blank/generic. Coverage today is uneven: of 25 models, **7 carry no LED-pin config** (Olimex Gateway, LOLIN D32, Generic ESP32 Dev, ESP32-S3 N16R8 Dev, LightCrafter 16, SE 16 V1, and any bare dev board) — those default to nothing and force the user to guess. Fill every model's LED-capable pin default, and **document the per-model map** (the annotated-pin images the § below already reserves are the natural home). **The per-board usable set is wider than the exposed header:** a pin the board commits to a peripheral it *doesn't mount* is fair game (the Olimex Gateway leaves 6 clean LED pins only if you count the **unmounted micro-SD** pins 4/13/14 — see [gpio-usage § Usable LED-output GPIOs](../../reference/hardware/gpio-usage.md) and the bench note in memory), so the per-model map must record *which non-exposed/repurposable pins are safe on this board*, not just the header breakout. Scope: **16 pins max** — do not over-generalize past the peripheral lane ceilings ([measured lane ceilings](../../reference/performance.md#multi-pin-led-driving-all-three-peripherals-128128-grid): Parlio 65535 bytes/lane single-shot (897 RGB lights at 8 lanes, ~448 at 16), LCD **8 or 16 lanes** on S3 (the widening shipped), RMT up to 8 TX channels). **Note the i80 gotcha:** an `LcdLedDriver` model should pick `clockPin`/`dcPin` clear of its data lanes — an overlap is a *warning*, not a blocker (the driver still runs; that one lane just carries the clock/DC waveform, which is fine for an unused parked lane but garbles an active strand), so a catalog default that overlaps an *active* lane would ship a subtly-broken board. -- **Ethernet PHY config** — LAN8720/RMII (MDC/MDIO/CLK/power-pin/PHY-addr/clock-mode) vs W5500/SPI (MISO/MOSI/SCK/CS/IRQ); the consumer is the runtime `Network.eth_*` controls listed above, replacing the hardcoded Olimex pins. -- **Power budget** — `maxPower` (Watts) per board, for a future current-limit / brightness-cap control. -- **Audio / I2S** — SD/WS/SCK/MCLK pins, the input side of audio-reactive effects (Pi-5 sensor note is the desktop counterpart). -- **Buttons & inputs** — push/toggle/lights-on, PIR, digital-input; needs an input-event concept the firmware doesn't have yet. -- **Relays & power control** — relay / lights-on / high-low pins. -- **Infrared** — IR receive pin (remote control). -- **RS485 / DMX** — TX/RX/DE pins (wired DMX-512 output beyond the current Art-Net path). The driver that consumes this is the [RS-485 / DMX-512 wired output item](backlog-light.md#rs-485-dmx-512-wired-output-future-the-physical-dmx-driver) in the light backlog. -- **Sensing** — voltage / current / battery / temperature ADC pins. -- **Onboard LED / key, exposed / reserved pins** — board-housekeeping and conflict-avoidance metadata. - -Sequencing rule (unchanged): each functionality lands a device-side control first, then its preset field; the catalog grows one earned consumer at a time, never as a speculative pin dump. - -**Module variant + PSRAM within the classic-ESP32 family.** `getChipDescription()` and MoonLight's `ModuleIO.h` both report only the *core* family ("ESP32"), not the *module* (WROOM / WROVER / PICO) — so neither distinguishes whether a classic-ESP32 board has PSRAM. This matters for MoonLight (whose large-LED story leans on PSRAM) in a way it doesn't for MoonLight: e.g. the **QuinLED Dig-Next-2 is built on an ESP32-PICO with 2 MB PSRAM**, but MoonLight's `esp32` build has no `CONFIG_SPIRAM` (see the `#ifdef CONFIG_SPIRAM` gate in `platform_esp32.cpp::psramAlloc`), so it flashes and runs as a no-PSRAM device and hits the non-PSRAM fragmentation ceiling at large grids that the 2 MB would otherwise relieve. A PSRAM-enabled classic-ESP32 firmware variant (e.g. `esp32-psram`) would unlock it; `deviceModels.json` could then carry a `psram` hint per board to steer the picker — but only once that variant exists (no consumer today). `deviceModels.json` currently maps every classic board to the WiFi-only `esp32` variant, which is correct-but-unoptimised for PSRAM-bearing PICO boards. - -### Per-layout coordinate offset for independent placement (backlog) - -`Layouts` stitches multiple child layouts into one physical light space, but only their *indices* are stitched (offset sequentially in `placeLights`) — their *coordinates* are not translated. Two layouts therefore overlap in the same coordinate box: two 64×64 grids both occupy x,y ∈ 0..63, so the Layer's dense bounding-box buffer is 64×64 (4096 voxels) even though the container reports 8192 lights, and the second layout's lights land on the first's positions. `scenario_Layouts_mutation` documents this (its steps assert pipeline liveness, not buffer-size arithmetic). - -When picked up: add `offsetX/Y/Z` (lengthType) controls to `LayoutBase`; `Layouts::placeLights` translates each child's emitted coords by its offset so layouts occupy disjoint regions of the physical extent (a 64-wide grid at offsetX=64 sits beside another at offsetX=0 → a 128×64 combined extent). `Layer::onBuildState` already derives physical dims from the max emitted coordinate, so it would pick up the wider extent automatically. Until then, "multiple layouts" means "multiple layouts sharing a coordinate box", which is only useful when they genuinely overlap (e.g. a sphere inscribed in a grid). - -### Improv as a child of NetworkModule (deferred — needs scheduler work first) - -Architecturally the right shape; attempted in plan-21, reverted. Blocker: `Scheduler::tick()` only walks top-level modules for `loop20ms`/`loop1s` — children silently miss those callbacks. See [lessons.md](../../work/past/lessons.md) "Trying to add a child module to NetworkModule". - -Minimum-scope fix before the move: -1. `MoonModule::loop20ms`/`loop1s` propagate to children (or Scheduler walks them) — pick whichever costs less at runtime. -2. Audit every existing override of `setup`/`loop1s`/`loop20ms`/`onBuildControls`/`teardown` in NetworkModule, SystemModule, FilesystemModule, HttpServerModule to confirm base-chaining. -3. Then the actual move is a one-line `networkModule->addChild(improvModule)` swap. Estimate ~2 h total. - -### Platform API: `std::span` migration (backlog) - -Several `platform.h` APIs still use `(buf, len)` pairs where `std::span` would catch length/pointer mismatches at compile time. Concrete sites: `http_fetch_to_ota`, `improvProvisioningInit`, and friends. ~2 h including ripple updates to callers. Do alongside the next platform-API expansion (Windows socket port or POST /api/firmware streaming). - -### Improv-as-REST follow-ups - -Device-model injection over Improv shipped as **"Improv = REST over serial"** (the `APPLY_OP` vendor RPC pushes the whole `deviceModels.json` entry over serial during install; the device runs the same apply-core the HTTP REST API does, on WiFi *and* eth-only firmware). That subsumed the earlier multi-step "board injection + Improv as a general data injector" plan — the general injector *is* APPLY_OP. What remains: - -**Open follow-up: closed-loop APPLY_OP pacing (read-back ack + retry).** The installer paces APPLY_OP frames open-loop (`sendApplyOpFrame` waits a fixed ~120 ms between ops) rather than reading the device's ack back, because a Web Serial duplex read while the writer lock is held is awkward. The delay covers the worst-case single-buffer consume window with headroom, and each op is idempotent (a lost op re-applies cleanly on a re-flash), so this is robust today. The closed-loop upgrade — read the RPC response, retry once on error `0x82` (buffer busy) — removes the fixed delay (faster install) and makes op-loss impossible rather than improbable. Worth doing if a real install is ever observed dropping an op, or when the config push grows large enough that the cumulative fixed delay is noticeable. Touches only `install-orchestrator.js`. - -**Open follow-up: shared JS helpers across device-UI and mooninstaller.** `safeLocalGet` / `safeLocalSet` (3-line hostile-storage guards) are duplicated in `src/ui/install-picker.js` (device firmware, embedded as a C string via `embed_ui.cmake`) and `mooninstaller/devices.js` (web installer page, served from Pages). The two live in different build contexts so the shared extract isn't trivial — it'd need a new `src/ui/safe-storage.js` plus updates to: `embed_ui.cmake` (embed the new file), `ui_embedded.h` generator (new C array), HTTP server file routing (new path served), `release.yml` workflow staging, `preview_installer.py` staging. Five files for one 3-line helper is too much pre-merge. Worth doing when the next shared helper arrives — `relativeTime` and `formatBytes` are candidates. Two helpers earn the build-glue cost; one doesn't. - -**Open follow-up: P4 Improv scan on a cold WiFi link (bench check).** `improvHandleScan` in `src/platform/esp32/platform_esp32_improv.cpp` calls `esp_wifi_scan_start`, which needs the WiFi driver started. On native ESP32/S3 the driver is up by the time a user provisions; on the P4 the radio lives on the C6 and comes up only after the esp_hosted prelude in `ensureWifiInit()` (triggered by `wifiApInit` / `wifiStaInit`). A scan requested on a P4 that has not initialised WiFi returns an error cleanly rather than scanning a cold link, so nothing crashes. The check: bench-verify whether a P4 provisioned from cold needs the link brought up first, and if so route the scan through the public `wifiAp`/`wifiSta` path. - -### Live scripting — author effects/layouts/modifiers/drivers/sensor logic on-device (multi-commit, design phase) - -Run user-authored scripts on a running device — a scripted effect, layout, modifier, driver, or core sensor rule, pushed as text and live on the next tick with no reflash/reboot — the leap WLED took with ARTI-FX and the heart of the PixelBlaze product. A scripted module **is** a MoonModule (controls, `loop()`, role, generic UI). The engine lives in core (domain-neutral: also "transform sensor data") and serves the light domain specifically. Targets in order: ESP32 classic + S3 first, then P4/other ESP32, then Teensy, then desktop. Must be blazingly fast (runs in the render hot path at 16K+ lights × 50 FPS), memory-smart (IRAM/PSRAM via `platform::alloc`, compile-once), and synced (Scheduler tick, tick-atomic hot-swap, live reconfig). - -The **bottom-up landscape survey** is done — [livescripts-analysis-bottom-up.md](livescripts-analysis-bottom-up.md): deep-reads the [ESPLiveScript fork](https://github.com/ewowi/ESPLiveScript/tree/fix-warnings) (a from-scratch C-like JIT that emits **native Xtensa** machine code — blazingly fast but **Xtensa-only**, so it covers classic+S3 and *not* P4/Teensy/desktop), surveys the field (PixelBlaze bytecode VM + web editor, WLED ARTI-FX AST-walking interpreter, embedded VMs / WASM / lightweight multi-ISA JITs), and extracts the load-bearing decisions (execution strategy, the IR seam ESPLiveScript lacks, the MoonModule binding, the per-pixel contract, memory placement, sync, sandboxing). Its thesis to validate: a **portable bytecode-VM baseline that runs on every target on day one + an optional native back-end for the hot ISAs behind a shared IR**. **Next: the top-down redesign** — the prompt that generates `livescripts-analysis-top-down.md` is at the bottom of the bottom-up doc; it produces the reference architecture + staged spike plan. Implementation is multi-commit, spike-ordered, after the top-down lands. Credits: [friend-repos/hpwit-ESPLiveScript.md](../../friend-repos/hpwit-ESPLiveScript.md). - -### HTTP file serving blocks the render tick (backlog) - -`HttpServerModule::handleConnection()` serves large embedded files (`app.js`, `style.css`) with the blocking `TcpConnection::write` — a page load can briefly stall `loop20ms`. One-shot per load (lower priority than the per-tick preview issue, which is fixed). Fix: serve large HTTP responses through a resumable per-client cursor drained on tick20ms (the shape the preview and full-state sends use). - -### Generic control + state topics over MQTT — the automation escape hatch (backlog) - -The MQTT bridge ships a **semantic** topic surface (`/on/set`, `brightness/set`, `hsv/set` → the `Drivers` controls) sized for Homebridge/HomeKit, which need fixed, typed topics per accessory. That leaves everything else the web UI can reach — effect choice, layout, modifier params, palette-by-index — unreachable from a home hub. The ask that surfaced this: *"expose the whole REST API over MQTT."* - -**It's compatible with Homebridge/HA, because it's a second topic namespace, not a replacement.** Homebridge only ever subscribes to the semantic topics it's configured for; a generic topic family sits beside them and a broker carries both. The two surfaces already share one apply-core — `Scheduler::setControl(module, control, json)`, the same seam `/api/control` and IR funnel through — so a generic MQTT surface is that core under a different topic shape, honouring the module's own "MQTT is a transport, not new control logic" contract. - -But "the whole REST API" over-scopes: of the routes (`/api/control`, `/api/state`, `/api/system`, `/api/modules[/…]`, `/api/dir`, `/api/file`, `/api/firmware/*`, `/api/reboot`, `/api/types`, the `/json/*` shim, `/ws`), only two are genuinely pub/sub-shaped. The rest are request/response or bulk transfer and belong on REST: - -- **`/api/control/set`** ← `{"module","control","value"}` (the exact JSON `/api/control` takes) → `applySetControl`. The real win: any control on any module — the effect/layout/modifier/palette reach HomeKit can't express. The apply-core already validates (range / read-only / module-not-found via `SetControlResult`), so safety is free. -- **`/api/state/get`** → publishes the `/api/state` JSON (retained). For dashboards (fps / heap / tick / current effect across a fleet), offline detection, and alerting — the monitoring half. - -Explicitly **out** (no practical MQTT case, and wrong for the transport): -- **`/api/file`, `/api/dir`, `/api/firmware/upload`** — kilobytes-to-megabytes of binary; MQTT is a small-message bus, REST already streams these correctly. -- **`/api/modules` add/delete/move/replace** — stateful, order-sensitive pipeline surgery a human does once in the UI; an automation reshaping the pipeline is an anti-pattern (the *Robust to any input* guarantee makes it *safe*, not *advisable*). -- **`/api/reboot`** — one narrow action; a single fleet-reboot topic is a mild nice-to-have, not "the API." -- **`/api/types`, `/json/*`, `/`, `/ws`, `/api/firmware/url`** — discovery metadata, the WLED shim, the UI itself, the socket: static, already-served-elsewhere, or a different protocol. - -**Shape:** ~15 lines routing into the existing `applySetControl` + a state publish; gate behind a `generic API` bool defaulting **off**, so a home user's broker isn't a remote-config backdoor and the curated HomeKit surface stays the clean default. **Don't** auto-generate a semantic topic per control — HomeKit/HA need stable typed topics; a generic `palette/set` whose meaning shifts per firmware breaks their discovery. Keep semantic topics hand-curated; let the generic pair be the escape hatch. - -**HA MQTT Discovery — SHIPPED.** The device announces a retained JSON-schema light config to -`homeassistant/light//config` (the Tasmota/ESPHome/Zigbee2MQTT pattern), so HA auto-creates a -wired entity — gated on the `haDiscovery` control, with a Last-Will availability topic. This covered -the "HA can't reach the device over MQTT" incidents. The *generic-topics escape hatch* above (the -whole REST API over MQTT, for power-user scripting) is the remaining unbuilt piece; keep it coherent -with the [DevicesModule command half](#devicesmodule-interop-plugins-the-command-half-discovery-shipped) -(Tasmota-MQTT / zigbee2mqtt as *outbound* control — the mirror of this *inbound* surface) and the -[LightsControl integration point](backlog-mixed.md). - -**Open question — Homebridge example maps both `setBrightness` and `setHSV`.** In `homebridge-mqttthing`'s `lightbulb`, HSV's *value* (V) already carries HomeKit's Brightness characteristic, so mapping both topic pairs may double-drive brightness (mqttthing's docs lean toward using one or the other). The [MQTT § Homebridge example](../../moonmodules/core/system.md#mqtt) currently lists both, to show the device's full topic surface. Resolve on hardware: flash a board, run Homebridge + mqttthing with that config, and check whether the Home-app brightness slider misbehaves — if it does, drop the two `brightness` topics from the example; if not, it's a non-issue. (Flagged by CodeRabbit; parked here rather than changing the doc on an untested hunch.) - -### HA update entity via MQTT discovery — release check (open follow-up) - -**Discovery config + install command wiring: shipped.** [`MqttModule`](../../src/core/system/MqttModule.cpp) now publishes a second HA-discovery component at `homeassistant/update/MoonLight_/config` (same haDiscovery gate, same MAC-stable id, same device card as the light), state on `/update/state`, and subscribes to `/update/set`. HA renders a *"Firmware: "* card in the diagnostic section of the device panel. An install command routes to `platform::http_fetch_to_ota` with the release-artifact URL built from the payload version + `kFirmwareName` (`https://github.com/MoonModules/MoonLight/releases/download/v/firmware--v.bin`), reusing the same OTA task the `/api/firmware/url` route drives. - -**Still to build: device-side release check.** `installed_version` and `latest_version` are equal today (both `MM_VERSION`), so HA renders the entity as up-to-date and the Install button is disabled. What's missing is a periodic poll of `https://api.github.com/repos/MoonModules/MoonLight/releases/latest`: at boot (~30 s post-network-up) and every 24 h thereafter, fetch the release JSON, extract `tag_name`, and if newer than `MM_VERSION` call `MqttModule::publishUpdateState()` with the updated `latest_version` (the publish path is already there: only the caller is missing). ~2 KB per check. - -Two blockers, both platform-layer: -- **`platform::http_fetch_to_ota` is OTA-shaped** — it writes bytes into the next OTA partition; there's no general "HTTPS GET into a buffer" seam. A small `platform::http_fetch_to_buffer(url, buf, cap, statusBuf, size_out)` next to it — ~30 lines wrapping `esp_http_client` on ESP32, a libcurl / URLSession stub on desktop — is the right home for this. -- **JSON parsing on-device** — the existing `mm::json` scalar helpers are flat and key-order-independent; a `releases/latest` payload uses a nested `assets[]` array which the current helpers don't walk. Either extend them (~30 lines to walk one level of array), or scan for the `tag_name` string directly with `strstr` for this one caller. - -Once both land, add a `ReleaseCheckModule` (or a small extension inside NetworkModule / FirmwareUpdateModule — decide when building) that owns the poll timer + last-seen version cache and calls into `MqttModule::publishUpdateState()`. ~50 additional lines. - -## Testing - -### Additional test coverage (pending) - -### The restructured scenarios lost four things the archive asserted (2026-09-29) - -The 27 scenarios moved to an archive and 11 new ones took their place, one per top-level module. -The archive is now empty. Of the 27, four were the core-folder ones whose questions the new network scenarios ask better (`scenario_Network_hardware_reconfigures_live` and `scenario_Network_identity_and_discovery` between them cover the mDNS toggle, the Home Assistant discovery toggle and the Ethernet reconfigure, with read-backs the old files never had). Of the remaining 23, thirteen were deleted as already covered by unit tests, three more once tests were written for them, and the seven whose subject only exists on hardware live in `test/scenarios/device/` marked `live_only`. -The structure is better and the new set asserts things the old one could not: 29 `expect_control` value checks where the old set had none, 9 `reboot` round-trips proving a setting reached the filesystem, and consent defaults pinned as a shipped privacy guarantee. -Four things did not survive the move, and nothing else in the tree covers them. - -**Every performance contract and bound is gone**, measured rather than estimated: `contract` 40 to 0, `bounds` 20 to 0. -Both mechanisms are still implemented in the runner (`scenario_runner.cpp` enforces a `tick_us` ceiling and a `free_heap` floor, and the `bounds.fps` gate below it). -The new set records `observed` numbers, which is a trend rather than an assertion, so nothing fails on a tick or heap regression. -One document still states otherwise and should be corrected when this is: `docs/reference/performance.md` says each scenario step carries an enforced contract. -The `collect_kpi.py` half is FIXED on 2026-09-29: its anchor named a file that exists nowhere in the repo, and now states the measurement instead. -The `device/` set carries `observed` blocks from a real ESP32-S3 as of 2026-09-29, which is the window a contract would be set from; none is set yet, since one run per step is not a baseline. - -**The light scenarios render nothing.** FIXED on 2026-09-29 for the two that did not: `scenario_Layouts_resize_reallocates_live` and `scenario_Drivers_output_and_brightness` now build the whole pipeline, so the runner's buffer checks fire and they report 5 and 8 checks rather than 2 and 5. -Control-tested by removing the effect, which fails `buffer non-zero after render`. - -What remains is the framework half, already filed under per-step assertions above: the buffer checks run ONCE at the end of a scenario, so a resize can still only assert that the control changed. -Nothing can say the buffer grew to 1024 lights at 32x32 and shrank back, which is the claim the scenario's own name makes. - -**Teardown under a live pipeline is untested.** FIXED on 2026-09-29. `scenario_Effects_teardown_under_a_running_pipeline` removes an audio producer while its consumer still renders, then the orphan, then the last effect, then rebuilds, and it clears the Drivers container to prove the non-editable preview survives a clear that takes the driver beside it. -That last was the only `clear_children` on Drivers anywhere, and the skip rule it pins is control-tested. - -**`HttpServerModule` has no scenario**, though it is a scheduler root on every networked target and the transport the live tier itself runs over. - -**Nothing is marked `live_only` any more.** FIXED on 2026-09-29: eight scenarios carry it. -`scenario_Network_hardware_reconfigures_live` took over what `scenario_NetworkModule_eth_reconfigure` did, cycling `ethType` through None, LAN8720, IP101 and W5500 with a live `ethStop` and `ethInit` where the desktop has only a no-op seam, and adds mDNS and Home Assistant discovery. -The seven in `test/scenarios/device/` are the rest: real peripheral silicon and its DMA budgets, internal-heap fragmentation behind `max_alloc_block`, and float cost on an Xtensa with no FPU. - -Most of the rest moved to a tier that holds it: modifier chains, peripherals and MoonLive are covered at unit level, and the MoonI80 no-freeze guard in particular is pinned by `unit_ParallelLedDriver_doublebuffer`. -What no unit test can reproduce is the cross-board comparative number set the peripheral sweep produced. - -**A measurement taken after a failed allocation is recorded as though the work happened (2026-09-30).** `scenario_Fluid_solver` on a classic ESP32 reads 107 us at 64x64 where an S3 takes 71,441 us, which no float-heavy solver does: the classic is 670x faster only because its grids never allocated. The tell is in the same block, where free heap RISES from 25,760 to 53,728 bytes as the tick collapses, so memory is being handed back while the effect ticks dark. `cube-20-d` reads the same way at 97 us. -Two halves, and neither exists today. `FluidEffect::prepare` refuses a grid it cannot allocate and leaves the effect ticking harmlessly, which is the right behaviour, but it publishes no status, so nothing downstream can tell a refusal from a cheap frame. And `run_live_scenario.py` records whatever tick it measures without asking whether the module producing it is in a state worth measuring. -The fix is a status on the effect (the shape `ParallelLedDriver` already uses for a bus that will not init) plus a runner check that labels or drops the sample. It needs a classic on the bench to verify, since that is the only board small enough to fail the allocation. Until then the affected rows are the classic's `grid-64-h`, `all-jets`, `long-persistence`, `cube-20-h` and `cube-20-d`, and they understate the cost rather than overstating it, so no contract set from them would be too lax. - -### The static set runs on every supported device before the rename (2026-09-29) - -The scenarios are the pre-cutover hardware check: a set that passes in-process proves the logic, and the same set against each board proves the platform layer under it. -That is the point of getting the static set solid first, and it is what the device sweep on cutover day reads from. - -Two of the three things it needed are now in place: the light scenarios render, and eight scenarios are marked `live_only` so a sweep asks a board something a laptop cannot answer. -What remains is a runner that takes the device list rather than one host, so the answer is a matrix across the chips we ship rather than a pass on whichever board was plugged in. -The first S3 run on 2026-09-29 found two defects the set had been hiding: a `channelsPerLight` prop that stopped being a control, and peripheral names renamed out from under the scenarios, where `optional: true` turned every wrong name into a silent skip and still reported a pass. - -`moondeck/moondeck.json` carries 24 registered devices and `run_live_scenario.py` takes a `--host`, so the missing piece is the loop and the per-device report, not the transport. - -### A scenario that runs until something breaks (2026-09-29) - -A static scenario proves a sequence works once. What it cannot find is the fault that needs a thousand random module additions, or a leak that takes an hour: the runner sets up, asserts and exits, so the long tail of lifecycle bugs has no test that reaches it. -A fuzz mode would run until killed on a device left on a bench, doing randomly chosen legal things and stopping the moment one fails. - -The runner already carries most of it: a step vocabulary, module add, remove and replace, control setting, measurement, and per-step failure reporting. -What it needs is a mode that generates steps rather than reading them, and four things that make a failure worth acting on. - -A **seed**, so a run is reproducible: a failure reprints the seed and the step index, and the same seed replays the same sequence. -A **legal-move set** the scenario names rather than the generator guessing, since a random string in a filepath control proves nothing about the pipeline. -**Halt on the first failure**, keeping the device in its broken state for inspection rather than tidying it away. -And a **floor under heap and tick**, because the faults this is for announce themselves as a slow drift rather than an assertion. - -Worth most where the gaps above are: random add and remove under a live pipeline is exactly what the archived `remove_module` steps covered, and a long run on a Raspberry Pi finds the leak a thirty-second scenario cannot. -Build it after the static set is solid, since a fuzz run is only as trustworthy as the assertions it fires. - - -- **Memory degradation cascade** — the output-buffer *allocation* decision (no buffer for a lone identity layer; a buffer for ≥2 layers or any LUT layer) is unit-pinned (`unit_Layers_container` "Drivers allocates the output buffer only when…"), and LUT-vs-identity is pinned by `unit_Layer_sparse_mapping`. What's **not** pinned is the *low-heap* half of [architecture.md § Degradation cascade](../../reference/hardware/firmware-variants.md#degradation-cascade): under heap pressure the LUT + driver buffer are skipped *together* (`lutSkipped()` true, forced 1:1), and below that the layer buffer *reduces dimensions* (halving to a 8×8 floor) rather than failing. The hook exists — `unit_BlendMap` already uses `platform::setTestMaxAllocBlock` to force allocation failure for the paging test — so a test could cap the block size and assert: (1) LUT+output buffer both skip and `lutSkipped()` flips, (2) the layer buffer shrinks to fit and never goes null. Pre-existing gap (predates multi-layer); the *happy-path* allocation contract is covered, only the OOM-degrade branch isn't. -- **Per-step assertions in scenarios (a framework gap, not a scenario gap).** A scenario step can - assert TIMING and HEAP (`bounds`, `contract`) and the run asserts the final buffer, but it cannot - say "after this step the fixture has 24 lights", "this module's status reports a compile error", - or "the rendered output changed". So a migrated script step proves the pipeline still ticks, not - that the edit did what it claims — the MoonLive scenarios walk break-and-recover cycles whose - most interesting states go unasserted. Wants a small vocabulary (`expect_lights`, - `expect_status`, maybe `expect_pixel`) added to **both** runners together: the desktop/live split - is exactly what let `write_file` exist on one and silently no-op on the other. Raised by - CodeRabbit against scenario_MoonLive_pipeline and scenario_MoonLiveEffect_controls; the sites are - listed there, but the vocabulary has to exist first. -- **UI page load time** — scenario step measuring HTTP response time for `/`, `/api/state`, `/api/system` via the live runner. Verifies acceptable load time on ESP32. -- **Module teardown memory** — scenario that tears down all modules and verifies heap returns to pre-setup baseline. Confirms no lifecycle leaks. -- **JavaScript test harness** — `vitest` + `jsdom` for the browser UI: pure helpers in `install-picker.js` (`isCompatible`, `parseFirmwaresFromAssets`, `relativeTime`) **and `app.js`'s conditional-control DOM logic** (`syncVisibleControls` — reconciles which control rows are rendered when a `hidden` flag flips). The C++/backend half of conditional controls IS unit-tested (`conditional_controls.h` + per-module tests pin the binding + `hidden` flag), but the **UI re-render half is not** — `syncVisibleControls` was the source of a real re-render-loop freeze (Network static-IP toggle) caught only on hardware. A `jsdom` test that builds a card, flips a control's `hidden`, runs the reconcile, and asserts the right rows appear/disappear (and that it converges — the unchanged→no-op fast path) would have caught it. **Attempted and reverted (2026-06-17):** stood up vitest + 13 passing tests for the install-picker pure helpers, but the high-value half (`syncVisibleControls`) needs either an `app.js` module seam or extracting its reconcile logic into a separate served `.js` (6 embed/route wiring edits for a firmware-served file). Judged not worth adding a whole Node/npm toolchain to a C++/Python repo to test ~3 small pure functions; the toolchain earns its place only once the `syncVisibleControls` DOM test (and a real body of JS logic) lands with it. **Do it as its own focused branch**, deciding the app.js seam first (it's already `type="module"`, so extracting `reconcileControlRows` into a served file — wired through `embed_ui.cmake` + the two HttpServerModule routes like the other UI .js — is the clean shape). Pure-helper `_test` exports + the reconcile extraction are the two pieces; both were prototyped in that reverted attempt. -- **Browser-level Improv automation** (deferred) — `moondeck/build/improv_smoke_test.py` (added 2026-06-03) exercises the device-side Improv listener over plain serial; what's missing is the browser-side equivalent — Playwright driving Chrome's Web Serial, clicking through ESP Web Tools' install modal, filling the WiFi creds form, asserting `PROVISIONED`. Catches "ESP Web Tools changed its Improv handling in a way that broke our manifest format" failures the serial-only smoke test can't see. Hard to set up reliably (headless Chrome with Web Serial is finicky, needs a wired ESP32 in CI). Pick this up if a regression in the browser flow ever escapes the manual dev-environment test (preview_installer flash-ready mode at ). - -### Live full-suite run leaks state between scenarios (test infra) - -`run_live_scenario.py --module all` runs scenarios in sequence against one device, and they share the live tree. Two scenario styles don't compose: -- **Canvas-preparing scenarios** (`scenario_modifier_swap`, `scenario_perf_light`, `scenario_perf_full`) `clear_children` the containers and rebuild, then their cleanup leaves the tree **bare**. -- **Canonical-tree-assuming scenarios** (`scenario_GridLayout_resize`, `scenario_MoonModule_control_change`, `scenario_NetworkModule_mdns_toggle`) are `mutate` scenarios that expect the boot tree (Grid / Noise / Multiply) to already exist and only tweak it. - -Run a bare-leaving scenario before a tree-assuming one and the latter fails pre-flight ("references ids neither on the device nor added by an earlier step"). Each passes **in-process** (fresh tree per scenario — the authoritative gate) and **live individually** (after a clean boot); only the chained live run trips. Not a product bug — a consequence of the "scenarios own their state, no restore" model the canvas-preparing scenarios follow, which the older ones predate. - -Fix options: (a) make every live mutate scenario clear+rebuild its own canvas (consistent with the newer ones) so order never matters; or (b) have the live runner reboot / restore the canonical tree between scenarios. (a) is the cleaner long-term shape. Until then, the in-process suite is the gate; live full-suite runs need a clean boot per scenario, or run scenarios individually. - -## An explicit `Custom` Ethernet preset is reseeded to a named one - -`ethPresetIsUnset()` returns true for any editable row, and `Custom` is the only editable row, so it cannot tell "nothing has chosen a preset" from "the user chose Custom". A board saved as `Custom` whose pins happen to match a named preset is therefore reseeded to that preset on the next rebuild, and its pin rows are hidden: a hand-wired board silently adopts a board map it was never given. - -The seam is what makes this awkward. `defineControls` runs before the config is restored, so the seed cannot simply latch a flag on the first render: that is the legacy path the seed exists for, a board whose file predates `ethBoard` and carries only pins. Distinguishing the two needs to know whether the restore supplied an `ethBoard` value at all, which is not visible where the seed runs. An attempt that latched on the first render broke the legacy path and was reverted. - -**What it costs today**: nothing on a board the catalog configures, since those name their preset. It bites a user who deliberately picks `Custom` on a board whose pins match a preset, which is the hand-wired case `Custom` exists for. - -**Where to look**: `FilesystemModule::applyNode` overlays values, calls `rebuildControls`, then overlays again. A control that wants to know whether IT was restored needs a signal from that pass, which no control has today. That is the general shape worth solving rather than a special case for one select. - -## Ethernet on the QuinLED Dig-Next-2 (W5500 over SPI) - -The DN2 is in the catalog with no Ethernet block, so it comes up WiFi-only. Adding it is a `deviceModels.json` entry and nothing else: the preset work landed the seam, and W5500 already applies live. - -**RMII is impossible on this board, so the W5500 is the only route.** The DN2 carries an ESP32-PICO-V3-02, whose package leaves GPIO 16, 17, 18 and 23 unbonded, and those are the RMII management and clock pins. This is the same package fact behind the flash-cache wedge recorded in [lessons](../past/lessons.md). QuinLED's own LAN8720 board stacks onto the QuinLED-ESP32 only, not onto a Dig board. - -**The wiring, from [Quindor's firmware source](https://github.com/intermittech/WLED-Dig-Next-2_W5500)**, using the QEXT header for the bus and the Stemma QT header for the two control lines. Fitting Ethernet therefore costs the I2C port: - -| W5500 | connector | GPIO | -|---|---|---| -| SCLK | QEXT green | 25 | -| MOSI | QEXT blue | 32 | -| MISO | QEXT yellow | 0 | -| CS | QEXT white | 33 | -| INT | Stemma QT SDA | 15 | -| RST | Stemma QT SCL | 14 | - -MISO on GPIO 0 is deliberate and sound: GPIO 0 is a strapping pin, and a W5500 leaves MISO high-Z while CS is inactive, which is what Espressif's rule about not driving strapping pins at reset asks for. - -**It stays `Custom`, and that is the finding.** Across the three W5500 boards now known, LightCrafter 16, SE 16 V1 and this one, no two share a single pin on any of the six lines. SPI Ethernet is a module wired to whatever a board left free, so there is no W5500 pattern to make a preset from, and inventing one would be a preset that fits nothing. - -**What is unverified.** The pin table is trustworthy as what that firmware does, because it was read out of the code rather than a description. What rests on one source is that it works: a single-author repository created 2026-09-19, self-labelled experimental, tested on one unit, with LED output, buttons, relays and mic explicitly not exercised. No independent build was reported when this was written. So the open question is not the pins but whether SPI Ethernet and the LED output coexist on this board under load, which is a bench answer. - -## Housekeeping - -### Promote the one-line `//` cap to an error, once the 604 `.cpp` blocks are homed (2026-09-21) - -`MAX_CODE_COMMENT = 1` is already the rule in both kinds of file, and already an ERROR in a header. It stays a warning in a `.cpp` only because `_blocks()` stages the sweep by file kind, and that function names its own removal: a rule leaves the staging as the tree meets it, which no-hard-wrap already did. - -**The shape of what is left.** 604 blocks across 92 files, 2081 comment lines. By area: 239 in `test/`, 210 in `src/`, 155 under `src/platform/`. By length: 81 are two lines, 321 are three, 124 are four, so 526 of 604 are three lines or fewer and the tail is one 15-line block, two of ten and a handful between. - -It concentrates, which is what makes it approachable. `HttpServerModule.cpp` alone holds 100 and the top ten files hold 312, over half; the other 82 files average under four each. - -**Where the depth goes is the blocker.** Of the 92 files only 19 already carry an `@technical` appendix. 68 have a `///` lead with no appendix and 5 have neither, so most files need the appendix written before a single block can move. - -**The fix is not to split them.** Chopping one comment into two shorter lines leaves the text identical and satisfies nothing, which is why the cap counts a block rather than a line. Each one is depth that belongs in the file lead's appendix with an `@xref` back, or prose that says less. - -**Suggested order.** `HttpServerModule.cpp` first: 100 blocks, 444 comment lines, and it already has the appendix. Then the other nine heavy files for 212 more. Then `test/` as its own sweep, because the argument that a test comment explains why a test EXISTS is strongest there and may deserve a different budget rather than an appendix. Promote once at zero. - -**Cost.** Not a scripted pass. Each block is a judgement between depth that belongs in an appendix, an explanation that should be shorter, and history git already holds. About 35 done by hand in one session is the measured rate, so the first file is a session of its own and the whole sweep is several days. - -### Hot path: move blocking work off the render callbacks (architecture) - -`-Wfunction-effects` proves the render path really does block — these are not annotation gaps, -they are synchronous I/O, allocation and lifecycle work reachable from `tick()`. Each needs the -work moved to a worker or made resumable, so each is a design change rather than a lint fix. -Confirmed by external review (CodeRabbit, PR #56). - -**`tick()` — every frame, the sharp ones:** -- `PreviewDriver::tick` — the socket half is FIXED (the pull-model transport only ARMS a message; - every socket byte moves on the transport tick). What remains: `buildCoordTable()` resizes - `keptIdx_` and the staging buffer on a rebuild/adopt tick. Suppressed at the site with the reason. -- `ParallelLedDriver::tick` — `tickSync()`/`tickRing()` reach `busWaitIfBusy()`, which spins for - the DMA peripheral. Deliberate (the driver owns the bus for the frame) but blocking. Suppressed. -- `Drivers::tick` — joins/stops the render-split worker synchronously on the timed-out recovery - path. Wants asynchronous handling that still preserves buffer ownership. -- `HueDriver::tick` / `tick1s` — synchronous HTTP (`pushOneChangedLight`, `pollPairing`, - `fetchLights`, `fetchGroups`) plus allocation. Wants a queue to a worker. -- `Layer::tick` — calls `applyState()` on a modifier rebuild, which runs prepare/release and - resizes ScratchBuffers. Wants the rebuild deferred to the scheduler's non-render prepare path, - keeping today's coalescing of `consumeNeedsRebuild()`. -- `DemoReelEffect::tick` — `advance()`/`swapTo()` create, delete and `applyState()` child effects - from the render callback. Wants a pending-switch flag consumed off-tick. -- `NetworkSendDriver::tick` — sends inline; wants to enqueue. -- `RmtLedDriver::tick` — waits on hardware completion and reset; wants polling or offload. -- `AudioService::tick` — UDP `sendTo`/`recvFrom` every frame (`syncSend`, `syncReceive`, - `syncEnsureSocket`). - -**`tick20ms()` / `tick1s()` — milder, same shape:** -- `HttpServerModule::tick20ms` — inline `handleConnection` transport work, so the 100 ms budget - cannot actually bound it. Wants resumable connection handling. `tick1s` writes WLED state - frames inline; wants the existing resumable sender. -- `MqttModule::tick1s` — synchronous DNS + discovery-buffer allocation. -- `NetworkModule::tick1s` — WiFi/AP lifecycle transitions and tree rebuilds. -- `FilesystemModule::tick1s` — the debounced `flush()` does filesystem work; the poll itself is - fine, the save wants a worker. -- `FileManagerModule::tick1s` — `platform::filesystemUsed()`; wants a cached value. -- `SystemModule::tick1s` — the P4 `coprocessorWifi()` path calls - `esp_hosted_get_coprocessor_fwversion()` synchronously; wants a cached snapshot. -- `ImprovProvisioningModule::tick` — runs the queued APPLY_OP inline; wants a cold task to - execute and publish only the result. -- `Scheduler::tick` — dispatches all of the above, so its own contract is only as good as theirs. - -**Explicitly NOT in scope:** the float-math findings (`BouncingBallsEffect`, `RipplesEffect`, -`SphereMoveEffect`). Review suggested fixed-point, but the float trajectory IS the ported -MoonLight behaviour and fidelity is deliberate. If the FPU-less Xtensa cost is real, that is a -profiling question first, not a lint fix. - -`-Wfunction-effects` reports these but never fails a build; the code check counts them per file, and that count may only fall. - -### ESP32 clang/LLVM toolchain — extend the clang checks to src/platform/esp32/ - -Espressif ships an xtensa LLVM (their fork), but the installed `esp-clangd` package contains -**only `clangd`** — no `clang++` driver — so a clang analysis pass over ESP32 sources needs their -full LLVM installed separately. - -What it would buy: `-Wfunction-effects` (and clang-tidy, clang-query) over `src/platform/esp32/` -— 20 translation units, the only code the desktop build excludes. Everything else, including the -LED drivers, already compiles on desktop and is already checked. - -What it costs: a second ~1 GB toolchain, maintained purely for analysis — the firmware would -still be built by GCC, so the analysing compiler is not the shipping compiler. That is a real -"one rule, one owner" tension, and the reason this is a decision rather than an obvious yes. - -Worth revisiting when either the coverage gap bites (a hot-path bug traced to the platform layer -that the desktop check could not see) or Espressif's LLVM becomes the default toolchain. - -### clang-tidy: triage the remaining 30 findings - -`.clang-tidy` runs `*` minus a documented disable list and reaches zero on everything except the -path-sensitive `clang-analyzer-*` family: **30 findings**, led by -`bugprone-unchecked-string-to-number-conversion` (9), `security.insecureAPI.strcpy` (5), -`core.NonNullParamChecker` (4), `deadcode.DeadStores` (3) and `unix.cstring.NullArg` (3). Roughly -half sit in test code (`strcpy` into fixed local buffers, a deliberate divide-by-zero probe). - -The nine `unchecked-string-to-number-conversion` sites were read individually and are all safe: -HueDriver guards on `if (id > 0)`, JsonUtil documents 0-means-absent, MqttModule uses a `v = -1` -sentinel and clamps. They stay reported rather than suppressed — a report shows the real situation, -and a `NOLINT` would hide a genuine class of bug for the next person who writes one of these. - -These surfaced late for an instructive reason: the report parser's check-name pattern rejected the -`,-warnings-as-errors` suffix clang-tidy appends when `WarningsAsErrors` is set, so **every finding -was silently dropped** the moment the ratchet was switched on. Fixed; recorded here because it is -the sixth silent-zero this tooling has produced, and each looked like a clean tree. - -`WarningsAsErrors` stays empty: clang-tidy reports, it does not gate -([testing.md § Static analysis](../../reference/testing.md#static-analysis)). - - -### Migrate HueDriver's name buffers to `ScratchBuffer` (finish the ScratchBuffer sweep) - -`HueDriver::ensureNameBuffers()` / `freeNameBuffers()` is the alloc-in-build / free-in-release / null-guard bundle that `ScratchBuffer` absorbs — and it does **no** `dynamicBytes` accounting, so the module's memory readout under-reports its Hue-light name buffers. It was skipped in the ScratchBuffer migration (its buffers are `char[]` name tables, not the grid-sized state the sweep targeted). Now that `DriverBase::release()` chains to `MoonModule::release()` (fixed on PR #43), a `ScratchBuffer` member here would free correctly on disable and self-report its bytes. Mechanical, one-file, behaviour-preserving. Flagged by the 👾 Reviewer on PR #43. - -### Link-time optimisation on the ESP32 builds (measure) - -Splitting `HttpServerModule` into three files cost 7,360 bytes on the classic ESP32 (2,129,216 to 2,136,576), the compiler no longer inlining across the files. Link-time optimisation (`-flto`) optimises the whole program at the link and wins that back, and likely more across the firmware. ESP-IDF's current docs offer it as `CONFIG_COMPILER_LTO_LINKTIME`; v6.1, which MoonLight builds with, has no such option. Try it once the ESP-IDF release MoonLight moves to offers it, per board: the image size, the IRAM placement in the map file (an interrupt handler must not inline code from flash), a run on the RMT and parallel drivers, and the build time, which LTO makes two to three times longer. - -### Pin the preview cancel over loopback (test) - -`cancelBufferedSend` closes a preview client left mid-message, so the next frame is never read as the rest of this one, and nothing pins it. The loopback `WsClient` in `unit_HttpServerModule_subscribe.cpp` already drives `/wsp` and reads a frame whole; a client that stops reading mid-frame, then a cancel, then a check that its socket closed while a client that read the whole frame stays open, covers it. - -### Adopting the v6.x ecosystem changes (plan) - -The as-is state of each item is the table in [build-system.md § The v6 ecosystem](../../reference/build-system.md#the-v6-ecosystem); this entry carries the plan and the triggers. - -**Per item, how and when:** - -- **EIM** — adopt any time. It shipped *in* v6.0 so it clears the v6.0-floor rule, and it has a headless CI mode. Add EIM as the **preferred** path in `setup_esp_idf.py` (`eim install`), keep `install.sh` as a documented fallback for one release. Keep the exact-commit pin: EIM's multi-version management helps reproducibility, it does not replace the pin. -- **PSA Crypto** — nothing to migrate while we stay on high-level components. Watch only: if a feature needs hashing or signing directly (signed-OTA verification, a device identity), write it against the **PSA API** from the start, not legacy mbedTLS. Trigger: first direct crypto use. -- **`network_provisioning`** — adopt to close the phone-app + SoftAP gap. It is in v6.0 (clears the floor) and is the IDF-native standard. Add it as a **sibling provisioning module** beside ImprovProvisioning (both wired-by-code System modules; a device offers whichever transports its chip supports), reusing the same WiFi-credential plumbing. Not a replacement for Improv: they cover different front-ends (browser vs phone-app) and a product can want either. Weigh the BLE-stack cost per chip. Spec before code. -- **CMake Build System v2** — watch until GA, not while it is a preview: adopting a preview build system is the opposite of common-patterns-first. Trigger: v2 ships as the default. Then dry-run a build under v2, fix any `idf_component_register()` / Kconfig fallout, switch. Low risk given how little custom CMake we have. -- **Built-in MCP server** — evaluate, do not default to it. The risk is a *second control path* bypassing the script policy, and it is ESP32-only (no desktop), so it cannot be the uniform path. If adopted, wrap it behind a script (`moondeck/run/idf_mcp.py`) so the policy layer still applies. Trigger: a concrete debug workflow the scripts cannot cover. - -**Sequence.** Close the v6.0 gaps one at a time as normal feature commits: - -1. **EIM installer** — rework `setup_esp_idf.py` to prefer `eim install`, keeping `install.sh` as a one-release fallback. Smallest and lowest-risk (build-path only, no firmware change, no hardware re-test), and EIM's multi-version management is what cleanly supports the v6.0-floor / v6.1-fallback juggling, so it sequences first as an enabler. -2. **`network_provisioning`** — the headline capability: the phone-app + SoftAP onboarding flow. Its own plan. -3. The rest (PSA-native crypto, CMake v2, MCP) as their triggers fire. - -**Guardrails.** Platform-generic stays intact: these are ESP32-specific gains and none may regress Teensy or the desktop paths, which do not use ESP-IDF at all. An IDF feature is adopted *inside* the ESP32 platform layer / build tooling, never by leaking an IDF assumption into shared `src/` or the desktop build. And the v6.0 floor holds: adopt only what is in v6.0, so the v6.0 fallback keeps working. - -### ESP-IDF version pinning (pending) - -The build pins ESP-IDF `v6.1` GA, commit `fff9895c`. `setup_esp_idf.py` holds it in `PINNED_IDF_COMMIT` and `PINNED_IDF_VERSION`, warns when the installed tree differs, and offers to check the pin out (`--no-checkout` keeps it a warning). The version facts, the v6.0 floor, the support policy and how to check for a newer tag, live in [build-system.md § ESP-IDF version](../../reference/build-system.md#esp-idf-version); moving the pin is the `bump-idf` process. **Still to do:** a MoonDeck banner or status dot surfacing the same drift, since the warning shows only while `setup_esp_idf.py` runs. **History:** until the move to GA on 2026-09-29 the build ran `v6.1-dev-399-gd1b91b79b5`, a dev-branch snapshot, which cost the missing `ESP_ROM_ELF_DIR` in the post-build gdbinit step (fixed in `build_esp32.py`); the move waited for v6.1 to tag rather than for its calendar date. - -### Clock sync — a shared monotonic clock across devices (committed design, unwired) - -The second half of the core [multi-device runtime](../../explanation/architecture/mooncore.md#multi-device-runtime); discovery ships, this does not. The design: one leader sends its elapsed time over OSC, the protocol that already carries the shared controls, with the same `addressing`; followers compute their offset, targeting sub-millisecond accuracy. A shared monotonic clock is the foundation any cross-device coordination builds on, which is why it is core rather than light-domain. - -The light-domain payoff is a wall of controllers animating in lockstep: effects already animate off elapsed time, so feeding them the leader's synced clock instead of each device's local one is the whole change on the render side. Device-to-device light *distribution* is a separate topology question and rides the existing ArtNet / E1.31 / DDP standards rather than a bespoke protocol. - -### Config provenance: firmware/MCU → deviceModel (catalog follow-ups) - -The model itself is now a shipped design; see architecture.md § Config provenance (deviceModel is the one provenance level, the `txPowerSetting` example, and "default only where the hardware actually fixes it"). The catalog that carries it is `mooninstaller/deviceModels.json` (schema). The remaining forward-looking pieces — a `devices.json`/MCU-layer split and annotated-pin images — stay gated by the sequencing rule (no catalog field ahead of a consumer). - -### Persistence overlay: partial-save / schema-change audit (backlog) - -The absent-key fix (`json::hasKey` guard in `applyControlValue`, so a saved file omitting a key no longer zeroes the control's default — the P4 `ethType` no-DHCP root cause) closed the acute hole. A broader audit would harden the overlay against the rest of the schema-drift surface, now that controls carry meaningful non-zero defaults: -- **Type-change safety (not migration, see MIGRATING.md).** `ethType` changed `int16` → `uint8` (Select). A persisted file written under the old type still loads (the flat parser reads the number either way, and Clamp snaps an out-of-range value to the new bounds). The task is to *confirm* each type transition degrades gracefully through the robust loader (absent → default, stale → clamp, unknown → ignore) — especially Text/Password/IPv4 buffer-size changes — NOT to add migration code. A transition that would lose data silently is documented as a break in the documented break, per the settled policy. -- **Test coverage.** `unit_Control_apply_absent_key` pins the absent-key contract; extend with a type-change round-trip (save int16, load into uint8 Select) and a narrowed-range clamp case so a future schema change can't regress silently. -This is hardening, not a known bug — the shipped fix is correct for the cases that occur today. +**Found** on the StadBeest after a repower: the legs' walking layer was saved disabled, so the script that `fader2` to `fader6` drive had no controls. +The slots could not follow their targets, stayed at 0, and the OSC feedback sent those zeros to the eyes, whose `bpm` and `sensitivity` then sat at their minimum. +Live slots are not saved, so after a boot only a target or a writer gives them a value. -### ESP32-P4 support — rounds 3-4 (in progress) +**The fix to build:** a slot with no target to follow holds no value rather than 0, and the mirror sends nothing for it until the target exists. +The followers then keep what they last had, and a refresh repairs them once the leader's target is back. +Pinned by a test: a disabled target's slot produces no feedback datagram. -Rounds 1 (board + Ethernet-only) and 2 (Parlio LED driver) have landed. Remaining rounds, each its own plan + commit: -- **Round 3 regression: WiFi associates intermittently on IDF v6.1 (2026-10-12; the PO sees it associate on some boots and not on others, 2026-10-16).** The variant keeps shipping so users can try it. It associated and served traffic on IDF v6.1-rc1 (below). The bench P4-NANO (v1.3, port `usbmodem5ABA0767291`, registry .139) flashed with `esp32p4rev1-eth-wifi` on the v6.1 pin, Ethernet unplugged: Improv delivered the bench SSID over USB serial, the board answered "unable to connect", and it never appeared on the network. Its log does not reach `monitor_esp32` on that USB-JTAG port, so the cause is unread. First step: read the P4's console (UART pins with an adapter) for the esp_hosted / C6 lines, then compare against the rc1 build that worked: the IDF move from rc1 to v6.1 and the esp_hosted version it pulls are the two candidates. -- **Round 3, WiFi via the C6 co-processor. WORKING, BUT THE LINK IS SLOW (2026-08-19).** Boots and associates on IDF v6.1-rc1 (see round 4); the remaining defect is throughput, bench-bisected below. The P4 has no native radio (`SOC_WIFI_SUPPORTED` absent); WiFi comes from the on-board ESP32-C6 over SDIO via `esp_wifi_remote` / esp_hosted. Landed as the `esp32p4rev1-eth-wifi` firmware variant: components pulled P4-only (`rules:` gate in `idf_component.yml`), and `ensureWifiInit()` needs no hosted bring-up of its own: esp_hosted self-initializes at boot via a constructor (`ESP_SYSTEM_INIT_FN`), which sets up the SDIO transport, RPC and wifi-remote channels before `app_main`. Calling `esp_hosted_init`/`connect_to_slave` there would be worse than redundant, `connect_to_slave` is a transport *reconfigure* that resets the slave and re-inits SDIO, which fails on a live link. The rest of the WiFi seam is unchanged because `esp_wifi_remote` is API-compatible. A deliberate, documented [v6.0-floor exception](../../reference/build-system.md#esp-idf-version); C6 config via `CONFIG_SLAVE_IDF_TARGET_ESP32C6` + `CONFIG_ESP_HOSTED_CP_TARGET_ESP32C6` + the `CONFIG_ESP_HOSTED_P4_DEV_BOARD_FUNC_BOARD` SDIO-pin preset. +## SystemModule - **Hardware results (bench, P4-NANO, 2026-06-12):** - - ✅ **esp_hosted / C6 SDIO comes up at boot.** `host_init: ESP Hosted`, `H_API: ESP-Hosted starting`, `add_esp_wifi_remote_channels`, `H_SDIO_DRV: sdio_data_to_rx_buf_task started`. No NVS error / assert / panic / hang. Device boots fully (~57-60 FPS), `hasWiFi` true, WiFi controls present. esp_hosted **self-initialises at boot via a constructor** (`ESP_SYSTEM_INIT_FN` → `esp_hosted_init`), so no bring-up code is needed in our platform layer — an earlier explicit `esp_hosted_init` + `esp_hosted_connect_to_slave` prelude was *removed*: init was a redundant no-op and `connect_to_slave` is actually a transport *reconfigure* (slave GPIO-54 reset + SDIO re-init). SDIO config confirmed correct on the wire: `CLK[18] CMD[19] D0[14] D1[15] D2[16] D3[17] Slave_Reset[54]`, 4-bit 40 MHz. - - ❌ **WiFi STA connect fails on the SDIO re-init.** The cascade DOES fire correctly (`Ethernet no link, cascading` → STA path), but `esp_wifi_init()` (forwarded to esp_wifi_remote) internally triggers `esp_hosted_reconfigure` → `Reset slave using GPIO[54]` → **`sdmmc_card_init failed` (×15) → `card init failed` → `esp_wifi_init failed: ESP_FAIL`**. So: the boot-time SDIO init succeeds, but a **runtime slave reset can't re-establish the SDIO link**. The C6 doesn't come back after the GPIO-54 reset during operation. This is an esp_hosted/SDIO/C6-slave-firmware level issue (reset timing or slave image), below our application code — the pins and Kconfig are correct. +### Memory: the largest buffers, by name - **Open issues before this is done:** +Kind: Feature - 0. **The esp_hosted build is slower on HTTP — BISECTED to the hosted link, not to us (2026-08-19), and much improved but NOT gone (re-measured 2026-08-21).** +The System card shows heap used by type and the largest internal block, and each Tasks row shows a module's `dynamicBytes`, but nothing names the big buffers: the layer buffer, a driver's DMA frame or ring, the Art-Net handoff. A read-only list under System, sorted largest first and fed by each owner's self-report, makes a 48 KB buffer visible as itself. It samples existing accounting once a second and intercepts no allocation; a leak hunt is a separate build-flag `heap_trace` mode, as CPU% is for Tasks. - **Re-measurement on IDF v6.1-rc1**, same board, same commit, same Ethernet interface, same method: +## DevicesModule - | build | per-request (`/api/system`) | throughput (76 KB `app.js`) | - |---|---|---| - | `esp32p4rev1-eth` | 10 ms flat | 1,973 KB/s | - | `esp32p4rev1-eth-wifi` | 40 ms typical, one 280 ms outlier in 12 | ~980 KB/s | +### DevicesModule: interop plugins and the command half - So the penalty is now **4x per-request and 2x throughput**, against 33-60x and 17x when this was written, and **the alternating 0.4/0.8 s pattern is gone** (one outlier in twelve, not every other request). Render is healthy in both (359 fps on the WiFi build). Something between the two IDF versions fixed most of it; what remains is the same shape (per-request, not per-byte) and still worth closing. The original measurement follows. +Kind: Feature - **Original (2026-08-19):** Same board, same commit, same application code, measured over **Ethernet on both builds** so the radio is not in the path: +Discovery is shipped ([DevicesModule](../../moonmodules/core/system.md)). It stays a code-wired child of Network: it is fleet-scope rather than this-device, so it is neither a System nor a Services module, and it moves to a top-level container of its own only once a second fleet module exists to justify one. What remains is *growth on the seam*, each piece additive (one plugin file, no core change): - | build | per-request (`/api/system`) | throughput (73 KB `app.js`) | - |---|---|---| - | `esp32p4rev1-eth-wifi` | alternating 0.434 / 0.788 / 0.446 / 0.813 s | 42 KB/s | - | `esp32p4rev1-eth` | 13 ms flat, 12 consecutive fetches | 708 KB/s | +- **More discovery plugins**: ESPHome, Tasmota, Hue (*hub-shaped*: a bridge whose Zigbee bulbs are children behind it, with link-button auth). Each is a new `DevicePlugin` declaring its `discoveryPort()` + classifying the datagram (or, for a system that only does mDNS, a re-introduced advertise-side browse scoped to *foreign* services only, never the ones we advertise). Hue is the canonical "more than a flat device" case the seam is shaped for. (Note: Hue *control* already ships as an **output driver**, see [HueDriver](../../moonmodules/light/drivers.md#hue): bulbs as effect pixels; the driver also *lists* its bridge in DevicesModule with the color-light count. Two complementary follow-ups remain: (a) auto-fill the driver's bridge IP from discovery so the user doesn't type it (the mDNS-browse plugin above); (b) **pair once, not per driver**: pairing + the app key currently live on each HueDriver, so two drivers on one bridge pair twice. The clean end-state moves the bridge identity (IP + key + Pair button + light list) into DevicesModule and makes HueDriver a pure output that reads the paired bridge by IP; do this together with the discovery plugin, since both hinge on DevicesModule owning the bridge.) +- **The command half** — `DevicePlugin::command()` (+ per-plugin capability/auth), so MoonLight can *control* a discovered foreign device, not just list it: set WLED brightness via its JSON API, a Hue resource via the bridge's authenticated CLIP API, a Tasmota via `cmnd`. Built when a control consumer exists; the discovery seam is already shaped to accept it (incl. hub plugins). This is the **multi-ecosystem selling point** — one UI controlling WLED + ESPHome + Hue. Commands split by need (the rule, not "all REST"): must-arrive config over REST; latency-critical sync over UDP (~0.5–1 ms vs REST's 10–50 ms — REST would visibly de-sync). +- **Live peer state** — a discovered peer's brightness / on-off shown in our list, refreshed by polling its REST `/json` after discovery gives the IP (discovery = UDP/mDNS, state = REST). The read-side complement to the command half. +- **Non-IP transports (board-gated, far future)** — Tasmota-MQTT / zigbee2mqtt need an MQTT client; **direct Zigbee/Thread** (S31/C6/H2 802.15.4 radio) makes MoonLight the *hub itself*, driving bulbs over the mesh with no gateway — the standout differentiator, the biggest lift. Same plugin philosophy, a transport addition + board gate. - Render is healthy in both (121-134 fps), so this is not frame-loop contention. Because the two images share every line of application code, the penalty is **esp_hosted being compiled in and its SDIO link serviced**, degrading traffic on an interface it is not even carrying. The cost is **per-request, not per-byte** (a 1 KB fetch cost nearly as much as a 36 KB fetch), which points at a periodic blocker a request must wait out rather than a slow pipe. +Full design + the reasoned transport split: `Plan-20260629 - UDP device discovery + mDNS advertise-only` (in the plans archive). - Already excluded on the bench: SDIO width/clock (already 4-bit/40 MHz, the maximum), WiFi buffer counts (match the S3's), ICMP latency (6.6 ms). An earlier `SystemModule` fix (`coprocessorWifi()` retried an unanswered version query every tick: 1,012,344 us → 257 us) was real and is kept, but it was **not** this: the stutter outlived it and vanished only when the C6 path left the image. +## EthernetModule - **Per-module tick timing, both builds on the SAME Ethernet interface (2026-08-19).** The measurement that says what this is NOT: +### An explicit `Custom` Ethernet preset is reseeded to a named one - | module | eth-only | eth-wifi | factor | - |---|---|---|---| - | HttpServer | 113 us | 3441 us | **30x** | - | File Manager | 279 us | 4686 us | 17x | - | Services | 242 us | 1388 us | 5.7x | - | Audio | 239 us | 1376 us | 5.7x | - | Effects | 172 us | 397 us | 2.3x | - | **Network** | 10 us | **92 us** | (negligible in absolute terms) | - | Drivers | 6601 us | 4215 us | **0.64x (FASTER)** | - | ParallelLed | 7186 us | 6569 us | 0.91x (faster) | +Kind: Bug - **Two theories are refused by this table.** It is not a blocking WiFi call in `NetworkModule` (`Network` costs 92 us, and `wifiStaRssi`/`wifiTxPower` are state-gated off entirely in `ConnectedEth`); and it is not periodic WiFi scanning, since no module carries anything like a 0.37 s cost. Instead **modules that touch no network at all** (Audio, Noise, Effects, Layer) slow by the same kind of factor as the HTTP path: a broad, roughly proportional slowdown of ordinary code. +`ethPresetIsUnset()` returns true for any editable row, and `Custom` is the only editable row, so it cannot tell "nothing has chosen a preset" from "the user chose Custom". A board saved as `Custom` whose pins happen to match a named preset is therefore reseeded to that preset on the next rebuild, and its pin rows are hidden: a hand-wired board silently adopts a board map it was never given. - **Working theory: L2 cache contention.** Both builds are byte-identical in every memory setting (`CACHE_L2_CACHE_128KB`, `SPIRAM_SPEED_200M`, `FLASHFREQ_40M`, `SPIRAM_MODE_HEX` — a full sdkconfig diff shows no cache/PSRAM/flash/CPU-freq difference), and code executes from flash through that shared 128 KB L2 in both. Adding esp_hosted's tasks, ISRs and DMA buffers evicts application code that was previously resident, so ordinary work starts missing to flash/PSRAM. This also explains the otherwise odd inversion: the DMA-bandwidth-bound `Drivers`/`ParallelLed` got *faster*, which is what happens when the CPU competes less for the same bus. **Consistent with the data, not yet proven** — separating cache contention from plain CPU stealing needs a cache-hit-rate counter or a per-tick histogram, neither of which we expose today. +The seam is what makes this awkward. `defineControls` runs before the config is restored, so the seed cannot simply latch a flag on the first render: that is the legacy path the seed exists for, a board whose file predates `ethBoard` and carries only pins. Distinguishing the two needs to know whether the restore supplied an `ethBoard` value at all, which is not visible where the seed runs. An attempt that latched on the first render broke the legacy path and was reverted. - **The cache theory does NOT explain the visible LED hiccup, and there are TWO effects here (PO observation, 2026-08-19).** Uniform cache contention predicts a smooth frame-rate drop (133 → 96 → ~73 fps), which is what the averages show. It does not predict a *stall*, yet a once-per-second hiccup is plainly visible on the fixture. So the throughput loss and the hiccup are separate problems and must be chased separately. +**What it costs today**: nothing on a board the catalog configures, since those name their preset. It bites a user who deliberately picks `Custom` on a board whose pins match a preset, which is the hand-wired case `Custom` exists for. - **Our instrumentation structurally cannot see the hiccup.** `MoonModule::tickTimeUs_` is a MEAN (`accumUs_ / frameCount`, [MoonModule.h:656](../../src/core/module/MoonModule.h)) and `Scheduler::fps()` is derived from it, so a single 300 ms frame among 70 good ones shifts the average ~4 ms and vanishes. Every per-module number in the table above is an average and none of them can confirm or refute a stall. External evidence of the stall is therefore weak but non-zero: sampling `fps` once per second for 30 s gives a steady 73 with periodic dips to 69-70 (~55 ms lost in those seconds), and the dips are NOT on a clean 1 s period. Ruled out along the way: the degradation is not caused by our own HTTP polling (fps is identical at 76 after 25 s of zero traffic and under continuous polling), and the early 96 → 73 decay is warm-up, not load. +**Where to look**: `FilesystemModule::applyNode` overlays values, calls `rebuildControls`, then overlays again. A control that wants to know whether IT was restored needs a signal from that pass, which no control has today. That is the general shape worth solving rather than a special case for one select. - **Per-TASK CPU, both builds, `--task-cpu-stats` (2026-08-19) — this is the decisive measurement.** Same profiling overhead on both, so the comparison is clean: +### Live RMII Ethernet reconfigure - | task | prio | eth-only | eth-wifi | | - |---|---|---|---|---| - | `main` (render) | 1 | **35.4%** | **93.3%** | the same work costs 2.6x the CPU | - | `IDLE0` | 0 | 63.3% | 5.1% | core 0 headroom is gone | - | `ipc1` | 24 | 7.8% | 12.7% | present in BOTH, so not the cause | - | `mmEncode` | 5 | 3.4% | 6.0% | | - | `sdio_read` / `sdio_write` / `sdio_process_rx` / `rpc_rx` / `rpc_tx` | 23 | absent | **0.0-0.1%** | the hosted tasks are IDLE | +Kind: Feature · Platform: all - **What this refutes.** Not a busy WiFi task: every SDIO/RPC task sits at 0.0-0.1%. Not periodic WiFi scanning, and not a blocking call in our render path either, since `main` is not *waiting* — it is *running*, and burning 2.6x the cycles for identical work. Not `ipc1` (priority 24, preempts everything), which is present on the eth-only build too. There is no WiFi *activity* to stop, so a "compile it in but don't run it" variant would likely change nothing; the cost is already there with the link idle. +RMII (classic/P4 internal EMAC) still saves config and asks for a restart to apply, because the EMAC bring-up is fiddlier to hot-cycle cleanly. Make RMII live too: a hot `esp_eth_stop` + EMAC/netif teardown + re-init on config change, matching the W5500 path, so every interface honors the no-reboot principle. - **What this supports.** The same instruction stream executing 2.6x slower with no extra runnable work is the signature of **memory contention**, consistent with the L2-cache theory above: esp_hosted's footprint evicts application code from the shared 128 KB L2, so ordinary code stalls on flash/PSRAM fetches. Cycles are spent *inside* `main`, which is why every module slowed proportionally and why the DMA-bound drivers (bandwidth-bound, not cache-resident) got faster. +## FilesystemModule - **Three more theories died on the bench (2026-09-29, IDF v6.1 final).** Each was tested against 30 consecutive `/api/system` fetches over Ethernet, so the radio carried nothing in any of them. - The FreeRTOS tick warning esp_hosted prints at boot (`CONFIG_FREERTOS_HZ is 100, ESP-Hosted recommended 1000`) is an unconditional nag rather than a measurement, and the arithmetic runs the wrong way: IDF's `usleep` busy-waits sub-tick intervals exactly, so the first eight TX retries cost 14.4 ms at 100 Hz against 17.2 ms at 1000 Hz. - Priority inversion is refuted by its own null result: raising our task from 1 to 10 changed nothing measurable, and IDF's own `esp_http_server` runs at priority 5, which would be equally starved if six tasks at 23 were the mechanism. - Nagle with delayed ACK looked compelling, since responses are written as a header then a body with no `TCP_NODELAY` and the median penalty is close to a 40 ms delayed-ACK timer, but setting it moved neither build: eth-wifi stayed at ~55 ms and eth-only was already 8-44 ms. - `CONFIG_LWIP_TCPIP_TASK_PRIO=23` is kept, on the grounds that level-with rather than below is defensible, NOT because it is proven: one run lost its 230-270 ms tail spikes and a later run saw them return. +### A config written for next boot is overwritten by the next save (2026-10-04) - **What the penalty actually costs a user, measured per interface (2026-09-29, both fixes in, 30 fetches of `/api/system` each).** +Kind: Bug - | build and link | median | spikes | - |---|---|---| - | eth-only, Ethernet | **14 ms** | none | - | eth-wifi, Ethernet | 51 ms | 3 of 30 at ~200 ms | - | eth-wifi, WiFi (RSSI -44) | 57 ms | 4 of 30 at 280-870 ms | +Network and Ethernet return false from `appliesConfigLive`, so a file written through `POST /api/file` waits on disk for the next boot. +Any save before that boot writes the module's in-memory values over it, so the upload is lost without a word. +Seen on the S3: a restored `NetworkModule.json` came back with the old `txPowerSetting` until the restore was followed straight away by a reboot. +The fix: `applyConfigFile` marks a module it does not apply live as pending on disk, and the flush skips that module's file until the next boot. +The uploaded file then wins with no UI change; the alternative, answering "applies at next boot" and offering a restart, still loses the file when the restart is declined. +Pin it with a test that writes the file, dirties the module, flushes, and finds the file unchanged. - Over Ethernet the eth-wifi build is DEGRADED rather than unstable, and an earlier note here calling it unstable on every interface was wrong. - A click lands in 51 ms where a person notices around 100, so the interface feels fine and the 3.6x is invisible for single actions. - It surfaces in two places: the ~200 ms outlier roughly once in ten requests, which a page load of eight parallel requests will usually hit, and any client that issues hundreds of requests, which is why the live scenario sweep failed 4 of 6 on this build over Ethernet while a human clicking around noticed nothing. - Over WiFi the median barely moves but the tail doubles and reaches 870 ms, and under scenario load it reaches multi-second timeouts. - So the honest split is: usable on a wired link, unreliable on a wireless one, and the eth-only build is the one to run when a P4 has to be dependable. +The root fix is a network bring-up that reconciles instead of re-creating. `NetworkModule::setup()` (netif create, driver install, the eth→WiFi→AP cascade) runs once at boot and crashes if re-run live, which is why Network opts out of the live apply; once it reconciles, a restored `NetworkModule.json` applies live and the pending mark is no longer needed for it. - **The interface is noticeably quicker than it was (PO observation, 2026-09-29).** Changing an effect over WiFi went from a visible hang to something that responds, which a fetch loop cannot measure: that gesture is a POST plus a tree rebuild plus a state resync, so it pays the tail several times over where a single GET pays it once. - THREE things changed together and no single one is credited: `TCP_NODELAY` on accepted connections, `CONFIG_LWIP_TCPIP_TASK_PRIO=23`, and a board no longer carrying a failed scenario's leftover driver. - That last one also taints the earlier "20-second timeouts on WiFi" figure recorded here, which was measured on a board in that state; the 4x penalty over Ethernet is clean and reproducible, the WiFi instability was not measured as carefully. +### Remove the flat config conversion (2026-10-08) - **Why the co-processor board and not the others.** A native-radio chip carries its WiFi stack in the image the cache was sized around; the P4 has no radio, so WiFi arrives as an additional resident subsystem (six tasks, an SDIO driver, RPC machinery, DMA buffers) on a chip whose 128 KB L2 is shared by everything. The difference is not silicon quality but whether WiFi is extra code competing for that cache. +Kind: Restructure · Trigger: in the release after next - **Still unexplained: the visible ~1 s LED hiccup.** Cache contention predicts the steady 2.6x, not a stall. Per-task CPU is cumulative-since-boot and cannot show a spike either. So the hiccup remains unmeasured, and the worst-case instrumentation below is still the next step for it specifically. +A config file in the flat format builds before 2026-10-08 saved is read as the state document it describes at the first boot and saved as one, so an in-place update keeps its config. +In the release after next, remove the conversion and everything that only serves it: +- `flatToStateDocument`, `isFlatConfig` and their helpers in `StateDocument.cpp` (`writeFlatNode`, `writeFlatValues`, `takeLive`, `flatName`, `distinctName`, `flatRoot`); +- the conversion in `FilesystemModule::loadSubtree`; +- the flat fallback in MoonBase's `ConfigScrape.h` (`flatChildKey`, `flatChildValue`); +- `configToDocument` and `isFlatConfig` in `src/ui/migrate.js`, after which an older backup no longer restores; +- the test "A config file in the flat format older builds wrote loads at boot and is saved back as a document", and the MIGRATING entry's lines on the conversion. - **Instrumentation gap to close first — worst-case tick tracking.** Add a per-module and per-scheduler *max* (and ideally a coarse histogram or a "frames over 2x mean" counter) alongside the existing mean, and expose it in `/api/system`. Cheap (one comparison per tick), and it converts "the PO can see a hiccup that no number shows" into a measurable quantity. Without it, every theory below is unfalsifiable from the host side. Reading the P4 console needs `CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y` or a UART adapter on GPIO 37/38 (see the dev-loop note in round 4), so serial is not the fast path. +`NetworkModule::adoptLegacySettings` calls `isFlatConfig`, so the 6.0 network adoption above goes first or in the same change. - Then, cheapest first: (a) read the new max/histogram to see whether the cost is uniform or spiky (uniform favors cache contention, spiky favors a blocking call, and the two coexisting is now the leading reading); (b) the P4 cache performance counters for a direct miss-rate read; (c) moving the hot render path to IRAM/internal RAM and re-measuring, which is also a candidate fix rather than only a diagnostic. - 1. **Runtime SDIO re-init of the C6 fails: CONFIRMED a C6 slave-firmware problem (not a guess).** SystemModule now exposes a `wifiCoproc` read-only control (via `platform::coprocessorWifi()` → `esp_hosted_get_coprocessor_fwversion()`), and on the bench it read **`not detected`** at the time (the control now reports **`no version reply`**, and only after a bounded number of attempts): the C6 returns no valid firmware version. **What that means was overstated here.** It was read as the signature of absent / incompatible slave firmware, but the same board later associated and served traffic over that very link while this RPC still went unanswered, so an unanswered version query says the QUERY failed, not that the C6 is absent. The round-3 conclusion below rests on the `sdmmc_card_init failed` re-init evidence, not on this control. Likely a version mismatch on top of that: The host pulled esp_hosted **2.12.9**; Espressif's P4-Function-EV-Board ships its C6 pre-flashed with esp_hosted slave **v0.0.6**, and the **Waveshare NANO is a different board that may carry a different / absent C6 slave image**. The symptom fits: boot inits the host SDIO master fine, but resetting the C6 (GPIO 54) and re-enumerating it as a slave fails (`sdmmc_card_init failed`) because the C6 has no compatible slave firmware responding. **Primary next step: build + flash the version-matched esp_hosted slave firmware onto the NANO's C6.** The slave project is already vendored at `esp32/managed_components/espressif__esp_hosted/slave/` (`sdkconfig.defaults.esp32c6`, `partitions.esp32c6.csv`); `idf.py create-project-from-example "espressif/esp_hosted:slave"` → `set-target esp32c6` → flash. **Caveat / needs PO + bench hardware:** flashing the C6 on the EV board uses an **ESP-Prog wired to the `PROG_C6` header** with the P4 held in bootloader mode (esp_hosted `docs/esp32_p4_function_ev_board.md` §5.2): the NANO's C6-flash path must be confirmed (separate USB? equivalent header? ESP-Prog?), and an ESP-Prog may be needed. An OTA slave-update path exists but needs a *working* link first (chicken-and-egg here). This is a hardware-provisioning task, not application code. Secondary fallbacks if firmware-match doesn't fix it: an esp_hosted option to skip the reconfigure/slave-reset when the transport is already up at boot; a slower SDIO freq or 1-bit mode; verify GPIO 54 reset polarity/timing for the NANO. **(Note: EIM, the build-system v6 ecosystem item, does NOT help here; it's a host-machine installer, unrelated to device-side C6 firmware.)** +### Config backup and restore: identity, a recipe, and tiers 2 and 3 - **User lead (2026-07-09) — avoid the WiFi teardown/re-init on hosted targets entirely; it may sidestep the slave-reset failure.** A user hit the mirror symptom on a *different* codebase (ESP32-Sveltekit / WLED-MM, not MoonLight — its `lib/framework/WiFiSettingsService.cpp` doesn't exist here): after a clean C6-flash the P4-NANO WiFi worked, but their app's boot-time `WiFi.disconnect(true)` fully tore down the WiFi stack, and the later AP bring-up then failed with `esp_hosted_transport_config: Transport already initialized` / `esp_hosted_init failed!` / `AP enable failed!`. Their working fix: on `CONFIG_ESP_WIFI_REMOTE_ENABLED` (= hosted) targets, **don't do a full stack reset** — keep STA enabled, reconnect *without* a full teardown/re-init, and never call `WiFi.disconnect(true)` in the disconnect callback. **Why this is relevant to us even though the file differs:** our own comment at `platform_esp32.cpp:796` already documents that the esp_hosted transport is set up **once at boot** and is fragile to re-init (`connect_to_slave` = a transport reconfigure that resets the slave + re-inits SDIO and fails on a live link). Our disconnect *callback* is already safe (`wifiEventHandler` on `STA_DISCONNECTED` only sets a flag — no teardown), BUT our **failover path is not**: `wifiStaStop()` (`platform_esp32.cpp:915`) calls `esp_wifi_deinit()`, and the STA-retry / AP-fallback then re-runs `ensureWifiInit()` → `esp_wifi_init()` — the exact deinit→reinit cycle that on a hosted target triggers the GPIO-54 slave reset (round-3 open-issue #2's `sdmmc_card_init failed`). So the round-3 failure ("runtime SDIO re-init of the C6 fails") and this user's report may be **the same root cause**: the re-init shouldn't happen at all on a hosted target. **Concrete next step to try on the bench:** guard the teardown/re-init on `platform::hasWifiCoprocessor` (already defined = `isEsp32P4 && hasWiFi`) — on hosted targets, do NOT `esp_wifi_deinit()` in `wifiStaStop()` and do NOT re-`esp_wifi_init()` in `ensureWifiInit()` once the boot-time init is up; instead just `esp_wifi_disconnect()` + `esp_wifi_set_config()` + `esp_wifi_connect()` (STA retry) or `esp_wifi_set_mode(APSTA)` for the fallback, reusing the live transport. This is cheaper than the C6 reflash and independent of #18759, so it's worth trying first once the board boots. If it works, it also removes the slave-reset from the normal failover, not just the AP case. Blocked behind the #18759 boot crash like everything else P4-WiFi, but this is the first thing to try when the board boots again. - 2. **Co-processor components no longer compile into `esp32p4rev1-eth` — FIXED.** The gate is now `rules: if "$CONFIG{MM_P4_WIFI} == True"` (the `$CONFIG{NAME}` form, no `CONFIG_` prefix inside the braces — the bare form silently skipped the dependency, see round 4 below) (a Kconfig option declared in `esp32/main/Kconfig.projbuild`, set only by `sdkconfig.defaults.esp32p4rev1-eth-wifi`), so `esp_hosted` / `esp_wifi_remote` are pulled **only** by the WiFi build, never by eth-only. The old `target == esp32p4` gate pulled them into `esp32p4rev1-eth` too; that wasn't merely build-time waste — esp_hosted self-inits its SDIO master at boot, which on the eth-only build interfered with the EMAC bring-up (a red herring chased during the P4 no-DHCP hunt). The eth-only image dropped 1.36→1.12 MB once gated out. The `wifiCoproc` read-out stays compile-gated on `platform::hasWifiCoprocessor` (`isEsp32P4 && hasWiFi`). - 3. **Build reproducibility.** `build_esp32.py` does not yet build this variant reliably: the C6 slave-target Kconfig `default ... if IDF_TARGET_ESP32P4` only fires on `set-target`, and the reconfigure a plain `build` triggers drops it back to ESP32-H2 (no WiFi) → fails on missing `CONFIG_WIFI_RMT_*`. A clean manual sequence works (`rm -rf ` → `set-target esp32p4` → `build`, all with the same `-DSDKCONFIG`/`-DSDKCONFIG_DEFAULTS`); the wrapper needs a fix so the auto-default sticks across reconfigures (see the KNOWN ISSUE comment in `build_esp32.py`). +Kind: Feature - **Round 4 — the IDF-update regression (2026-07-03).** After the IDF bump to `v6.1-dev-5215-g0d928780081`, the variant stopped building entirely, then stopped booting. Two distinct causes, both IDF/component-manager side (all our config was correct): - - ❌→✅ **Build: `esp_hosted.h` not found — the manifest `if` syntax changed.** The component manager (now 3.0.3) silently skipped `esp_hosted`/`esp_wifi_remote` (`NOTICE: Skipping optional dependency`) because our `idf_component.yml` rule used the bare `CONFIG_MM_P4_WIFI == True`. The current manager only recognises a Kconfig variable in the **`$CONFIG{...}`** form (its `KCONFIG_VAR_REGEX = \$CONFIG\{([^}]+)}`); the bare `CONFIG_X` falls through to plain string-eval → false → skipped. **Fixed:** `if: "$CONFIG{MM_P4_WIFI} == True"` (note: NO `CONFIG_` prefix inside the braces). Confirmed by [idf-component-manager #104](https://github.com/espressif/idf-component-manager/issues/104) + the official manifest docs. This also supersedes open-issue #3 above — with the correct syntax the pull is reliable (no `set-target` dance needed for the *dependency*). - - **Boot: `sleep_clock_icg_startup_init` aborts with `ESP_ERR_NO_MEM`.** Worked around in the P4 WiFi fragment; tracked in [Issues in other projects](../../reference/issues-in-other-projects.md) as esp-idf #18759. -- **Round 4 — Parlio loopback self-test FIXED (2026-07-09), real long strip still to prove.** The Parlio loopback self-test now passes on P4 hardware at any grid size (verified on MM-P4, jumper GPIO 32↔33, at both 8×8 and 128×128). A real *long* WS2812 strip (not just the bench panel) is the remaining hardware proof. +Tier 1, a browser-side bundle over the file API with the rename map and restore report, ships in the File Manager. - **The bug it exposed (fixed):** on IDF `v6.1-beta1` the self-test failed `bad bit 0/0` at large grids — it worked at 8×8 but not 128×128. Two stacked hardware limits, both hit only because the test built a frame sized to the *whole operational grid*: (1) the P4 Parlio has no DMA-EOF, so one non-loop transfer is hard-capped at `PARLIO_LL_TX_MAX_BITS_PER_FRAME` (~0.5 Mbit) — a 128×128 grid (~1.2 Mbit) is rejected outright with `ESP_ERR_INVALID_ARG`, so nothing transmits; and (2) even once the transfer fits, the shared RMT-RX capture can't hold a large symbol count (a big frame → `rx captured 0 symbols`). Both surfaced as the same misleading `bad bit 0/0`. **A wrong first guess** (an IDF `sample_edge`→`shift_edge` clock-edge rename) was tried on hardware and rejected — both edge values fail, ruling the edge out; the serial log (`loopback: N bytes in …`, `rx captured 0 symbols`) is what pinned the real cause. **The fix:** the self-test now caps its frame to a small fixed `ParallelLedDriver::kLoopbackTestLights` (256) instead of the operational grid — the test only needs to prove the peripheral emits correct WS2812 bits, which a few hundred lights exercise fully (encode → fused correct+transpose → single-shot DMA → latch pad), and 256 fits under both the transfer cap and the capture buffer on every parallel driver, so it runs identically at any grid size. **Lesson:** an on-device self-test must size its own probe to the peripheral's real limits, not to the user's configured output size. (This whole diagnosis is a good "verify against the working reference before concluding" case — the PO's memory that it *had* worked at fd6c1d8e is what redirected the hunt from "capability gap" to "regression".) +- **Restore clones a device's IDENTITY along with its config** (found answering + [#76](https://github.com/MoonModules/MoonLight/issues/76), 2026-09-04). Backup bundles every + file including hidden `.config`, and restore writes them all back unfiltered, so restoring one + device's bundle onto another copies four things that must differ per device: - **Loopback on the MHC-WLED P4 shield — RESOLVED: the header is not pure GPIO (shield builder confirmed, 2026-07-09).** The shield's 4x-In/Out header (pins **46, 47, 2, 48**; 46 & 2 are P4 boot straps per gpio-usage) never passed the continuity precheck: **46↔47** captured partial/corrupt (206 of 1536 symbols → `bad bit 0/0`); **47↔48** read `jumper not detected` even with the jumper seated. The `mm_loopback continuity …: hi=%d lo=%d` diagnostic read `hi=0 lo=0` (no direct signal path). **Shield builder (Wladi) confirmed the cause:** the header has **no pure GPIOs** — outputs go through a **level shifter**, inputs through **diode protection + a ~16 kHz low-pass filter** (by design, for robust button-style inputs), so the raw drive-and-read continuity probe can't bridge it. Not a firmware bug and not fixable in software; the self-test needs a genuinely direct GPIO pair. **Workaround for a real loopback test on the shield (Wladi's suggestion):** drive the LED signal out one **RS485 output**, configure a 4th channel as an **RS485 input**, and jumper the two with cables — the signal is then readable on the respective GPIO. The frame-size fix itself is **proven independent of this** (MM-P4 NANO, direct 32↔33, PASSes at every grid). The `loopbackJumperOk` continuity read is kept as a permanent bench HAL diagnostic (it's what pinned this) — a `hi=0 lo=0` result now has a documented meaning: a buffered/level-shifted header, not a direct GPIO. + | field | why it must differ | + |---|---| + | `deviceName` | the single network identity: mDNS hostname, SoftAP SSID and DHCP hostname all derive from it (`SystemModule.h`). Twelve clones all answer to `.local`, resolution goes non-deterministic, MoonDeck's device list collapses to one row | + | WiFi `password` | travels in the bundle (the UI button warns), so a shared or attached backup leaks it | + | a static IP | if set, every clone claims one address | + | `universeStart` and the Art-Net/DDP window | exactly what must differ per device in the light-pole case #76 describes: cloned, every pole shows the same thing | - **Dev-loop note — reading the P4's runtime log over USB.** The P4-NANO's primary console is **UART on GPIO 37/38** (`CONFIG_ESP_CONSOLE_UART_DEFAULT`), not the USB port, so `ESP_LOGI` / `mm_net` lines are *not* visible over `/dev/cu.usbmodem*` by default — only the ROM boot banner and `std::printf`-to-stdout (which routes to the **secondary** USB-Serial-JTAG console) come through. Two workarounds when you need the runtime log over USB: (a) temporarily set `CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y` (note the JTAG endpoint re-enumerates when the app starts, so a reader must reconnect across the drop — `idf.py monitor` handles it; a plain fixed `pyserial` handle dies); or (b) hang a USB-UART adapter on GPIO 37/38. This cost real time during the P4 no-DHCP hunt; the fastest signal there turned out to be a `printf` of the runtime struct (stdout → secondary JTAG console) plus a `git worktree` bisect (build an old commit, flash, check LAN reachability) to prove code-vs-hardware without needing the log at all. + This turns "clone this pole to the other eleven" from the feature the issue wants into a trap. + **The fix is small and is a prerequisite for the recipe idea rather than a separate job:** restore + treats identity as per-device, either skipping those fields or prompting once with the target's + current values prefilled. Worth deciding whether the bundle should carry the secrets at all, or + keep an identity section the restoring device is expected to supply. +- **A backup is a file bundle, not a recipe** (#76 step 2): nothing binds it to a `deviceModel`, so + restoring a Dig-Quad bundle onto an S3 writes pin maps that do not fit the board. The 28 profiles + in `deviceModels.json` are the missing half. +- **Tier 2**, a single-archive device endpoint (one request instead of a walk); **tier 3**, restore hosted on + MoonBase's page, the migration answer for future partition-table moves. -### WiFi runtime disable (backlog) +## HttpServerModule -Compile-time answer already ships: `--firmware esp32-eth` excludes the WiFi stack. The default `esp32` already *cascades* — `ethInit()` runs first, WiFi only comes up if no PHY responds — so a wired board never associates over WiFi. What's still missing is reclaiming WiFi's **heap**: even when Ethernet wins the cascade, `esp_wifi_init`'s RX buffers stay allocated. This item skips that init entirely once Ethernet is up, freeing ~16 KB. Defer until the heap saving is worth the teardown-ordering risk. +### The HTTP server is audited for the two PHP request-handling bug classes (2026-10-21) +Kind: Investigation -## UI +PHP's critical advisory of 2026-10-21 (CVE-2026-109618, CVE-2026-109619) names two bug classes any C++ server can have. **Out of memory mid-request**: an allocation fails while a request is half built, and cleanup or a later step treats the partial state as valid. That path is live here: a classic ESP32 fell to 916 bytes free during a page load. **State outliving its request**: a reused connection slot (`wsClients_`, `streams_`) keeps a pointer, length or buffer from its previous client. The audit is a read-only pass over `HttpServerModule` and the platform TCP layer, every allocation-failure path and every slot reuse, and each finding is pinned by a desktop test that forces the failure. -Forward-looking companion to the shipped UI spec, [moonmodules/core/ui.md](../../moonmodules/core/ui.md). The live spec describes the UI as shipped; this file holds what is **not** in it yet: deferred items, open design questions for 1.0, and the gap analysis against MoonLight v1. +### A request body shares the two upload slots (2026-10-25, from the pre-merge review) -### Split app.js by feature (2026-10-15) +Kind: Bug -The UI ships as one minified bundle with ETags, so splitting `app.js` costs nothing at runtime. Almost every card comes from the one generic renderer, so the candidates are the features with their own UI: the control surface and pads, the File Manager, the gallery, the MoonLive editor, the preview, the status bar. Six to ten files, the shape `preview3d.js`, `help.js`, `migrate.js` and `gif.js` already have. Worth it once a feature is hard to find in the 500 KB source. +A request whose body trails its headers waits in a `FileTransfer` slot (`deferRequest`), of which there are two. With two uploads in flight, a `POST /api/control` whose body comes in a second segment answers 503 "every send slot is busy", and a slow-bodied control write holds an upload slot for the 10 s stall window. A slot of its own for `Kind::Request`, or a count kept apart, separates them; it waits for a user who meets it, since two uploads at once is rare. -### Deferred to 1.x +## I2cBusModule -- Side nav with drag-reorder of root modules (root order is fixed in `main.cpp` today; not painful — and arguably correct, see the gap-analysis note below) -- Health panel (`

    ` + `GET /api/test`) -- Log panel (`
    ` + WS `{t:"log",m:"…"}`) -- Core affinity badge (C0/C1) — only meaningful when core pinning lands -- Module `category()` field — taxonomy beyond `role()` for the picker (decision: derive from `role()` for now) +### A second I2C bus, once a board wires one (2026-10-06) -### File Manager follow-ups +Kind: Feature · Trigger: a board that wires a second bus -The shipped File Manager (see [system.md](../../moonmodules/core/system.md#file-manager)) is a lazy expand/collapse tree over `/api/dir` + a size-capped text editor over `/api/file`, with drag-drop upload (tier 1) + per-file download + a filesystem-usage bar. Deferred capabilities, each self-contained: +`I2cBusModule` drives one bus on controller 0, which covers every catalog board: one bus carries about 112 addresses, so a codec, a sensor and a display share it. +A board needs a second bus for two chips on the same fixed address, devices at different voltages or pull-ups, or a long cable run kept off the onboard devices. +The shape then: the module becomes addable with a controller-number control, the platform keeps one bus per controller, and a device on a bus (`AudioService`'s codec first) gains a bus selector defaulting to 0, so the catalog's boards keep working unchanged. +The trigger is a real board; a touch controller or camera on its own pins next to the codec is the likely first, the Guition M3's touch wiring being unchecked. -- **Upload — recursive folder tier.** Single-file upload of **any size** streams to the file (`fsWriteStream`, binary-safe, `kUploadMax` + free-space guarded) and downloads stream back (`fsReadAt`), so text/config/binary all work. Remaining: **multi-file / recursive folder drops** — client-side `mkdir` + walk via `dataTransfer.items` `webkitGetAsEntry()`. -- **Download — folder as `.zip`.** Per-file download streams (`` on `/api/file`). A folder download needs the browser to walk `/api/dir` recursively, fetch each file, and build a `.zip` client-side — which means bundling a zip library into `app.js`. That's a permanent app.js size bump, and app.js is embedded in firmware, so it weighs on the flash budget (see the flash-budget item above). Gate on real demand; symmetric with the folder-upload tier. -- **Last-modified dates.** Needs a time source (NTP) + LittleFS mtime storage; the tree is name + size until both land. Backlogged with the NTP work. -- **`.ml` syntax highlighting in the editor.** MoonLive source wants *highlighting* (a color layer over the textarea), not the JSON-style reformat — a bigger editor change (a highlight overlay or a small tokenizer), added when MoonLive `.ml` files land on disk. The editor already has an extension seam (`fmPrettify`) for the reformat case; highlighting is the separate, larger tier. -- **Keyboard + screen-reader access for the tree.** The `fm-row` tree entries are mouse-only today (click to select/expand/open). Making them keyboard-operable (focusable, Enter/Space activation) with ARIA tree semantics (`role="treeitem"`, `aria-expanded` from `isOpen`, `aria-level`) is a real accessibility win but adds JS that lands in the firmware-embedded `app.js` (flash cost). Do it when a11y is a stated goal, together across the whole generic UI, not just this panel. +## ImprovProvisioningModule -### Open design questions +### Improv-as-REST follow-ups -These don't block the shipped baseline but should be answered before 1.0: +Kind: Feature -- **Multi-layer UI** — [the architecture](../../explanation/architecture/index.md) plans for N layers blended into one Drivers. The current card layout shows one Layer. Likely needs a tab/accordion to switch layers, or a per-layer column. -- **Modifier chain visualization** — show the modifier order visually. They're a flat list today, but the `children[]` order **is** the apply order now (modifiers compose as a chain, M₁∘M₂∘…), so a visual that conveys the stacking (and that order matters) would help users reason about a multi-modifier layer. -- **Presets** — save/load named bundles of control values. Persistence already stores them; needs a UI surface. -- **Canvas/node-graph view** — v2 attempted this. Powerful for complex setups but doubles the UI surface. A reasonable v3 follow-up gated on user demand. +What remains of "Improv = REST over serial" ([ImprovProvisioningModule](../../moonmodules/core/system.md)): -### Gap analysis — v1 features not yet in v3 +**Open follow-up: shared JS helpers across device-UI and mooninstaller.** `safeLocalGet` / `safeLocalSet` (3-line hostile-storage guards) are duplicated in `src/ui/install-picker.js` (device firmware, embedded as a C string via `embed_ui.cmake`) and `mooninstaller/devices.js` (web installer page, served from Pages). The two live in different build contexts so the shared extract isn't trivial — it'd need a new `src/ui/safe-storage.js` plus updates to: `embed_ui.cmake` (embed the new file), `ui_embedded.h` generator (new C array), HTTP server file routing (new path served), `release.yml` workflow staging, `preview_installer.py` staging. Five files for one 3-line helper is too much pre-merge. Worth doing when the next shared helper arrives — `relativeTime` and `formatBytes` are candidates. Two helpers earn the build-glue cost; one doesn't. -Inventory of v1 frontend behaviours v3 lacks, with a recommendation each. Items already shipped (control types, dragTs, two-timescale inputs, type picker, theme, scroll-shrink preview, status bar, reset-to-default, fps/ms toggle, drag reorder, side nav + drawer + footer) are not repeated. +**Open follow-up: P4 Improv scan on a cold WiFi link (bench check).** `improvHandleScan` in `src/platform/esp32/platform_esp32_improv.cpp` calls `esp_wifi_scan_start`, which needs the WiFi driver started. On native ESP32/S3 the driver is up by the time a user provisions; on the P4 the radio lives on the C6 and comes up only after the esp_hosted prelude in `ensureWifiInit()` (triggered by `wifiApInit` / `wifiStaInit`). A scan requested on a P4 that has not initialised WiFi returns an error cleanly rather than scanning a cold link, so nothing crashes. The check: bench-verify whether a P4 provisioned from cold needs the link brought up first, and if so route the scan through the public `wifiAp`/`wifiSta` path. -Legend: **Adopt-1.0** (small, high value) · **Defer-1.x** (needs engine work or a feature we lack) · **Drop** (not needed). +## ModuleFactory -### Per-card features +### ModuleFactory: registration can fail silently (uint8_t ceiling, unchecked bool) -| v1 feature | v3 today | Recommendation | -|---|---|---| -| Header: setup-dot before name | name only | **Defer-1.x** — needs `setupOk()` + `health()` on MoonModule with a real failure mode. Today both would always be `true` / `""`. | -| Module ID shown separately from name | name only | **Defer-1.x** — add when instances need disambiguating (e.g. two effects of the same type under one Layer). | -| Category emoji badge on the card header | role emoji in the picker, not on the card | **Defer-1.x** — `ROLE_EMOJI` already exists in `app.js`; showing it per-card is a small step if card scannability needs it. | -| Core affinity badge (C0/C1) | core pinning not implemented | **Drop** until core pinning is a real engine feature. | -| Memory split heap vs PSRAM | `static+dynamic` shown on the card | **Defer-1.x** — splitting `dynamicBytes` further needs `platform::isPsramPointer(p)` or per-alloc tracking, neither exists yet. | +Kind: Bug -### WebSocket / panels +**Found:** 2026-07-27, while adding the effects grid sweep; flagged by the 👾 Reviewer. -| v1 feature | v3 today | Recommendation | -|---|---|---| -| Drag-to-reorder *root* modules (`POST /api/modules/reorder`) | not supported | **Drop** — root order is fixed in `main.cpp` and that's correct: Layouts/Layers/Drivers + system modules are mandatory and ordered. Children reorder via drag already. | -| Log channel `{t:"log",m:"…"}` pushed by server | no server log push | **Defer-1.x** — needs an engine-side log producer. Gate: when boot/network/persistence logs become interesting to non-developers. | -| Schema channel `{t:"schema",modules:[…]}` for tree-shape changes | full `/api/state` push every update | **Drop** — keep the full-tree push; re-evaluate only if WS bandwidth becomes a problem with large trees. | -| System health panel (polls `GET /api/test`, pass/fail table) | none | **Defer-1.x** — needs a runtime `/api/test` that runs the doctest suite; `ctest` covers this for now. | -| Log panel (ring buffer, severity coloring, stick-to-bottom, `GET /api/log` backfill) | none | **Defer-1.x** — pairs with the log WS channel; both arrive together. | +`ModuleFactory::count_` is a `uint8_t`, and at the 255 ceiling `grow()` returns false, registration fails, and the return value is discarded at every call site, including the static-init registrars, which cannot check it anyway. The failure is silent: the type is simply absent from `/api/types` and `create()` answers `nullptr` for it, far from the cause. -### Cost / decision table +Production is not near the ceiling today (~90 types), so this is not urgent. -| Cost class | Items | -|---|---| -| Tiny (< 30 lines, no backend) | category emoji badge on the card header | -| Medium (minor backend change) | richer `category()` than role()-derived | -| Large (separate plan) | health panel + `/api/test`; log panel + WS log channel; OTA + GitHub-update badge; full multi-layer UI; presets UI | +**Options, cheapest first:** (a) widen `count_`/`capacity_` to `uint16_t` (a handful of bytes, removes the ceiling as a practical concern); (b) make a failed registration loud in a debug build (assert or a boot-time log) so it can never be silent again. Both are probably the right pair. -### GCC Release-mode truncation warnings (17, non-blocking) +## MqttModule -`uv run moondeck/build/build_desktop.py --gcc` reproduces CI's compiler (CI builds **Debug**, and that is clean). Building the same tree **Release** with GCC surfaces **17 further `-Wformat-truncation` / `-Wstringop-truncation` / `-Wstringop-overflow` warnings** in `Control.cpp`, `HttpServerModule.cpp`, `JsonUtil.h`, `MqttModule.cpp` — Release inlines more, so GCC can prove more about buffer sizes and gets stricter. +### Generic control and state topics over MQTT: the automation escape hatch -They are **not** CI failures (CI is Debug) and each one inspected so far is a false positive of the same shape: a bounds check exists, but GCC cannot prove the degenerate case (e.g. `HttpServerModule.cpp:2400` warns "writing 1 byte into a region of size 0" on a line *guarded* by `if (wsLen + headerLen > sizeof(hdr)) return false;`). Still worth clearing: each is either a genuinely unprovable bound (fix the code) or a bound the code knows but does not state (make it explicit). Doing it in one pass beats letting them mask a real one later. +Kind: Feature · Stage: Integrate -Not done with the multi-destination/tab-UI merge because 17 warnings across four core files is its own change, not a tail on someone else's. +The MQTT bridge ships a **semantic** topic surface (`/on/set`, `brightness/set`, `hsv/set` → the `Drivers` controls) sized for Homebridge/HomeKit, which need fixed, typed topics per accessory. That leaves everything else the web UI can reach — effect choice, layout, modifier params, palette-by-index — unreachable from a home hub. The ask that surfaced this: *"expose the whole REST API over MQTT."* +**It's compatible with Homebridge/HA, because it's a second topic namespace, not a replacement.** Homebridge only ever subscribes to the semantic topics it's configured for; a generic topic family sits beside them and a broker carries both. The two surfaces already share one apply-core — `Scheduler::setControl(module, control, json)`, the same seam `/api/control` and IR funnel through — so a generic MQTT surface is that core under a different topic shape, honouring the module's own "MQTT is a transport, not new control logic" contract. -## MoonLive core/platform layering + JIT sdkconfig scoping (CodeRabbit #29, 3 findings left) +But "the whole REST API" over-scopes: of the routes (`/api/control`, `/api/state`, `/api/system`, `/api/modules[/…]`, `/api/dir`, `/api/file`, `/api/firmware/*`, `/api/reboot`, `/api/types`, the `/json/*` shim, `/ws`), only two are genuinely pub/sub-shaped. The rest are request/response or bulk transfer and belong on REST: -Four 🟠 Major boundary findings from the PR #29 review are real but each is its own scoped change, not a tail on the ring branch. The Critical sibling (a `cpl<3` overflow guard in the MoonLive effect's `tick`) landed with the branch it was found on. +- **`/api/control/set`** ← `{"module","control","value"}` (the exact JSON `/api/control` takes) → `applySetControl`. The real win: any control on any module — the effect/layout/modifier/palette reach HomeKit can't express. The apply-core already validates (range / read-only / module-not-found via `SetControlResult`), so safety is free. +- **`/api/state/get`** → publishes the `/api/state` JSON (retained). For dashboards (fps / heap / tick / current effect across a fleet), offline detection, and alerting — the monitoring half. -**One of the four is CLOSED (2026-09-07):** the scenario now uses `PreviewDriver`, the in-process -sink, instead of `NetworkSendDriver`. Its `tick_us` half was reviewed and DISMISSED rather than -fixed: a `measure` step asserts nothing, it records, and that recording is what feeds repo-health's -per-commit performance trend. A reviewer reading the file could not see that; deleting the -baselines would have blinded the trend to fix nothing. Recorded here so it is not re-raised. +Explicitly **out** (no practical MQTT case, and wrong for the transport): +- **`/api/file`, `/api/dir`, `/api/firmware/upload`** — kilobytes-to-megabytes of binary; MQTT is a small-message bus, REST already streams these correctly. +- **`/api/modules` add/delete/move/replace** — stateful, order-sensitive pipeline surgery a human does once in the UI; an automation reshaping the pipeline is an anti-pattern (the *Robust to any input* guarantee makes it *safe*, not *advisable*). +- **`/api/reboot`** — one narrow action; a single fleet-reboot topic is a mild nice-to-have, not "the API." +- **`/api/types`, `/json/*`, `/`, `/ws`, `/api/firmware/url`** — discovery metadata, the WLED shim, the UI itself, the socket: static, already-served-elsewhere, or a different protocol. -The three that remain: +**Shape:** ~15 lines routing into the existing `applySetControl` + a state publish; gate behind a `generic API` bool defaulting **off**, so a home user's broker isn't a remote-config backdoor and the curated HomeKit surface stays the clean default. **Don't** auto-generate a semantic topic per control — HomeKit/HA need stable typed topics; a generic `palette/set` whose meaning shifts per firmware breaks their discovery. Keep semantic topics hand-curated; let the generic pair be the escape hatch. -- **Core includes platform, compiled core in `mm_core`.** `src/core/moonlive/MoonLive.cpp` `#include`s `platform/platform.h` and calls the exec-memory API directly, and the root `CMakeLists.txt` compiles `MoonLive.cpp`/`MoonLiveCompiler.cpp` into `mm_core` and links `mm_core → mm_platform` — violating the header-only-core / no-platform-includes contract both files declare. The runtime exec-memory placement layer wants a core-neutral injected interface (or to move out of `src/core`), so the compiled/platform-dependent surface sits behind `mm_platform` and `mm_core` stays INTERFACE-only. These two are one change (same boundary). -- **W^X disabled in the board default.** `esp32/sdkconfig.defaults.esp32s3-n16r8` turns off `CONFIG_ESP_SYSTEM_MEMPROT_FEATURE` and enables `CONFIG_HEAP_HAS_EXEC_HEAP` for *every* build on that board, even with no MoonLive effect installed. The JIT genuinely needs a writable-then-executable heap, but that belongs in a dedicated MoonLive/JIT opt-in overlay or an explicit build profile, not the board default — so a stock build keeps memory protection on. -## MoonI80 prime-only ring: no stall backstop (sibling-path gap) +**Open question — Homebridge example maps both `setBrightness` and `setHSV`.** In `homebridge-mqttthing`'s `lightbulb`, HSV's *value* (V) already carries HomeKit's Brightness characteristic, so mapping both topic pairs may double-drive brightness (mqttthing's docs lean toward using one or the other). The [MQTT § Homebridge example](../../moonmodules/core/system.md#mqtt) currently lists both, to show the device's full topic surface. Resolve on hardware: flash a board, run Homebridge + mqttthing with that config, and check whether the Home-app brightness slider misbehaves — if it does, drop the two `brightness` topics from the example; if not, it's a non-issue. (Flagged by CodeRabbit; parked here rather than changing the doc on an untested hunch.) -**Found:** 👾 Reviewer, pre-commit on the whole-frame stall fix (2026-07-22). +### HA update entity via MQTT discovery: release check -The MoonI80 wait-timeout backstop (`moonI80Ws2812Wait`, `platform_esp32_moon_i80.cpp`) now recovers a lost/coalesced EOF on **two** of the driver's three completion paths: the lapping ring (`nSlices > ringBufs`, oracle-gated) and the whole-frame path (`!isRing`). The **prime-only ring** (`isRing && nSlices <= ringBufs`) falls through both conditions, so a lost terminator-EOF there leaves `busy` stuck true with no recovery — and every prime/arm/transmit-ring path refuses under `busy`, the same permanent-wedge class the whole-frame fix just closed. +Kind: Feature · Stage: Integrate -Mitigated in practice: prime-only fires exactly one EOF per frame (no intra-frame coalescing), so the lost-EOF trigger is far rarer than on the whole-frame or lapping paths. But it is the same defect, and per the CLAUDE.md sibling-path rule (a cross-cutting recovery that core owns for one path should cover the sibling path, not be re-implemented per case) the backstop should extend to it rather than leave a third path uncovered. +**Still to build: device-side release check** (the update entity itself is shipped, [MQTT](../../moonmodules/core/system.md#mqtt)). `installed_version` and `latest_version` are equal today (both `MM_VERSION`), so HA renders the entity as up-to-date and the Install button is disabled. What's missing is a periodic poll of `https://api.github.com/repos/MoonModules/MoonLight/releases/latest`: at boot (~30 s post-network-up) and every 24 h thereafter, fetch the release JSON, extract `tag_name`, and if newer than `MM_VERSION` call `MqttModule::publishUpdateState()` with the updated `latest_version` (the publish path is already there: only the caller is missing). ~2 KB per check. -**Fix:** widen the backstop's condition so a stuck prime-only ring finalizes too — likely a single "any ring frame whose wire time has elapsed with `busy` still set" oracle that subsumes both ring branches, calling the shared `finalizeStalledTransfer`. Verify on the expander wall (prime-only = the small-strand ring config), since the ring recovery is not desktop-testable. +Add a `ReleaseCheckModule` (or a small extension inside NetworkModule / FirmwareUpdateModule — decide when building) that owns the poll timer + last-seen version cache and calls into `MqttModule::publishUpdateState()`. ~50 additional lines. -**Related (same file, same wall dependency):** the whole-frame backstop stops the hardware before draining the FIFO and the EOF ISR drops a firing against an empty FIFO, which rejects a late EOF that arrives after recovery. A tighter guarantee — an ISR that has *already passed* the empty-FIFO guard cannot then clear a freshly-recovered `busy` or give `wireFree` for the next transfer — would need explicit serialization (disable the EOF interrupt across finalize+rearm, or an explicit recovery/rearm state the ISR checks). The window is extremely narrow (an ISR in-flight at the instant of finalize) and stopping GDMA+LCD first already disables the source; the hardened version is an ISR-concurrency change that must be proven on the wall, not landed blind. +## NetworkModule -## ESP32-P4 400 MHz on rev-3+ silicon +### The access point never blocks a WiFi retry (2026-10-09) -**Found:** bench, 2026-07-25 (Quindor Discord question about the P4's 400 MHz). +Kind: Bug -The P4 build runs at **360 MHz** because IDF's `Kconfig.cpu` caps a `SELECTS_REV_LESS_V3` build (which we set, so a stock binary boots on the v0.x/v1.x P4 chips in the field) at 360; 400 MHz is IDF's default only for rev ≥ 3. Forcing 400 on our bench P4 (a **rev v1.3** chip) was tried and **bootloops** — `assert failed: esp_clk_init clk.c:105` — so 400 is a genuine hardware limit on pre-rev-3 silicon, not marginal stability. The ~11% compute headroom is left on the table for rev-3+ owners. +After losing its network, a device opens its access point and retries its known networks every minute (`retryKnown` in `NetworkModule.h`), but skips the retry while a client is on the access point. +A phone that stays on it, or rejoins it by itself, then keeps the device off its network indefinitely. +WiFi has preference, so the retry runs whoever is on the access point: drop the `wifiApClientCount() > 0` check. +- **The cost:** someone on the access point while a known network exists is interrupted for a few seconds once a minute; a first setup has no known network, so it never retries. +- **A test:** a client on the access point, the known network back, and the device joins it within the retry interval. -**Two ways to reach it, both deferred until rev-3 P4 hardware is on the bench to validate:** -- **A separate `esp32p4-400` variant** — `sdkconfig` with `ESP32P4_REV_MIN` = rev 3 + `CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_400`, so IDF's normal boot-time clock init sets 400 and the bootloader refuses to run on a pre-rev-3 chip (safe). The web installer offers it only to rev-3+ boards. This is the MoonLight-idiomatic per-variant path. -- **A runtime bump** — keep the 360 build (boots everywhere) and, at `app_main`, read `esp_chip_info().revision` and if ≥ 300 call `rtc_clk_cpu_freq_mhz_to_config(400)` + `rtc_clk_cpu_freq_set_config`. One binary, self-selecting. Riskier: a runtime CPU-PLL change on the P4 couples to the flash/PSRAM clock trees configured for 360, so it needs validation on real rev-3 hardware before shipping. +**Some devices do not rejoin their WiFi** (2026-10-09): now and then a device stays off its network after the router came back. This block is one suspect; the other is the path for losing the network while connected, where the WiFi driver can get stuck and need the radio restarted. +Next time one hangs, capture before touching it: +- **The Network card's status:** "No network", "AP: MM-…" or "WiFi STA: …" says which state it is stuck in. +- **The access point's `clients`**, which says whether a phone is holding it. +- **The serial log**, through `monitor_esp32.py`, whose `NetworkModule:` lines say what it tried last. -Neither ships until a rev-3 P4 can prove 400 runs clean — no untested clock config, per the same rule the S31/320 and this P4/400 bootloop both taught. +### Refresh a multicast membership on a fixed pace (2026-10-09) -## ESP32-P4: esp-dsp's assembly FFT and the hardware-loop erratum +Kind: Bug -Tracked in [Issues in other projects](../../reference/issues-in-other-projects.md) as esp-idf #19025 and esp-dsp #119 and #102, with the FFT and HLS soak to run once esp-dsp's alignment release ships. +A socket in a multicast group joins again after 3 seconds of silence, which repairs a router that forgets a member, with a gap of about 2 seconds each time. +The GL.iNet Slate AX forgets after about 18 seconds, so audio on the StadBeest drops every 20.6 seconds, measured on the bulb. +The standard fix is the router's multicast querier; this item is for a network that cannot be configured, such as a venue's. +- **The rule:** join again 10 seconds after the last join as well, so a membership is renewed before such a router forgets it. +- **The cost:** a leave and a join, two IGMP packets of about 60 bytes per socket every 10 seconds, and at most one lost packet in the moment between them. +- **Where:** `UdpSocket::keepMembership` in `platform.h`, one more condition, and a test beside the silence re-join's. -## Flaky unit tests: the AudioService sync suite contends on a fixed UDP port +### Remove the 6.0 network adoption (2026-10-04) -**Found:** 2026-07-27, caught by `premerge.py`; pinned to the exact cases by looping the suite and keeping the failing logs. Fails roughly **1 run in 10**. +Kind: Restructure · Trigger: once a release has carried it -Two cases in `test/unit/core/unit_AudioService_sync.cpp` fail intermittently, both around the socket rather than the audio logic: +`NetworkModule::adoptLegacySettings` and the two `adoptLegacy` it calls move 6.0's top-level network keys onto the Ethernet and WiFi cards at the first boot, so an in-place update keeps its network. +Remove all three, their test `unit_NetworkModule_legacy.cpp`, and the "update to this release before a later one" line in MIGRATING's WiFi and Ethernet entries, once a release has carried it and the field has moved past 6.0. -- `:125` *"…drives frame_, then holds it and reports listening"* — `CHECK(strcmp(status(a), "listening") == 0)`. The receive path **binds** the fixed port `kTestSyncPort` (21988); when a previous run's socket is still in `TIME_WAIT` the bind fails and the service never reaches "listening". -- `:95` *"Local+send: lazy-opens once and reports sending"* — `CHECK(strcmp(status(a), "sending") == 0)` plus `syncOpenForTest()`. This one **connects** to a broadcast address rather than binding, so it is a *different* failure mode on the same suite. +### Some devices stop answering mDNS over WiFi (2026-10-08) -**An attempted fix that did not hold (recorded so it is not retried blind).** Deriving the port per run (from the steady clock) instead of the fixed constant cut the receive-path failures but left the send-path case failing ~3 in 20 — because that case never binds the test port, so the port was never its problem. The real fix has to address the send path's socket open on its own terms, not just port choice. +Kind: Bug -**Why it matters:** the gate scripts run the suite on every commit and merge, so a 1-in-10 flake interrupts gate lists often enough to train the reader to re-run instead of investigate — the habit that hides a real regression later. +**Found** on the StadBeest: after the legs restarted, their MIDI service could not resolve the bridge's `MM-MIDI-rtp.local` for five minutes, and by address it connected at once. +Browsing from a Mac on the same network listed `_http._tcp` for MM-Eye2 alone: the legs, the left eye and the bridge announced nothing, and no `_apple-midi._udp` record appeared. +The left eye announces nothing new, so the cause is wider than the RTP-MIDI announcement. +The WLED native app finds devices by browsing `_wled._tcp`, so it is the likely reason it does not always list every device. -**Fix (do not paper over it):** treat the two cases separately. For the receive path, bind an **ephemeral** port (bind 0, read the assigned port back) so two runs can never contend. For the send path, first determine *why* the lazy open fails under repetition: whether the broadcast connect is refused or the latch is left set by an earlier case in the same process. A retry wrapper or a sleep is explicitly the wrong fix: it hides the next genuine failure too. `unit_NetworkReceiveEffect*` (a real UDP round trip on localhost) and `unit_SineEffect` (timing) flake the same way, about 1 full run in 3, and deserve the same treatment while the fix is fresh. +**The fix to build:** find why a device stops answering, starting from what differs between MM-Eye2 and the others: WiFi power save dropping multicast between beacons, transmit power (the bridge runs at 8), the responder losing its interface after a reconnect, or the announcement order in `mdnsInit`. +Watch it with a working `dns-sd -B` from a computer and the device's serial log, and confirm the bridge's `_apple-midi._udp` record appears. +Pinned on the bench: every device answering its `.local` name and listed in the WLED app after a restart and after a WiFi reconnect. -## ModuleFactory: registration can fail silently (uint8_t ceiling, unchecked bool) +### Advanced network settings (2026-10-03) -**Found:** 2026-07-27, while adding the effects grid sweep; flagged by the 👾 Reviewer. +Kind: Feature -`ModuleFactory::registerType` returns `bool` and **appends unconditionally** — it does not dedupe by name, so the same type registered from several places takes several slots (in the test binary `Layer` holds 9 and `RainbowEffect` 8). `count_` is a `uint8_t`, and at the 255 ceiling `grow()` returns false, registration fails, and the return value is discarded at every call site — including the static-init registrars, which cannot check it anyway. The failure is silent: the type is simply absent from `/api/types` and `create()` answers `nullptr` for it, far from the cause. +Left out of the plan that makes Ethernet, WiFi and the access point Network submodules, and expected to be asked for: -Production is not near the ceiling today (~90 types), so this is not urgent. It became visible because a test binary that registers repeatedly can reach it, which is what made the grid sweep construct effects directly instead of registering them. +- **Roaming** between access points of one network while connected, by signal threshold (802.11k/v/r where the chip supports it). +- **Enterprise WiFi**: WPA2/WPA3-Enterprise with a username, a password or a certificate. +- **Pinning a network to one access point** (BSSID) or one band. +- **WiFi power save**: the modem-sleep mode, traded against latency for Art-Net and the UI. +- **The country code**, set by hand where adopting the router's country is not enough. +- **The access point's address**, fixed at 4.3.2.1. +- **IPv6 beyond link-local**: global addresses and a static one; the station and Ethernet carry a link-local address, which mDNS advertises. -**Options, cheapest first:** (a) make `registerType` idempotent — ignore a duplicate name and return true, which fixes the multiplier and is arguably the correct semantic anyway; (b) widen `count_`/`capacity_` to `uint16_t` (a handful of bytes, removes the ceiling as a practical concern); (c) make a failed registration loud in a debug build (assert or a boot-time log) so it can never be silent again. (a) plus (c) is probably the right pair. +### NetworkModule treats a panel-output NIC as the device's uplink (2026-09-28) -## Network-receive throughput starves HTTP (issue #69) +Kind: Bug · Platform: desktop -**Found:** 2026-08-20, reproducing issue #69 on MM-testbench-S3 (ESP32-S3, 240 MHz, v6.1-rc1). +A NIC claimed for raw L2 panel output carries no LAN, but NetworkModule enumerates it anyway and reports `Connected via Ethernet` on it. When the panel link renegotiates, every flap runs the full cascade, which drops the browser's WebSocket and makes the UI look dead while the server is healthy. Seven flaps were logged in one bench session. The driver already announces itself through `ethClaimRawL2(true)`, so the claim is there to honor; the change is for NetworkModule to skip a claimed adapter. Crosses two modules, so it wants a decision rather than a patch. -Reported as "DDP gets very stuttery once the led count gets over a few thousand, even with no led drivers, and the same stream to WLED plays smoothly". Reproduced, and the threshold matches: HTTP availability while a DDP source runs, sampled 6 requests per point. +### The persisted interface label carries the negotiated link speed (2026-09-28) -| lights | pkt/s | KB/s | HTTP | -|---:|---:|---:|---:| -| 480 | 30 | 44 | 6/6 | -| 1440 | 90 | 131 | 6/6 | -| 2880 | 180 | 262 | 6/6 | -| 4800 | 300 | 437 | 3/6 | -| 8160 | 510 | 743 | 0/6 | -| 12288 | 780 | 1136 | 0/6 | +Kind: Bug · Platform: desktop -Fully reversible: 8/8 before, 1/8 during, 8/8 after. Art-Net degrades the same way and, per light, sooner — it carries 170 lights/packet against DDP's 480. +PanelCardDriver saves its adapter as `Realtek USB GbE Family Controller, 1 Gb`, speed included. A renegotiation to 100 Mbit rewrites that string, which dirties the config and rewrites `Drivers.json`. Matching already strips the suffix and compares the stable head, so the binding survives, but an identifier that changes when a cable renegotiates is the wrong thing to persist. Store the adapter's stable identity and render the speed for display only. -**The limit is throughput, not packet rate.** Art-Net survived 510 pkt/s where DDP failed at 300, but both broke at ~440-480 KB/s. A matched-throughput control (~740 KB/s) gave 3/6 for DDP at 510 pkt/s and 2/6 for Art-Net at 1350 pkt/s — 2.6x the packet rate, same result. So a deeper `LWIP_UDP_RECVMBOX_SIZE` (6 today) is not the lever it looks like; the bytes have to be moved regardless of how they are grouped. +## PinsModule -**Not the render loop either.** MM-S31 at 4096 lights held 6/6 all the way to 1966 KB/s while ticking at 12 fps (82 ms), against the S3's 8.3 ms tick. If the once-per-tick drain in `NetworkReceiveEffect::tick()` were the constraint, the board with the 10x slower tick would fail first. It is the fastest board that fails, which points at contention between the receive path and the HTTP task rather than at drain cadence. +### PinsModule: strict reject-on-add mode -**Partly fixed 2026-08-20** (`NetworkReceiveEffect`, this branch). The staging→layer `memcpy` ran every tick whether or not a packet had arrived; the Layer does not clear the buffer between frames, so that copy was writing identical bytes. Guarding it on a `dirty_` flag cut the effect's idle tick from **3522 us to ~240 us** at 12288 lights (93%), measured on MM-testbench-S3. HTTP availability under load, same board, before → after: +Kind: Feature -| lights | pkt/s | DDP before | DDP after | -|---:|---:|---:|---:| -| 4800 | 300 | 3/6 | 6/6 | -| 8160 | 510 | 0/6 | 5/6 | -| 12288 | 780 | 0/6 | 2/6 | +[PinsModule](../../moonmodules/core/system.md) is shipped; [PinsModule.h](../../../src/core/system/PinsModule.h) records the design. The soft-flag choice obviated the reassignment broker (a live pin swap already works: set A→B's pin, then B→A's, the transient conflict clears). -Art-Net gains the same way (4800 lights 3/6 → 6/6; 1350 pkt/s 2/6 → 4/6) — the fix is on the shared staging path, not per protocol. The reported threshold ("a few thousand") is now clean; 12288 lights at 780 pkt/s still degrades. +The one still-open item is an **optional strict reject-on-add mode** for the installer/catalog path: a "clean tree" that *refuses* a module add whose pins collide, rather than accepting it soft-flagged. This is add-path *validation* (a different feature than the live soft-flag reassignment), wanted only if a stricter installer UX is desired. Spec + `/plan` if picked; the soft-flag default stays for live editing. -**Remaining work — measurement, not a fix:** find where the rest of the bytes cost. Candidates are the `recvfrom` copy out of lwIP plus the staging→layer `memcpy` (both O(bytes/s), both on the render thread), and core-0 contention between the lwIP task and HTTP — the LC16 Ethernet starvation entry is the same shape. `handleConnection` running synchronously in `tick20ms` with ~5 ms/~50 ms budgets is the likely victim. -Related: WLED is smooth on the same stream because it receives via `AsyncUDP` — packets are consumed in a callback from the lwIP task the instant they arrive, rather than polled once per render tick. Moving to that model is the structural fix, and it needs `staging_` synchronized against the render thread. +Why soft-flag is the default, and what strict mode must not break: a refusal on add wedges a legitimate live swap, since two drivers exchanging pins pass through a transient double claim, while a claim that lands and flags red never wedges, which is the degrade-visibly rule. Strict mode is for the installer and catalog path only, where a tree is built from scratch and no swap is in flight. It wants an explicit shared-pin opt-out for the one legitimate case, two consumers reading one input, as ESPHome's `allow_other_uses` does. +### A driven GPIO the Pins map never sees: bus padding, and a hidden clockPin -## MIDI as a control surface, and the OpenLamp convention (2026-09-22) +Kind: Bug -[ControlSurface](../../../src/core/util/ControlSurface.h) was written for MIDI hardware, and [MidiService](../../moonmodules/core/services.md#midi) is its MIDI transport beside OSC: it drives the surface from Mackie Control desks and the APC40 mkII, over the browser's Web MIDI, a board's USB port or RTP-MIDI, and sends the way back through the surface's verbs. What stays open is a controller with no profile: what its notes and CCs mean, which the OpenLamp convention below proposes, and MIDI learn, the item after it. +**Found:** 2026-08-21, on MM-S31, after a bench session that started as "the LED panel stopped working" and cost hours chasing a firmware regression that did not exist. -**What OpenLamp offers.** [openlamp-spec-midi](https://github.com/openlamp/openlamp-spec-midi) is an MIT-licensed convention for driving WLED over MIDI: notes 59-68 for hues plus black and white, notes 48-56 for off, on, toggle and blackout, CC 1 for brightness, CC 3-4 for hue and saturation, CC 5-8 for effect, speed, intensity and palette, Program Change for presets, MIDI channel for targeting, and MIDI clock for beat sync. The [organization](https://github.com/openlamp) also has an Ableton Link and MIDI-clock tempo library, and a CC0 asset set of 72 palette illustrations and 216 effect previews in eight languages. +An ESP32-S31 driving a ColorLight receiver card over raw Ethernet showed the panel dark while every diagnostic said the transmit path was healthy: link negotiated at 1000 Mbit, ~4600 packets/s, zero drops, and a byte-for-byte dump of a 128x128 frame matched `ColorLight5A75Packet.h` exactly. The card's own activity LED never blinked. Four firmware versions across two ESP-IDF releases behaved identically. -**Why the convention matters more than the code.** Its engine is Python and cloud-free by design, so nothing there ports to a device. The value is in agreeing what a note and a CC *mean*, since a MIDI transport has to answer that whatever we do, and answering it the same way as a project already aimed at WLED costs nothing and buys a user their existing mappings. The palette and effect artwork is CC0 and separately interesting for the catalog, which today has one screenshot per module and no palette illustrations at all. +The cause is a **GPIO collision that no part of the system could report**. `ParallelLed` carried `clockPin = 10`, and GPIO 10 is `txd2` on the S31's RGMII bus (`platform_esp32.cpp`: the EMAC's fixed IO_MUX pads are 8-19 plus MDC/MDIO on 5/6). The LED driver drove one of the four Ethernet transmit data lines, so every frame left the MAC counted-as-sent and arrived corrupt. Disabling the LED drivers fixed it instantly; moving the clock to GPIO 21 fixed it with all four drivers running. -**Judge it against principle 2** before adopting: the standard construct beats a bespoke one, and this is a candidate standard. The caution is that the spec says plainly it is a draft, "likely to change, and early on to change quickly", and the organization has low single-digit stars. So the sequence is a MIDI transport shaped by our own `ControlSurface` first, with the OpenLamp note and CC numbers as the default mapping where they fit, rather than a port of their model. Their beat-sync library is worth reading against [the audio work](backlog-light.md), since MIDI clock is a tempo source we do not have. +One defect remains, the other two being fixed ([lessons](../past/lessons.md#esp32-s31-rgmii-ethernet-bring-up-four-bugs-between-code-compiles-and-link-up)): -**What it is not.** Not a replacement for OSC, which carries labels and arbitrary addresses that MIDI cannot. Not a lighting-control protocol in the DMX or Art-Net sense. This is about a musician's controller driving the show. +**`clockPin` defaults to 10 and is hidden.** `MoonLedDriver::clockPin = 10` is a hardcoded default that lands inside the S31's reserved RGMII block, and `addBusControls` hides the control unless `pinExpanderMode()` is on. So on this board the value was invisible on the card, unchangeable through the UI, and still driving a pad. A pin with a real effect must be visible, whatever mode it is in. The first sighting was on an S3 (2026-07-12): an `i80` lane list `18,5,6,7,8,9,10,11` overlapped the hidden `clockPin` 10 and `dcPin` 11 defaults, so two lanes carried the bus clock and DC toggling instead of pixel data, and nothing flagged it. -Build trigger: someone with a MIDI controller who wants it to drive a device, or the DMX work reaching a point where a tempo source is the missing piece. +### A bus pin that belongs to another peripheral is accepted -### MIDI learn: any note or CC onto any control (robwomp, 2026-10-12) +Kind: Bug -The MIDI service decodes two known layouts, Mackie Control and an Akai APC40 mkII profile, so a controller sending plain notes or CCs outside them drives nothing: a user with a generic controller on Linux got no note or CC data in. The infrared and gamepad services already map an arbitrary input onto any control with `learn` rows, and MIDI gets the same rows: each one's `learn` arm captures the next note or CC with its channel, a target names the control, and the value scales into the target's range (a CC's 0 to 127, a note's velocity, or a note as a toggle). That is MIDI learn as consoles and DAWs offer it, through the same primitive, so one mechanism maps every input. The two profiles stay for the desks whose layout they describe, since they carry the way back (motor faders, knob rings, lit pads); a learned row has none unless the controller echoes its CCs. +- **A bus pin that belongs to another peripheral is accepted, and takes the board off the + network** (2026-09-06). An Olimex ESP32-Gateway given a WR/DC pin on its Ethernet MDIO/MDC + (18/23) or its PHY reset line (5) lost its Ethernet link within seconds and looked crashed + (the firmware kept ticking on serial; it stayed reachable only through its WiFi fallback). + The pin registry already + detects a double claim (`PinsModule::flagConflicts`) but only colors the edge, so a control + write that lands on a GPIO another control owns should be refused with a status naming the + owner, the way a reserved flash pin already is. A collision with a network pin is worse than + most: it ends the session that could have fixed it, and the SMI pins stay re-muxed until a + reboot even after the driver releases them. -Build trigger: met, a generic controller that cannot reach MoonLight today. +## Scheduler -### Notes as an effect input, which is the other half +### Effects and Drivers find their source one way, not two (2026-10-08) -Routing a keyboard through a device so **each note triggers something inside an effect** is a different feature from the surface above, and the difference is worth stating before either is built. A control surface *sets controls*: a fader moves `brightness`, a pad selects a preset, and the effect never knows a surface exists. Notes are **performance data**: an effect reads them the way it reads audio, so what a note does is the effect's decision rather than a mapping table's. +Kind: Restructure -The architecture already has the shape this needs, which is why it belongs here rather than as a new concept. [AudioService](../../moonmodules/core/services.md#audio) publishes an [AudioFrame](../../../src/core/util/AudioFrame.h) each block, effects read it through `latestFrame()`, and MoonLive scripts reach the same values through builtins such as `level` and `bands`. `AudioFrame::onset` is already "a hit happened this block", which is exactly a note-on without a pitch. A `MidiService` publishing a `MidiFrame` alongside it would need no new plumbing: the same publish-and-read rule, one more producer. +Effects finds the top-level Layouts, and Drivers the top-level Effects, by type through `Scheduler::topOfType` when nothing was injected, which is what lets a state document build a tree with no wiring. +`setLayouts` and `setEffects` remain beside it for the unit-test rigs, 171 calls in 74 files, so two mechanisms do one job. +The fix: a rig adds its containers to a Scheduler, as the scenario runner does through `createTopLevel`, and the setters go. -What a `MidiFrame` plausibly carries: which notes are held and how hard, which arrived this tick and which left, the pitch bend and modulation positions, and the clock's beat phase. Held notes are the interesting part, because that is what audio cannot give an effect. An effect can then light a pixel per key, place a particle at a pitch, colour by velocity, or hold a shape while a chord is held and release it on note-off, none of which is expressible as "a control moved". +### A script's setControl rebuilds a control subtree on every write (2026-09-06) -The open design questions, all cheap to answer badly and expensive to redo: whether a note is a level that decays or an event that fires once, how polyphony maps onto a grid without the effect hardcoding a keyboard's range, whether an effect opts in (as audio effects do, where `hasAudio` gates them) or every effect sees the frame, and what happens on a device with no MIDI attached. The audio path answered all four already, so the honest first step is to read how it did and follow it rather than invent a parallel set of answers. +Kind: Bug -**The relationship to OpenLamp:** their spec maps notes to *fixed meanings* (note 59 is a hue, note 48 is off), which is the control-surface half. Notes as performance data is the opposite: the note has no meaning until an effect gives it one. Both can share one MIDI transport, and they want different things from it, so the transport should deliver raw messages and let each consumer interpret, rather than translating to lamp actions on the way in. +Measured on the P4 at .139: **2 fps**, with `MoonLive-2` at 251 ms and `MoonLive-3` at 245 ms per +tick, together 498 ms of a 506 ms frame, and HTTP down to a 0.5 s round trip because it is served +from the same loop. Both services ran `sweep.mls`, whose `tick20ms` writes four faders. Two copies +at 50 Hz is 400 control writes a second, and `Scheduler::setControl` calls `rebuildControls()` +unconditionally on each one, so each fader write tears down and re-creates the whole `Control` subtree. A person moving a slider +does this a few times a second and nobody notices; a script at 50 Hz multiplies it by hundreds. -Build trigger: the same MIDI transport as above, since both halves need it and neither is worth building it alone. +Two things to fix, and they are independent. **The rebuild should be conditional**: a `live` +control's value change cannot alter the schema, and `rebuildControls` already computes +`schemaSignature()` before and after to decide whether to notify, so it knows. Rebuilding only when +the shape can actually change (what `setLive` and `affectsPrepare` already distinguish) removes the +cost for every value write, scripted or human. **And the script tick is too fast for its job**: a +fader sweep does not need 50 Hz, so `sweep.mls` should run on a slower tick, which also caps the +damage any future script can do through this path. -### The rest of the band: drums, guitar, voice +Not a regression from the RMT work: it predates it and was found while measuring an unrelated slow +board. -A keyboard is the easy instrument, because MIDI hands us the notes already decided. Every other instrument in a live setting arrives as **audio**, and the work is deciding what happened in it. That splits into three paths with very different costs, and conflating them is how this becomes a project with no end. +## Across modules -**Path one: the instrument already speaks MIDI, or can.** Electronic drums are MIDI over a cable, so each pad is a note and the work is zero beyond the transport above. A guitar reaches MIDI through a hex pickup or a converter pedal, a voice through a pitch-to-MIDI box. In every case the hard problem sits in a device the musician already owns and has already paid for accuracy on. **This is the path that reaches the most instruments for the least code**, and it argues for building the MIDI transport before anything else here. +### A Progress total rides the value patch (2026-10-19) -**Path two: one instrument on its own channel.** A live rig already splits the band across a mixer's channels, so a direct out or an aux send gives one input carrying one instrument. That is a source-seam question, and [the audio roadmap](audio-dsp-roadmap.md) already covers widening the seam with line-in, codecs and multi-channel front-ends. What it does not cover is the analysis, which is the real work: +Kind: Feature -- **Drums**: our single `onset` fires on any hit anywhere in the spectrum, so a kick and a snare are indistinguishable. Per-band onsets separate them cheaply, since a kick lives in the low bands and a snare in the mids and highs, and the bands are already computed. This is the smallest useful step and it needs no new hardware. -- **Guitar and bass**: `peakHz` gives a dominant frequency, which tracks a single note and falls apart on a chord. Real polyphonic transcription is a research problem and out of scope; monophonic pitch tracking on a bass or a lead line is not, and the existing peak is most of it. -- **Voice**: a pitch track plus a loudness envelope is achievable and genuinely useful for lighting a vocal line. Recognising words is not, and nothing in a live show needs it. +A Progress control's total travels only in `writeControlMetadata` (Control.cpp), so a changed total needs a schema resync, which `rebuildControls()` triggers because `aux` is in the schema hash. +One line in `writeControlValue` and one in `app.js`'s patch handler would let the total ride the value, and the hash could then skip it, so a track change in FseqPlayerEffect stops resyncing every browser. +Pin it with a test that changes the total, patches, and finds the browser-side max moved with no resync. -**Path three: the whole mix through one microphone.** What the device does today. Source separation to pull a drummer out of a mixed stereo field is a machine-learning problem that does not fit a microcontroller, and pretending otherwise would be the kind of speculative build principle 1 rejects. The honest ceiling here is what the band structure already gives: bass energy, spectral flux, a tempo estimate. +### What disable means: release the memory, or keep the module hot (2026-10-20) -**The one unifying idea worth keeping.** Whatever the source, the useful output is the same shape: *this instrument did this thing, this hard, just now*. A MIDI note-on is that. A per-band onset is that. A pitch track is that with a pitch attached. So the frames stay separate at the source (`AudioFrame` and a `MidiFrame`) and an effect reads whichever it wants, rather than one merged everything-frame that forces every producer to pretend it is the others. That keeps each producer honest about what it actually knows. +Kind: Investigation -**The order this argues for**, cheapest and most certain first: the MIDI transport (which reaches electronic drums, MIDI guitars and pitch-to-MIDI voice with no analysis at all), then per-band onsets (drums from a channel, and a better beat for everything), then monophonic pitch (bass and vocal lines), and only then anything about separating a mixed signal. +A disabled module keeps its settings only ([coding-standards](../../contributing/coding-standards.md)), and deleting a module already releases everything, so the question is whether disable should release too or keep the module hot for an instant resume. The contract test (`unit_ModuleContract.cpp`) lists the modules that keep runtime memory while disabled, Layer and Drivers with their pixel buffers the largest, and fixing them would settle the answer, so they wait for it. The analysis weighs: presets and DemoReel switching many modules, where kept memory grows with what is added rather than what runs; a quick toggle, where hot means an instant resume; the measured re-enable cost of every module, its `prepare()` time, gathered with the contract test's placement; three shapes, release on disable, keep everything hot until delete, or keep the settings and what is slow to rebuild while releasing the large buffers; and how comparable systems treat a disabled or paused element. The product owner decides, and the rule in coding-standards, the contract's lists and the Layer and Drivers fix follow. -Build trigger: a live rig to test against. Every step is measurable in an evening with an instrument in the room, and none of it is worth guessing at without one. +### Playlist in core: one sequencer for FSEQ, effects and DemoReel (2026-10-19) -### Everything as MIDI, and where the conversion should live +Kind: Restructure -The appealing shape is one where every instrument on stage arrives as MIDI: the drums, the guitar and the voice each converted, so the device reads one kind of event and an effect never asks where a hit came from. It is the right instinct, and the question it turns into is **where the conversion runs**, because that decides whether this is a weekend or a research project. +A playlist is a list of items with an optional duration each, played in order or shuffled once per pass, repeated off, one or all, under play, pause, stop, previous and next: the media player's model. Two copies of that sequencing exist: `DemoReelEffect::advance()` cycles effects, and `FseqPlayerEffect` cycles files with its own cursor, shuffle pass and loop mode, written as the same shape. An effects playlist would be the third, so it starts by putting the model in core once, a `Playlist` over core's editable `ListSource` whose owner supplies how to play item *i* and when it ends: the FSEQ player's items are files that end with the track, an effects playlist's are presets applied through the state-document engine that end after their duration, as the presets plan foresees, and DemoReel becomes an effects playlist of effects. Preset looping, rotating between a first and last [preset](../../reference/presets.md) slot on a timer, is an effects playlist whose items are presets. The change that builds the effects playlist moves FSEQ's and DemoReel's sequencing into it. -**Converted off the device, it is available today.** Drum triggers, hex pickups and pitch-to-MIDI pedals all exist, are well made, and put the hard analysis in hardware the musician already trusts. A mixer's direct outs feed those boxes, their MIDI merges onto one cable, and the device reads notes. **Nothing here needs writing beyond the MIDI transport.** That is the version worth having first, and it is genuinely what a working stage rig already looks like. +### A device without a deviceModel is not told its catalog half is missing (bench, 2026-08-26) -**Converted on the device, each instrument is a different problem.** Calling them all "X to MIDI" hides that the three have nothing in common technically. A drum channel is an onset detector, which we nearly have. A bass line is monophonic pitch tracking, which is a known algorithm and real work. A voice is pitch plus envelope, doable. A strummed guitar chord is polyphonic transcription, which is a research problem that does not fit a microcontroller and should be named as out of scope rather than left as an implied maybe. +Kind: Bug -**The cost that bounds it.** One mono audio input measures about **8 ms per tick on a P4**, already the second-largest consumer on the board after the effect itself. Four instruments analysed on-device is four FFTs and four detectors, which is most of a frame budget before a single light is drawn. So the ceiling is not the algorithms, it is the arithmetic: **one or two analysed channels on a device, not a band.** A mixer feeding eight direct outs into one ESP32 is not a design this hardware supports, and saying so early is cheaper than discovering it late. +A board ran for days with `deviceModel` empty and `ethType` 0: Improv and access-point provisioning restore the WiFi credentials only, and nothing tells the user the catalog half (Ethernet wiring, per-board settings) is missing. Candidates: MoonDeck re-pushes the catalog on discovering a device whose deviceModel is empty, or the UI badges the state. -**Where a mixer does fit.** A direct out or aux send giving **one clean instrument** is exactly the input that makes path two work, and it is worth more than any algorithm: a kick drum alone on a channel needs a threshold rather than a separator. So the mixer plug-in is valuable for a *small* number of channels, which is also the number the frame budget allows. The seam for that is the multi-channel front-end [the audio roadmap](audio-dsp-roadmap.md) already anticipates. +### One board-preset helper for Ethernet and the SD card (2026-10-12) -**The architectural conclusion.** Rather than converting everything to MIDI on the way in, keep the frames separate and let an effect read what each producer honestly knows: a `MidiFrame` for what arrived as notes, an `AudioFrame` per analysed channel for what arrived as sound. Forcing a per-band onset to pretend it is a note-on adds a fake pitch and a fake velocity, and an effect that reads them cannot tell which are real. Same rule as above, applied to the tempting case. +Kind: Restructure -Build trigger: a stage with more than one instrument to point at it. The first honest experiment is one drum channel through a direct out, because it answers the cost question and the usefulness question at the same time. +EthernetModule and SdCardService each carry the same board-preset machinery: the per-chip filtered option list, selection by label, the applied tracker, applying a preset when the selection moves, and reading the preset back off the pins. +A `core/module/BoardPresets.h` helper would own the options, index, count, selection and applied state, with the module supplying its table and two functions: write a preset into the pins, and match the pins against one. +Do it before a third preset table appears (a display's or a microphone's wiring), so that one becomes a table rather than a third copy. -## The Open Stage Control session's labels (2026-08-30) +### `ListSource` methods compile twice: ~8.6 KB of thunks (2026-10-01) -Found wiring a real control surface to the [OSC module](../../moonmodules/core/services.md). +Kind: Restructure -**The shipped Open Stage Control session renders no labels, and its pad matrix draws nothing.** -[`docs/reference/examples/open-stage-control.json`](../../reference/examples/open-stage-control.json) works for every fader, encoder and switch, but the widget names never appear and the `matrix` of pads is an empty box. -Five attempts at the label property failed (`@{}`, `JS{}`, `#{}` with `unit`, an explicit string, omitting it so `"auto"` applies), and notably a session SAVED by Open Stage Control itself carries no `label` key on a fader at all, so the name is drawn from something else. -The session was written by reading a minified app's bundled HTML docs; the reliable fix is to build one widget of each kind by hand in its editor, save, and copy the shape it produces. -Cosmetic: the control path works, only the labels and the pad grid are missing. +Every module that is `MoonModule` and `ListSource` gets its list methods emitted twice on ESP32: once as the method, once as a "non-virtual thunk" for the second base, and GCC copies the whole body into the thunk instead of a jump. +Measured on the S3 image with `xtensa-esp32s3-elf-nm -S`: 8.6 KB in all, the largest `GamepadService` 1.6 KB, `ButtonService` 1.3 KB, `FixtureProfilesModule` 1.2 KB, `ControlModule` 1.1 KB, `InfraredService` 0.9 KB, `AnalogService` 0.9 KB. +Candidates, to measure rather than assume: a `ListSource` that is a member the module hands out (no second base, so no thunk), or keeping the bodies out of line so the thunk can only jump. +Done when the thunk total in the S3 symbol table is near zero, with the delta in repo-health. -## POST /api/file reports success while writing an empty file (no Content-Length) +### An early state push costs 3 to 5 ms on the S3 (2026-10-01) -**Found:** 2026-08-20, on MM-testbench-S3, while uploading an edited MoonLive script. The upload -answered `{"ok":true}` twice while the board kept the previous file, and a compile result was read -from the stale script before the mismatch was noticed. +Kind: Investigation · Platform: S3 -`contentLen` defaults to **0** when the request carries no `Content-Length` -(`HttpServerModule.cpp:152`, "declared body length (0 if no Content-Length)"). `handleWriteFile` -then clamps the already-buffered body against it: +`notifyValuesChanged()` patches one module on the next 20 ms tick, at most 25 times a second; with a MIDI desk following a self-playing Pong it took the S3 from about 325 to 295 fps, 3 to 5 ms per push. +Building that patch walks one module, so the time is most likely the WebSocket write on the render thread, but the split is unmeasured: one write per frame instead of two saved nothing. +Time the build and the send separately on the S3, then decide between moving the write off the render thread and a lower cap. -```cpp -const size_t initial = initialLen < contentLen ? initialLen : contentLen; // → 0 -``` +### `applyControlValue` should say whether the value changed (2026-10-03) -so `fsWriteStream` streams nothing, succeeds, and the handler answers 200. The body on the socket is -discarded. Reproduced deliberately: an 11-byte file written with `Content-Length: 11` lands as 11 -bytes; the identical request sent `Transfer-Encoding: chunked` answers `{"ok":true}` and leaves a -**0-byte** file, destroying what was there. Any HTTP client that streams without declaring a length -hits this — `curl --data-binary` on a pipe, and chunked uploads generally. +Kind: Restructure -This is data loss reported as success, which is the part that matters: a caller has no way to know -the write did not happen, and the previous contents are already gone. +The boot reapply in `FilesystemModule::reapplyNode` detects a changed value by serializing each control before and after the apply and comparing the bytes, a list control included. +`applyControlValue` reports only whether the value parsed and applied. The proposed shape is the apply also reporting whether storage moved, an `ApplyResult::Unchanged` or a flag, so `Scheduler::setControl` can skip the reaction to a write that changes nothing. +Cold path today, so a tidy-up rather than a cost. -**Options:** (a) reject a body-bearing POST with no `Content-Length` (411 Length Required) — smallest, -honest, and standard; (b) support `Transfer-Encoding: chunked` in the upload source, which is more -work and only worth it if a real client needs it; (c) at minimum, never report 200 for a write whose -byte count does not match what was declared. (a) plus (c) is the pair worth doing. The UI's own -File Manager always sends a length, so this does not affect it — an API caller or a script does. +### Runtime board presets (multi-commit, partially landed) -Pin with a test that a length-less upload does not report success and does not truncate the target. +Kind: Feature -## MoonDeck scripts crash on Windows whenever their output is redirected (2026-08-22) +The firmware-vs-board separation, the runtime pin controls and the catalog that carries per-board defaults are shipped ([MoonInstaller](../../explanation/architecture/mooninstaller.md#the-three-words)). What remains: -62 of the ~64 scripts print `→ ✓ ⚠ —` or box-drawing characters. A REDIRECTED Windows stdout takes `locale.getpreferredencoding()`, cp1252, and the first such character raises UnicodeEncodeError, *after* the real work has succeeded: `collect_kpi.py` measures everything, writes the metrics, then dies printing the summary arrow. An ATTACHED console has been UTF-8 since Python 3.6 (PEP 528), measured on this bench: `utf-8 isatty=True` from a console against `cp1252` through a pipe. So the entry's original framing was backwards. A human at a Windows terminal is the one case that works, and every redirected path fails: a pipe, a tee, a CI log, an agent's captured output. The gate runner that handed children `PYTHONIOENCODING=utf-8` is gone, which is why this surfaces now. +**Prior art: the predecessor's per-board pin database** ([ModuleIO.h](https://github.com/ewowi/MoonLight/blob/main/src/MoonBase/Modules/ModuleIO.h)). The predecessor MoonLight (our own earlier project) already models exactly this for ~25 boards across ESP32-D0 / S3 / P4: a `pins[]` array of `{GPIO, usage, index}` plus board-level `maxPower`, `ethernetType`, `ethPhyAddr`, `ethClkMode`. Don't copy the file or paste its tables here: read it when building the catalog and write our own. Its `usage` enum enumerates the hardware functionalities a board preset here *could* drive; those still without a device-side consumer: -The READ side breaks too, and that half is worse because `PYTHONIOENCODING` does not mask it: `Path.read_text()` and `open()` without an encoding decode as cp1252. A sweep found 43 unqualified `read_text`/`write_text` plus 47 bare `open()` across `moondeck/`. +- **LED output pins**, a gap with two halves now the parallel drivers take 16 lanes: + - **Per-model usable-GPIO map (the data).** Identify **which up to 16 GPIOs each device model actually exposes for LED output**: not the chip's full pin count, but the pins broken out to a usable header/connector AND safe to drive (exclude strapping, flash/PSRAM, input-only, and pins already owned by eth-RMII / I²C / the onboard LED). The codebase knows the chip *ceiling* (`MM_MAX_GPIO` from `CONFIG_SOC_GPIO_PIN_COUNT`, [Control.h:13](../../src/core/module/Control.h)) and the live *ownership/reserved* grading ([PinsModule](../../moonmodules/core/system.md)), but NOT the per-board *exposed-and-safe* set, which is board knowledge (schematic/pinout per model). The authoritative source is the **annotated pinout image per model under `docs/assets/deviceModels/`** (e.g. [`esp32-s3-n16r8-dev.png`](../../assets/deviceModels/esp32-s3-n16r8-dev.png)) (and MoonLight's `ModuleIO.h` `pins[]` as prior art: read it, write our own against `deviceModels.json`). The per-MCU reading is in [gpio-usage § Usable LED-output GPIOs](../../reference/hardware/gpio-usage.md#usable-led-output-gpios-per-mcu-quick-reference); do the same read per model. + - **Encode as per-model defaults + coverage (the catalog).** The 16 usable lane GPIOs become the model's default `pins` string, so a fresh flash of e.g. "Serg UniShield V5" comes up with the *right* lane pins pre-filled, not blank/generic. Coverage is uneven: of 25 models, **7 carry no LED-pin config** (Olimex Gateway, LOLIN D32, Generic ESP32 Dev, ESP32-S3 N16R8 Dev, LightCrafter 16, SE 16 V1, and any bare dev board), and those default to nothing and force the user to guess. Fill every model's LED-capable pin default, and **document the per-model map** (the annotated-pin images are the natural home). **The per-board usable set is wider than the exposed header:** a pin the board commits to a peripheral it *doesn't mount* is fair game (the Olimex Gateway leaves 6 clean LED pins only if you count the **unmounted micro-SD** pins 4/13/14), so the per-model map must record *which non-exposed/repurposable pins are safe on this board*, not just the header breakout. **Note the i80 gotcha:** an i80 model should pick `clockPin`/`dcPin` clear of its data lanes; an overlap is a *warning*, not a blocker (the driver still runs; that one lane just carries the clock/DC waveform, which is fine for an unused parked lane but garbles an active strand), so a catalog default that overlaps an *active* lane would ship a subtly-broken board. +- **Power budget**: `maxPower` (Watts) per board, for a future current-limit / brightness-cap control. +- **RS485 / DMX**: TX/RX/DE pins (wired DMX-512 output beyond the current Art-Net path). The driver that consumes this is the [RS-485 / DMX-512 wired output item](backlog-drivers.md#rs-485-and-dmx-512-wired-output-the-physical-dmx-driver) in the light backlog. -One Windows day (2026-09-29) hit it FOUR times in four different files, each fixed separately at its own call site: `_test_metadata.py` stopped the test runner starting, `test_desktop.py` would have killed a run on one non-ASCII byte from the test binary, `run_live_scenario.py` printed an arrow per step and so recorded a scenario the device PASSED as FAILED, and `preview_installer.py` died writing a staged page, leaving the web installer preview unusable on Windows. Four site fixes in one day is the argument for one home, made by the defect itself. +Sequencing rule (unchanged): each functionality lands a device-side control first, then its preset field; the catalog grows one earned consumer at a time, never as a speculative pin dump. -The preamble is now copied into five scripts (`setup_esp_idf.py`, `ci/verify_version.py`, `docs/build_docs.py`, `scenario/run_live_scenario.py`, `scenario/run_scenario.py`), each carrying its own comment explaining the same fact. Per-script `sys.stdout.reconfigure()` is the wrong shape at 62 files: every new script would have to remember, and the one that forgets fails in the field. It wants ONE home, and the two candidates do not cover the same ground. `PYTHONUTF8=1` in whatever env MoonDeck's front ends already establish settles BOTH halves, because UTF-8 mode changes the default for `open()` and `Path.read_text()` as well as for stdio. A shared `moondeck/_stdio.py` imported by the entry points settles only the stdio half, since `reconfigure()` touches the standard streams and nothing else, so it leaves every unqualified `read_text`/`open` exactly as broken and those sites still need `encoding="utf-8"` written out. Pick when someone next runs a check by hand and it dies on a tick mark. +### Hot path: move blocking work off the render callbacks (architecture) -**`run_scenario.py` was the fifth site, fixed 2026-09-30 ahead of a Windows run.** Its sibling `run_live_scenario.py` got the preamble on the Windows day; this one did not, and it relays the output of `mm_scenarios`, which prints an arrow and an em-dash of its own (`test/scenario_runner.cpp` carries 29 non-ASCII characters, several inside `printf`). Nothing had caught it because the in-process tier had only ever run on macOS, where an attached console and a pipe are both UTF-8. Left alone it would have read as a scenario failure rather than an encoding one, which is the misdiagnosis the live runner's fix was written to prevent. Five sites in two days is the count the one-home argument above should now be weighed against. +Kind: Restructure -## Changing a live MoonLiveEffect's script does not rebind it (2026-09-29) +`-Wfunction-effects` proves the render path really does block — these are not annotation gaps, +they are synchronous I/O, allocation and lifecycle work reachable from `tick()`. Each needs the +work moved to a worker or made resumable, so each is a design change rather than a lint fix. +Confirmed by external review (CodeRabbit, PR #56). -**Being worked on the macOS bench; this entry is measurement for whoever has it.** Rewriting the `script` control on an existing `MoonLiveEffect` leaves the module largely rendering what the previous script rendered. The card reports a clean status, the byte count updates, frames keep animating, so nothing in `/api/state` says anything is wrong. Only the pixels do. +**`tick()` — every frame, the sharp ones:** +- `PreviewDriver::tick` — the socket half is FIXED (the pull-model transport only ARMS a message; + every socket byte moves on the transport tick). What remains: `buildCoordTable()` resizes + `keptIdx_` and the staging buffer on a rebuild/adopt tick. Suppressed at the site with the reason. +- `ParallelLedDriver::tick` — `tickSync()`/`tickRing()` reach `busWaitIfBusy()`, which spins for + the DMA peripheral. Deliberate (the driver owns the bus for the frame) but blocking. Suppressed. +- `Drivers::tick` — joins/stops the render-split worker synchronously on the timed-out recovery + path. Wants asynchronous handling that still preserves buffer ownership. +- `HueDriver::tick` / `tick1s` — synchronous HTTP (`pushOneChangedLight`, `pollPairing`, + `fetchLights`, `fetchGroups`) plus allocation. Wants a queue to a worker. +- `Layer::tick` — calls `applyState()` on a modifier rebuild, which runs prepare/release and + resizes ScratchBuffers. Wants the rebuild deferred to the scheduler's non-render prepare path, + keeping today's coalescing of `consumeNeedsRebuild()`. +- `DemoReelEffect::tick` — `advance()`/`swapTo()` create, delete and `applyState()` child effects + from the render callback. Wants a pending-switch flag consumed off-tick. +- `NetworkSendDriver::tick` — sends inline; wants to enqueue. +- `RmtLedDriver::tick` — waits on hardware completion and reset; wants polling or offload. +- `AudioService::tick` — UDP `sendTo`/`recvFrom` every frame (`syncSend`, `syncReceive`, + `syncEnsureSocket`). -Measured on Windows over the 33 shipped `.mle` effects, same scripts and same instrument, changing only the route. Each script ran 3 s while its preview frames were sampled, then scripts were compared on mean level and on frame-to-frame motion; a pair matching closely on both is a pair that cannot be telling the pixels apart: +**`tick20ms()` / `tick1s()` — milder, same shape:** +- `HttpServerModule::tick20ms` — inline `handleConnection` transport work, so the 100 ms budget + cannot actually bound it. Wants resumable connection handling. `tick1s` writes WLED state + frames inline; wants the existing resumable sender. +- `MqttModule::tick1s` — synchronous DNS + discovery-buffer allocation. +- `NetworkModule::tick1s` — WiFi/AP lifecycle transitions and tree rebuilds. +- `FilesystemModule::tick1s` — the debounced `flush()` does filesystem work; the poll itself is + fine, the save wants a worker. +- `FileManagerModule::tick1s` — `platform::filesystemUsed()`; wants a cached value. +- `SystemModule::tick1s` — the P4 `coprocessorWifi()` path calls + `esp_hosted_get_coprocessor_fwversion()` synchronously; wants a cached snapshot. +- `ImprovProvisioningModule::tick` — runs the queued APPLY_OP inline; wants a cold task to + execute and publish only the result. +- `Scheduler::tick` — dispatches all of the above, so its own contract is only as good as theirs. -- Reusing one module and rewriting its `script` control: **59 indistinct pairs**, in groups. Nine scripts (noise, octopus, plasma, rain, ripples, sparkle, spectrum, stadbeest-frog-eye, stadbeest-walk) all sat at mean 27.2 and motion 0.16, and six more (fountain, fractal, gradient, lines, metal, mh-ambient) at mean 42.9 and motion 0.33. -- A fresh module per script, which is what the UI's picker does: **1 indistinct pair**, and the values separate as you would expect, with fractal at 108.2, noise at 127.2, mh-aim at 255.0 and stadbeest-frog-eye at 151.5. +**Explicitly NOT in scope:** the float-math findings (`BouncingBallsEffect`, `RipplesEffect`, +`SphereMoveEffect`). Review suggested fixed-point, but the float trajectory IS the ported +MoonLight behaviour and fidelity is deliberate. If the FPU-less Xtensa cost is real, that is a +profiling question first, not a lint fix. -The picker works because it does two things rather than one: `replacePickedType` in `src/ui/app.js` calls `replaceModule` to put a NEW module in the slot and only then `setCardScript`. A user who switches scripts from the dropdown on an existing card gets the broken path; a user who picks from the module picker does not, which is why the same library looks fine one way and stale the other. +### Clock sync: a shared monotonic clock across devices -Two scripts render nothing that changes even by the working route, and may simply be correct: `gradient.mle` produces one distinct frame at mean 104.8 with zero motion, and `mh-aim.mle` one distinct frame with every sampled channel at 255. `mh-aim` aims a moving head rather than painting a grid, so full white may be the honest answer. Worth an eye rather than an assumption. +Kind: Feature -The check itself is worth keeping. Frame distinctness plus cross-script comparison catches exactly what compiling cannot, and it is what the existing test suite has no view of: `unit_MoonLiveScripts.cpp` proves all 55 scripts COMPILE, and every one of them passed while the pixels were telling this story. +The second half of the core [multi-device runtime](../../explanation/architecture/mooncore.md#multi-device-runtime); discovery ships, this does not. The design: one leader sends its elapsed time over OSC, the protocol that already carries the shared controls, with the same `addressing`; followers compute their offset, targeting sub-millisecond accuracy. A shared monotonic clock is the foundation any cross-device coordination builds on, which is why it is core rather than light-domain. -## Desktop audio cannot capture what the machine is playing (2026-09-29) +The light-domain payoff is a wall of controllers animating in lockstep: effects already animate off elapsed time, so feeding them the leader's synced clock instead of each device's local one is the whole change on the render side. Device-to-device light *distribution* is a separate topology question and rides the existing ArtNet / E1.31 / DDP standards rather than a bespoke protocol. -An audio-reactive light show usually wants the music the computer is playing, and there is no way to get it. `platform_desktop_audio.cpp` calls `ma_context_get_devices(&ctx_, nullptr, nullptr, &infos, &count)`, passing null for the playback list and count, so only capture devices are ever enumerated; the device then opens as `ma_device_config_init(ma_device_type_capture)`. Measured on a Windows bench: the OS offers `Microphone (Realtek)` and `Speakers (Realtek)`, the picker offers `default` and the microphone, and the speakers are absent. There is no Stereo Mix on that machine, not even disabled, so no zero-install workaround exists either. -miniaudio already supports this as `ma_device_type_loopback`, which is WASAPI-only and takes a PLAYBACK device as its target. Closing it is three pieces: enumerate playback devices as well, mark them in the picker so a user can tell a microphone from "what is playing", and open loopback rather than capture when one is chosen. macOS has no equivalent and needs a virtual device such as BlackHole, so the picker should offer what the platform can actually do rather than a row that fails. +Three render-side consequences. Time-seeded randomness takes a frame index derived from the synced clock rather than raw milliseconds, so devices a millisecond apart hash alike. `hashInt` is the default for anything a synced effect randomizes, and the `Random8` stream stays for local-only effects. Particles, ripple, fire and the cellular automata expose a deterministic re-seed from (time, seed), so a late device is placed into the same state by replay or keyframe. +### Config provenance: firmware/MCU → deviceModel (catalog follow-ups) -Worth pairing with a status the user can read. A microphone input with nothing plugged into it reports a plausible-looking level (RMS around 20 to 30 at 40 to 170 Hz, which is mains hum), so "audio is working" and "audio is hearing silence on a disconnected jack" are indistinguishable from the card today. +Kind: Feature -## The noise floor defaults high enough to look broken (2026-09-29) +The model is shipped ([MoonInstaller § Config provenance](../../explanation/architecture/mooninstaller.md#config-provenance-mcu-devicemodel)). The remaining forward-looking pieces, a `devices.json`/MCU-layer split and annotated-pin images, stay gated by the sequencing rule (no catalog field ahead of a consumer). -With `floor` at 100 a quiet room reports `level RMS 0`, `flux 0`, `peakHz 0 Hz` on every axis, which reads as a dead audio path rather than a working one below the gate. Dropping the floor to 0 on the same device produced live values immediately. The gate is doing its job, and the default may well be right for a wall in a noisy venue, but the card gives a user nothing to distinguish "gated" from "broken". A status line naming the gate when the raw level is non-zero but the gated level is not would cost one line and save the wrong bug report. +### Persistence overlay: partial-save and schema-change audit -## RTSP refuses TCP transport, so a player that asks for it gets nothing (2026-09-29) +Kind: Investigation -Verified on Windows with a real client: `ffprobe rtsp://127.0.0.1:554/` pulls `h264 128x128 30fps yuv444p` over UDP, and the same probe with `-rtsp_transport tcp` fails at `method SETUP failed: 461 (Unsupported Transport)`. Nothing in `RtspDriver.h` or the desktop platform layer mentions interleaved transport or `RTP/AVP/TCP`, so UDP-only reads as an omission rather than a decision. +The absent-key fix (`json::hasKey` guard in `applyControlValue`, so a saved file omitting a key no longer zeroes the control's default — the P4 `ethType` no-DHCP root cause) closed the acute hole. A broader audit would harden the overlay against the rest of the schema-drift surface, now that controls carry meaningful non-zero defaults: +- **Type-change safety (not migration, see MIGRATING.md).** `ethType` changed `int16` → `uint8` (Select). A persisted file written under the old type still loads (the flat parser reads the number either way, and Clamp snaps an out-of-range value to the new bounds). The task is to *confirm* each type transition degrades gracefully through the robust loader (absent → default, stale → clamp, unknown → ignore) — especially Text/Password/IPv4 buffer-size changes — NOT to add migration code. A transition that would lose data silently is documented as a break in the documented break, per the settled policy. +- **Test coverage.** `unit_Control_apply_absent_key` pins the absent-key contract; extend with a type-change round-trip (save int16, load into uint8 Select) and a narrowed-range clamp case so a future schema change can't regress silently. +This is hardening, not a known bug — the shipped fix is correct for the cases that occur today. -It matters because forcing TCP is the ordinary remedy for the two things RTSP over UDP is bad at, a firewall in the way and packet loss on a busy network, and several players try it first or are configured to. The docs point the reader at VLC without saying which transport works. Either implement interleaved transport or say plainly in the driver docs that the stream is UDP only, so the person whose player sits there timing out knows why. +### A second instance says it is serving, serves nothing, and shares the first one's config (2 -## A second instance says it is serving, serves nothing, and shares the first one's config (2026-09-29) +Kind: Bug · Platform: desktop -Launching the desktop binary twice leaves a silent zombie. The second process prints `MoonLight is running: http://localhost:8080/` and `HTTP server -> http://:8080`, then listens on no TCP or UDP port at all, logs no bind failure, no error and no warning, and carries on rendering at about 14% of a core. Measured on Windows with a 128x128 layout: the first instance kept the port and kept serving throughout, so the deliberate omission of `SO_REUSEADDR` does its job and the port is never stolen. What is missing is the report. +Launching the desktop binary twice leaves a silent zombie. The second process prints `MoonLight is running: http://localhost:8080/` and `HTTP server -> http://:8080`, then listens on no TCP or UDP port at all, and carries on rendering at about 14% of a core. Measured on Windows with a 128x128 layout: the first instance kept the port and kept serving throughout, so the deliberate omission of `SO_REUSEADDR` does its job and the port is never stolen. `HttpServerModule` prints `HTTP server failed to open port`, but the banner in `main_desktop.cpp` prints regardless. The sharper half is the filesystem. The second instance mounts the SAME data root and writes to it: `saved /.config/NetworkModule.json` landed while the first instance was live and serving. Two processes owning one config directory is a corruption waiting for the right interleaving, and neither of them knows the other exists. -A bind that fails is a startup failure and wants to read as one: report it, and either exit or say plainly that another instance holds the port. The banner should follow what actually bound rather than what was intended. Whether a second instance should be refused outright is the open design question, since the data root is per user rather than per process and two of them cannot both own it. - -## A Windows scenario run recorded tick averages 250x its own repeat, cause unknown (2026-09-29) - -A Windows run of `scenario_Layouts_resize_reallocates_live` recorded 245 ms, 564 ms and 458 ms for its three measure steps, against 41 to 89 us for the same steps on macOS. `tickTimeUs` is a one-second average (`src/core/module/Scheduler.cpp:127`), so those figures describe a second containing roughly one tick, which reads as a blocking call rather than slow rendering. The blocks were reverted rather than committed, since an observation filed at 245 ms teaches a future reader that Windows costs that. - -An immediate repeat on the same machine and binary did not reproduce it. The three steps came back at 913 us, 1097 us and 768 us, and the tick log showed `PanelCard:0us`, `Network:1us` and `Drivers:30us` throughout, with the tick dominated by `MoonLive:3000us`. Nothing was blocking during the repeat, so the first reading stands unexplained. The candidate that fits the shape is `ethBindRawInterface()` calling `pcap_findalldevs` (`src/platform/desktop/platform_desktop.cpp:1299`), measured separately at about 2.3 seconds on Windows against milliseconds on macOS and reached through driver `prepare()` on a resize, but the repeat neither confirms nor kills it: no raw interface was bound on either run. - -The enumeration cost is real and independently measured, so caching it or keeping it off any path a resize can reach earns its place on its own. - -**The second half of this is already answered, from the other side of the same day (2026-09-29).** The ask was for a max-tick beside the average, to tell one blocked tick from a uniformly slow second. The observation block records `min`, `max`, `p50`, `p95`, `n` and the raw `samples` per target, so a repeat of this run reports its own worst frame rather than only its mean: a single 245 ms tick inside a second of good ones now shows as a `max` far above the `p50`, which is the distinction that was missing. - -**Re-measure before reading anything into the old figures.** `scenario_Layouts_resize_reallocates_live` was rewritten the same day (59 lines changed): it now builds a full pipeline and asserts with `expect_control` where it previously only measured, so the Windows numbers above describe a scenario that no longer exists in that form. Whatever a Windows run reports next is the figure to reason from, and it will carry the spread this entry wanted. - -## MoonLight hangs when a USB network adapter is re-plugged (2026-09-28) - -Unplugging and re-plugging a USB Ethernet adapter while PanelCardDriver is active wedges the whole application. Measured: the render task stops logging entirely (0 bytes in 8 s), one thread spins at 74% of a core while the other two sit in `Wait`, the listener still accepts so connections pile up unanswered (11 of them), and HTTP never returns, tested to 45 s. The last line written is always `NetworkModule: Ethernet up (recovered from Idle)`, so it enters the spin somewhere in the Ethernet-recovery path on the render thread rather than blocking on a lock. - -Reproduced once, on a bench with a Realtek USB GbE adapter feeding a ColorLight card. The trigger is ordinary: anyone driving panels from a USB dongle does this. The suspects are the pcap calls that recovery reaches, since `ethBindRawInterface` runs `pcap_findalldevs` and `pcap_open_live` on an adapter that has just re-enumerated, and both are called from `prepare()` on the render task. Worth a second reproduction with a debugger attached before guessing further: a spinning thread with a live listener is a loop, not a deadlock, and the loop has a home. - -## The interface picker re-enumerates adapters on every control rebuild (2026-09-28) - -`rawInterfaces()` caches nothing, and `defineDriverControls()` calls it on every rebuild of a driver that owns an interface Select. On Windows `pcap_findalldevs` opens each adapter in turn and measures ~2.3 s, so every control change on PanelCardDriver, every `/api/types` that builds the type catalog, and every UI refresh paid it. Measured with a panel driver present: `POST fixture` 2.2 to 6.7 s, `/api/types` 2.3 to 4.4 s, `/api/state` 2.2 s. The same payloads on a config with no panel driver: 23 to 36 ms. POSIX is not affected, since it enumerates with `getifaddrs`. - -A cache with a 60 s window took those to 8 to 35 ms and was reverted before the release as too much risk for hardware-critical code. Two things learned from doing it: the window has to outlast a person pausing to think, because a 3 s one leaves every deliberate control change paying full price; and skipping the rebind needs `ethLinkUp()` in the condition, because an open pcap handle whose link is down still wants rebinding and guarding on "already bound" alone removes the only recovery a user has. Scope any cache to Windows, since it buys POSIX nothing and costs it hot-plug latency. - -## NetworkModule treats a panel-output NIC as the device's uplink (2026-09-28) - -A NIC claimed for raw L2 panel output carries no LAN, but NetworkModule enumerates it anyway and reports `Connected via Ethernet` on it. When the panel link renegotiates, every flap runs the full cascade, which drops the browser's WebSocket and makes the UI look dead while the server is healthy. Seven flaps were logged in one bench session. The driver already announces itself through `ethClaimRawL2(true)`, so the claim is there to honor; the change is for NetworkModule to skip a claimed adapter. Crosses two modules, so it wants a decision rather than a patch. - -## The persisted interface label carries the negotiated link speed (2026-09-28) - -PanelCardDriver saves its adapter as `Realtek USB GbE Family Controller, 1 Gb`, speed included. A renegotiation to 100 Mbit rewrites that string, which dirties the config and rewrites `Drivers.json`. Matching already strips the suffix and compares the stable head, so the binding survives, but an identifier that changes when a cable renegotiates is the wrong thing to persist. Store the adapter's stable identity and render the speed for display only. - -## `build_esp32.py` reports success without building, under Git Bash (2026-09-28) - -Run from Git Bash it prints `MSys/Mingw is no longer supported`, stops after `Setting target to esp32s3...`, and **exits 0** ten seconds later with no binary. Run from PowerShell the same command builds correctly. A green exit code for a build that never ran is the worst shape a failure can take, and on a runner it would publish a stale binary while every gate stayed green. Either detect the MSYS environment and fail loudly, or re-exec through the shell the IDF tools expect. - -## `build_desktop.py` cannot relink while the app is running (2026-09-28) - -Windows holds an executable open while it runs, so a rebuild fails with `LNK1104: cannot open file ... MoonLight.exe` where POSIX would replace it. `run_desktop.py` already stops a running instance before launching; `build_desktop.py` could reuse that and remove a papercut that costs a confusing error every time someone edits code with the app open. - -## `disasm.py` cannot run on Windows (2026-08-21) +A bind that fails is a startup failure and wants to read as one: exit, or say plainly that another instance holds the port, with the banner following what actually bound rather than what was intended. Whether a second instance should be refused outright is the open design question, since the data root is per user rather than per process and two of them cannot both own it. -The tool shells out to `c++` and `objdump` to build the per-ISA emitter and disassemble its bytes, and a Windows machine carrying only MSVC has neither. That holds for every ISA, not just the new `x86_64` one — but x86-64 is where it bites, because the host it disassembles is now a Windows desktop and the tool is what turns "the script did nothing" into an answer. The x86-64 backend was brought up without it, using byte-pinning tests plus WinDbg on the emitted image; that worked, and was slower than reading the instructions would have been. +### A non-ASCII Windows profile path defeats the desktop settings directory (2026-08-23) -Closing it is a compiler/disassembler pair behind the two `subprocess.run` calls: `cl.exe` for the build, and for the disassembly either LLVM's `llvm-objdump` (which understands `-b binary` the way the tool already expects, and ships with the VS "C++ Clang tools" component) or a `.obj` wrapper around `dumpbin /disasm`, which does not. Prefer the former — the flags are already right, so it is a lookup rather than a second code path. - -## A non-ASCII Windows profile path defeats the desktop settings directory (2026-08-23) +Kind: Bug · Platform: desktop `std::getenv("LOCALAPPDATA")` returns the ANSI form of the path, so a Windows user whose profile name carries characters outside the system codepage (CJK and Cyrillic on a Western machine; most accented Latin survives cp1252) gets `?` where those characters were. `?` is not legal in a Windows filename, so `create_directories` fails, `fsMount` returns false, and the driver reports `cannot use ..., persistence disabled` naming the mangled path. It degrades visibly rather than corrupting anything, which is the standard Principle 5 asks for, but that user has no working persistence. @@ -1785,421 +520,12 @@ Reading the variable wide is only half of it. `toFsPath` composes a `std::filesy Not done with the per-user data directory because it reverses a documented decision across the whole desktop filesystem layer, four of whose five call sites predate that change. What the change did do is make the root capable of containing a username, where it was previously the literal `build`. Worth closing the next time this file is opened for other reasons. -## Opt-in usage reporting: know which hardware and configurations are actually out there (2026-09-01) - -We build for hardware we cannot see. Which chips people actually run, how big their walls are, which drivers and features they enable, and how many are still on an old version are all questions currently answered by whoever happens to speak up on Discord. That is a biased sample: it hears from people with problems and from people who post, and it is silent about the majority who install something and it simply works. Effort therefore goes where the loudest reports are rather than where the users are, and a decision to drop support for something rests on a guess. - -**What a report would carry.** Enough to answer "what should we build and what may we stop supporting", and nothing else: - -- **The machine.** Chip family and variant for a device (classic ESP32, S3, P4, S31 and so on), flash size, whether PSRAM is present and how much. For a desktop, the OS and architecture instead, which is a question we have no way to answer at all today. -- **The version.** Firmware or application version, release channel, and on an upgrade the version it came from. Whether this was a fresh install or an upgrade. -- **The light setup, in shape rather than content.** Total light count, which layouts are in use and their dimensions, which drivers are active, and how many outputs or buses. Not what the wall shows, only how big it is and how it is wired. -- **Which features are switched on.** The modules present and enabled: audio, MoonLive, MQTT, Art-Net or E1.31 send and receive, panel cards, Ethernet versus WiFi. This is the half that answers "is anyone actually using this", which is what decides whether a feature earns its maintenance. -- **A country**, derived at the server from the address the request arrives from, so the map is by region rather than by installation. - -**What it must never carry**, and this list is a constraint on the design rather than a note on it: device name, IP or MAC address, WiFi credentials, MQTT passwords, any free-text field a user typed, and the contents of a layout or a script. - -**The identifier: a random value per install, dropped after seven days** (settled 2026-09-08). Not derived from the MAC or from anything else about the hardware, so it cannot be reversed to a device even by whoever holds it. It exists to answer one question the aggregates cannot: how many DEVICES, rather than how many reports, and therefore how many installs are still on an old version. The server keeps per-report rows for seven days, de-duplicates, then keeps only the aggregate and drops the id. - -Two options were weighed and rejected. **No identifier at all** was the original text here: simplest to promise, but it makes every per-device question unanswerable, and "how many are still on the old version" is one of the questions this whole entry exists to answer. **A hashed MAC** is what WLED shipped first (`sha1("WLEDUSAGE" + MAC)`) and it buys exactly one thing more than a random id, given reports are one-time: linking one device's reports across months. That is the tracking capability itself, it survives a factory reset and a reflash, an open-source salt over a MAC space that size is rainbow-tablable, and a stable pseudonymous identifier is personal data under GDPR rather than anonymous. WLED's own shipped version moved off it to an anonymous id, which is the same conclusion from people who had it running. - -**Consent.** Opt-in, from a prompt shown after a fresh install or an upgrade, with a decline that is as easy to click as the accept and is remembered. One report per install or upgrade, never a heartbeat. A user who declines transmits nothing at all, rather than transmitting a "declined" record. - -**The privacy policy already commits to this shape** ([docs/legal/privacy-policy.md](../../legal/privacy-policy.md)), including the honest wording about the IP address a server unavoidably sees. Whatever is built has to match what is promised there, and the policy has to be updated with the specifics BEFORE the feature ships, not alongside it. - -**Open questions for whoever picks this up.** Where the server runs and who administers it, since it is the first piece of infrastructure this project would own rather than borrow from GitHub. Whether the dashboard is public, which we would want it to be for the same reason the source is. Whether the desktop reports at all or only devices, given the desktop is the half we currently know nothing about. And what happens to a report from a version whose fields have since changed, because a schema that cannot be read a year later answers nothing. - -**WLED ships this, and the dashboard is public**: [usage.wled.me](https://usage.wled.me). Worth reading before building anything, because it answers two of the questions above by example and disagrees with this entry on a third. - -What it shows: distributions by version, chip, matrix, flash size, PSRAM, release, LED count and filesystem usage; upgrade versus install events over six months, split by chip; which LED features, peripherals, integrations, usermods and bus types are in use; and device count by country. That list is close to what is proposed above, which is reassuring about the shape. - -How it is collected ([PR #5116](https://github.com/wled/WLED/pull/5116), merged 2025-11-27, superseding an earlier [#4342](https://github.com/wled/WLED/pull/4342)): a one-time POST to `usage.wled.me/api/usage/upgrade` when a persisted version file shows the firmware changed, behind a startup prompt offering Yes / Not Now / Never Ask, and suppressed entirely in AP mode. The server is open source ([netmindz/WLED_usage](https://github.com/netmindz/WLED_usage)), which is how they answer "is the deployed code the published code". - -**Where they differ from this entry, and it is the interesting part.** WLED sends a device id: the first design hashed the MAC (`sha1("WLEDUSAGE" + MAC)`, "unique but not reversible"), the shipped one an anonymous `deviceId`. Either way the point is de-duplication, and it is what lets their dashboard say how many DEVICES rather than how many reports. This entry deliberately refuses that, and pays for it in accuracy. Their earlier PR also discussed a retention split (per-device data for 7 days, aggregates longer), which is a middle position between the two: keep an identifier only long enough to de-duplicate, then drop it. Worth considering rather than assuming the strictest option is automatically right. - -### The smallest honest version - -Sketched 2026-09-08. Not started; the blocker is the server, not the firmware. - -**The firmware side is small, because the payload already exists.** Every field is a control or a -module name the device publishes today: `chip`, `cpu`, `flash`, `psram`, `sdk`, `firmware` and -`deviceModel` are SystemModule controls; `version` and the previous version are FirmwareUpdateModule -controls; the enabled modules are the state tree's own top level; the light setup is the Layouts -grid and the Drivers children. So this is a serializer over facts already in memory, not new -instrumentation. Budget it as one module of a few hundred lines, plus the consent prompt. - -**When it sends.** Once, when a persisted version file shows the firmware changed, which is the -shape WLED converged on after starting from a UDP spray every second. Never a heartbeat. Suppressed -in AP mode, where there is no internet and a device is usually mid-provisioning. - -**Consent.** A prompt on the first boot after an install or upgrade: Yes / Not Now / Never, with -Never remembered. A decline sends nothing at all, not even a "declined" record. The prompt says what -is in the report in one sentence, and links the privacy policy. - -**Ask WLED to host it, before building a server at all.** Investigated 2026-09-08, and their server -is closer to a fit than expected. - -[netmindz/WLED_usage](https://github.com/netmindz/WLED_usage) is Kotlin on Spring Boot with a MySQL -schema under Flyway migrations, a Docker compose deploy, and one endpoint: -`POST /api/usage/upgrade`, no auth, with the country derived server-side from an `X-Country-Code` -header. The dashboard is a single static `index.html` against a `/api/stats` controller. - -**It is already multi-project.** The `device` table carries a `repo` column (added 2025-12), there -is a `RepoHistory` entity, and EVERY stats query is written `(:repo IS NULL OR repo = :repo)`. So a -second project reporting under its own repo name is a supported case rather than a change they would -have to make, and our figures would not be mixed into theirs. - -**We adopt their schema unchanged, and drop the one field that does not fit** (settled 2026-09-08). -chip, version, previousVersion, releaseName, ledCount, isMatrix, flashSize, psramSize/Present, -fsUsed/Total, busCount and busTypes mean the same thing in both projects, so those figures are -directly comparable and MoonLight can sit in a pooled overview rather than being a special case. -Our own vocabulary rides the free-form lists: ledFeatures, peripherals, integrations, and `usermods` -for the enabled-module names. That works because migration V2026040301 (2026-04) replaced twenty-odd -fixed boolean feature columns with three comma-separated lists, aggregated by counting whatever -values appear: adding MoonLight's names costs their server nothing. - -The layout dimensions this entry originally wanted (width x height x depth, since we are 2D and 3D) -have no equivalent there, and are DROPPED rather than added. `ledCount` plus `isMatrix` answers most -of what they were for, and being identical to a schema someone else maintains is worth more than one -field. If a real question later needs the third dimension, a list value is the place for it. - -**Their payload is close to what this entry wants.** `UpgradeEventRequest` already carries -deviceId, version, previousVersion, releaseName, chip, ledCount, isMatrix, bootloaderSHA256, brand, -product, flashSize, partitionSizes, psramSize, psramPresent, repo, fsUsed, fsTotal, busCount, -busTypes, ledFeatures, peripherals, integrations, usermods. Every field we listed above maps onto one -of those except the layout dimensions, and `usermods` is the natural home for "which modules are -enabled". Sending it means shaping our report to their names, which is a small price for not owning -a server. - -**What to settle with them before relying on it.** The repository has NO LICENCE file, so -strictly nobody may reuse it, and it was last pushed 2026-05-31, so it is quiet rather than dead: -both are conversations rather than blockers, but they are conversations to have first. Then the real -questions: are they willing to take another project's reports at all, who administers the box and -what happens to our data if that person stops, does the public dashboard gain a repo selector or -would we render our own from their API, and does the seven-day identifier retention this entry -commits to match what their server actually does (their earlier PR discussed it; the shipped schema -keeps a `device` row keyed by id, which suggests it does not). - -If the answer is yes, this feature loses its blocker entirely and becomes a firmware change plus a -conversation. If it is no, the plan below stands. - -**The server is the whole cost, and it is a standing commitment rather than a feature.** It is the -first infrastructure this project would own rather than borrow from GitHub, and it needs an -administrator, a domain, TLS, a retention job that actually runs, and a public dashboard. WLED -publishes their server ([netmindz/WLED_usage](https://github.com/netmindz/WLED_usage)), which is how -they answer "is the deployed code the published code": whatever runs here should be public for the -same reason the firmware is. Until someone owns that, this feature cannot ship honestly, and that is -the reason it is still in the backlog rather than in a branch. - -**Order of work, and the first two are worth doing whether or not the rest ever lands.** Write the -privacy policy revision FIRST, since it is the promise everything else has to match, and the current -page says plainly that nothing of the kind exists. Then the report BUILDER as a pure function over -the state tree, with a unit test asserting that the forbidden fields cannot appear in its output: -that test is the design constraint made executable, and it is worth having even if nothing ever -sends. Only then the consent prompt, the one-time trigger, and last the server. - -## A driven GPIO the Pins map never sees: bus padding, and a hidden clockPin - -**Found:** 2026-08-21, on MM-S31, after a bench session that started as "the LED panel stopped working" and cost hours chasing a firmware regression that did not exist. - -An ESP32-S31 driving a ColorLight receiver card over raw Ethernet showed the panel dark while every diagnostic said the transmit path was healthy: link negotiated at 1000 Mbit, ~4600 packets/s, zero drops, and a byte-for-byte dump of a 128x128 frame matched `ColorLight5A75Packet.h` exactly. The card's own activity LED never blinked. Four firmware versions across two ESP-IDF releases behaved identically. - -The cause is a **GPIO collision that no part of the system could report**. `ParallelLed` carried `clockPin = 10`, and GPIO 10 is `txd2` on the S31's RGMII bus (`platform_esp32.cpp`: the EMAC's fixed IO_MUX pads are 8-19 plus MDC/MDIO on 5/6). The LED driver drove one of the four Ethernet transmit data lines, so every frame left the MAC counted-as-sent and arrived corrupt. Disabling the LED drivers fixed it instantly; moving the clock to GPIO 21 fixed it with all four drivers running. - -Three separate defects made this invisible, and each is worth fixing on its own: - -**1. Pins the map cannot see, because nothing declares them.** The shipped conflict soft-flag grades what modules *declare*, so an undeclared pin is invisible to it however hard the silicon drives the pad. Three cases, two now closed: - -- ✅ **Bus-padded lanes** (fixed): a one-strand board had seven i80 lanes parked on `clockPin`, driven at bus-clock rate and listed nowhere. `spareLanesNeedPad()` stops the padding on a backend that routes its own GPIOs, so the pin is no longer driven and the map is truthful again. -- ✅ **RGMII data pads** (fixed): all twelve are now published by NetworkModule as read-only pin controls from one `platform::ethRgmiiPads` list that `ethInitEmac` also reads. Verified on MM-S31: `gpio 10` reports as `ethTxd2`. -- ✅ **P4 RMII data pins** (fixed): the six lines the EMAC drives are in `platform::ethFixedPads` and reported through `fixedPins()`, same as the S31's RGMII pads. Verified on MM-P4: Network owns all ten of its Ethernet GPIOs (28/29/30/34/35/49 plus MDC 31, MDIO 52, clock 50, reset 51), against four before. -- ✅ **Classic ESP32 RMII data pins** (fixed): TX_EN 21, TXD0 19, TXD1 22, CRS_DV 27, RXD0 25, RXD1 26, entered as a third `ethFixedPads` branch. Sourced from IDF's own RMII Data Plane GPIO table (`docs/en/api-reference/network/esp_eth.rst`): one IO_MUX choice per signal, which is why `ethInitEmac` never sets them. Verified on MM-Olimex: all ten Ethernet pins claimed, `Eth: 192.168.1.210 (100 Mbit)`. -- ✅ **MDC/MDIO on the classic ESP32** (fixed): the chip default was `mdc -1, mdio -1`, so `ethInitEmac` skipped the assignment, IDF applied its own 23/18, and the map claimed neither. `ethConfigDefault` now states 23/18, sourced from IDF's own classic default and our QuinLED Dig-Octa entry. Verified on MM-Olimex: `Eth: 192.168.1.210 (100 Mbit)` with `gpio 23 MDC` and `gpio 18 MDIO` in the map. NOTE: a board with -1 already persisted keeps it until those controls are set once or NVS is erased. - -**The fix is the RGMII one, extended.** Being fixed in silicon does not make `gpioCapability`'s reserved list the right home: reserved means "routing I/O here corrupts the device" (flash, PSRAM, USB), which is unconditional, while an EMAC pad is only held while that interface runs. With `ethType = None` the init returns false and every one of those GPIOs is free for LEDs, so a static reserved list would permanently forbid pins a WiFi-only board can use. What makes a control the right shape is not that the pin is configurable (it is not) but that the claim is CONDITIONAL: published when the interface is selected, released when it is not, which is exactly what the pin map reads. +### Drivers' palette and fixture-profile lists, sent once rather than in every view (2026-10-13) -So the same `platform::ethRgmiiPads` treatment applies, with one wrinkle. On the **classic ESP32** the RMII data pins are silicon-fixed, so a second per-chip pad list serves them directly. On the **P4** 49/34/35/28/29/30 is the Waveshare NANO's *board* wiring that the IDF macro happens to default to, not a chip constant, so those belong in `deviceModels.json` beside the other per-board eth pins, letting a different P4 carrier declare its own. Both then reach the pin map through the control path already built, rather than a third mechanism. +Kind: Restructure -Lower risk than the RGMII case (six pins rather than twelve, and nothing of ours currently collides), but the failure mode is identical: a driver claims one, the MAC still reports a healthy link, and every frame goes out corrupt. - -**2. `busPinList()` pads spare lanes with `clockPin`, and MoonI80 routes them as data.** The i80 bus is always 8 or 16 bits wide (`ParallelLedDriver::busWidthPins`), so a board driving one strand gets seven lanes parked on the clock pin, and `configureGpio` (`platform_esp32_moon_i80.cpp`) routes every entry it is given. The padding exists because `esp_lcd` rejects an NC data pin, but the MoonI80 backend does not have that limit: its own comment says *"pins past `laneCount` go nowhere"*. It is simply never told the real lane count. Passing it would free six or seven GPIOs on every direct-mode board and remove the hidden claim at the source. +Drivers is the largest root in the state, 35 KB of the Teensy's view where Services is 10 KB, and two lists are 25 KB of it, mostly repetition. +The `palette` options carry 66 palettes with 16 hex colors each for the dropdown's swatches, which change only with the firmware or a palette script: the state carries the names, and the page fetches the colors from a route the browser caches across reloads. +FixtureProfiles' `profiles` summary is 1.9 KB, and its `detail`, 14.5 KB, repeats each expanded row's editor fields, their name, type and bounds, for every profile: the row schema is sent once, as `optionSets` already sends the role names, and each row carries its values. +Together Drivers falls to about 12 KB, and the largest single control, the block a streamed state still needs whole, from 15 KB to a few. -**3. `clockPin` defaults to 10 and is hidden.** `MoonLedDriver::clockPin = 10` is a hardcoded default that lands inside the S31's reserved RGMII block, and `addBusControls` hides the control unless `pinExpanderMode()` is on. So on this board the value was invisible on the card, unchangeable through the UI, and still driving a pad. A pin with a real effect must be visible, whatever mode it is in. - -**This also closed the S31 Ethernet defect, open since 2026-07-26.** That entry (removed) blamed an RGMII Tx-clock mismatch at 100M for DHCP never completing, and had concluded "the frames never reach the router". The cause was the same collision: `ParallelLed`'s default `clockPin = 10` is `txd2`, so a DHCP DISCOVER was garbled exactly as the panel frames were. With the clock pin moved off the RGMII block the S31 leases normally, verified on the bench at `Eth: 192.168.1.125 (1000 Mbit)`. Two long-standing bugs, one GPIO. - -## Input transports: foot pedals, USB game controllers, and MoonLive at the pins (2026-09-01) - -`ButtonService` shipped with the [GPIO seam](../present/Plan-20260901%20-%20Input%20mapping%20and%20scripted%20sensors%20(7%20of%2012).md) -(`gpioInputBegin` / `gpioRead` / `gpioWrite`). It names a target as `Module.control` and writes it -through `Scheduler::setControl`, so a press and an OSC message are indistinguishable downstream. -Three follow-ups build on that seam rather than beside it. - -**Foot pedals are two different things**, corrected in -[input-mapping-analysis.md](input-mapping-analysis.md): a **footswitch** is a switch on a TS jack, -which `ButtonService` in momentary mode already covers, while an **expression pedal** is a -potentiometer on a TRS jack, which is an ADC read mapping onto a fader and does not exist yet. -Neither is USB. **Action: document the footswitch, scope the expression pedal as an analog-input -service.** - -**USB game controllers are their own project, and are S3/P4-only.** The mapping half is trivial once -reports arrive (buttons to pads, axes to encoders and faders, all through `setControl`); the -transport half is not: - -- The **classic ESP32 cannot do it at all**: no USB Host peripheral, no USB OTG. So the Dig-2-Go, - and every classic board, is out from the start. -- **S3 and P4** have USB-OTG and the IDF ships a USB Host stack with a HID class driver. A gamepad - is a HID device whose report descriptor must be parsed to learn which byte is which button or - axis, and that parsing is the real work: descriptors vary per vendor, and the well-known - controllers each have quirks. -- **Desktop** would go through the OS gamepad API behind the same seam, a wholly separate - implementation. - -Expect a plan of its own, after the GPIO inputs have settled. The honest scope is "a HID report -parser plus a mapping UI", not "read a controller". - -**MoonLive at the pins** is the piece that makes the seam pay twice. The direction is that MoonLive -gains driver scripts whose hello-world is "read from GPIO, write to GPIO", which needs: - -- `gpioRead(pin)` / `gpioWrite(pin, on)` as builtins, mapping straight onto the platform seam. -- `setControl(module, control, value)` as a builtin. **This one needs a decision before it ships**: - it is the same primitive every transport already uses, so exposing it is consistent, but it also - lets any script write any control on any module. That is power worth granting deliberately rather - than as a side effect. -- A `MoonLiveService` host module, the service twin of `MoonLiveEffect`: a `script` control, the - compile and status path, the picker integration. Mostly a copy of the existing binding, and it is - what turns "MoonLive can read a pin" into "a user can add a scripted service". - -`ButtonService` is then the precompiled sibling of a script anyone could write, exactly the -relationship `ballpit.mle` has with `BallpitEffect`. - -**Double-click and multi-click** (2026-10-25), from ESPHome's trigger vocabulary: the same debounce -timers with more states, so one button does several things. Built when a panel needs more actions -than it has buttons. - -## Relay-gated boards, and what a driver is (2026-09-01) - -Wiring a QuinLED Dig-2-Go found that its LED supply sits behind a relay on GPIO 12, the vendor's -"LED Relay enable pin". Undriven, the data line is perfectly correct and the strip stays dark, which -is a hard failure to diagnose from the firmware side. `Drivers` now carries a `relayPins` list that -follows the master `on` control. - -**A list, because boards do not agree.** The Dig-2-Go has one relay for one LED output; the -Dig-Next-2 has **four relays for two outputs**, all carrying the same `Relay_LightsOn` role in -MoonLight's own model. So a relay maps to neither the device nor a driver, and every one of them -follows master power together. It lives beside `on` rather than on a driver because a relay gates -the SUPPLY, which several drivers share, where a driver's own controls describe that one driver's -output. - -**Still open: the definition of a driver.** Two statements in the repo disagree. -[`drivers.md:3`](../../moonmodules/light/drivers.md) says "A driver sends lights somewhere", while -[`architecture.md:143`](../../explanation/architecture/index.md) frames drivers as the consumer half of producers vs -consumers. The product owner's definition is broader than both and is the one to adopt: **a driver -communicates with hardware or the network**, which explicitly includes talking to GPIOs. - -The concrete tension is in code: `DriverBase` declares `virtual void setSourceBuffer(Buffer*) = 0`, -so every light driver is structurally required to consume the light buffer, and all sixteen do. The -resolution is that there are two families of one idea, split by whether the light buffer is -involved: a **light Driver** (`Drivers` container, consumes the buffer, outputs it) and a **core -Service** (`Services` container, a capability bridge, no buffer). `services.md` already says exactly -this. **Action: scope `drivers.md`'s claim to light drivers, and name the general sense in -`architecture.md`.** Documentation only, no code. - -## A desktop build reports firmware "unknown", so the update UI guesses from the browser (2026-09-06) - -Spotted on the container's Firmware card, which showed **Device: macOS arm64** while the image is -Linux amd64. It was not reading the device: `desktopKeyForThisHost()` (`src/ui/install-picker.js`) -and `desktopAssetPrefix()` (`src/ui/app.js`) both sniff `navigator.platform`, which describes the -machine holding the BROWSER. The container makes it visible because there the two always differ, but -the same card lies on any desktop instance opened from another machine. - -The download it offers is the failure mode worth naming: on a Mac the picker serves -`MoonLight-macos-arm64-*.dmg`, a real file that installs and runs perfectly, and updates the wrong -computer. Nothing errors, and the device stays on its old build. - -**Root cause is one missing build flag.** `MM_FIRMWARE_NAME` already exists for exactly this -(`build_info.h`, surfaced as the `firmware` control): `build_esp32.py` passes the variant key, and -the desktop packaging passes nothing, so every published desktop binary falls through to the -`"unknown"` default. `build_info.h:43` even records the assumption that made this safe, "local -builds aren't published" — the container is a published desktop build, which is what retired it. - -**What it would take:** have `package_desktop.py` pass -`-DMM_FIRMWARE_NAME="desktop--"` for the target it packages (the keys the picker's -`DESKTOP_LABEL` map already lists), then delete both browser-sniffing functions and read the -`firmware` control instead. `deviceFirmwareInfo()`'s `isDesktop` test (`firmware === "unknown"`) -needs the same treatment, since a named desktop build no longer matches it: test the -`desktop-` prefix. Local unflagged builds keep reporting `unknown`, where a guess is still better -than nothing. - -Worth doing with the OTA path rather than alone: a desktop cannot install its own archive anyway, so -the honest end state may be a card that names the platform and links the release rather than -offering a Download button that only ever means "fetch a file and swap it by hand". - -## Verify P4 WiFi on our own board: the C6 slave firmware first (2026-10-19) - -Issue #146 (a P4 `eth-wifi` boot loop) was fixed without anyone seeing WiFi work: the bench P4-NANO's C6 never answers on SDIO (`ESP-Hosted link not yet up`, no `Card init success`), with or without the fix. So every P4 WiFi question waits on a reporter's board. The textbook cause of a silent C6 is a host/slave mismatch: our host side is ESP-Hosted 2.12.13, built into the firmware, while the C6 runs whatever slave firmware the board maker flashed. **Make our own P4-NANO's link come up, then keep it as the WiFi reference.** - -- **The slave image**: `network_adapter.bin` built from esp-hosted-mcu's `slave` example at the host's version (2.12.13), for the C6. -- **Flashing it, with the link down** (our case): directly over the C6's own UART with its boot pin held, through an ESP-Prog or a USB-serial adapter. The P4-NANO's schematic says where the C6's UART and boot pads are; ESPHome's board page names only the SDIO pins (`clk` 18, `cmd` 19, `d0`-`d3` 14-17) and the C6 reset (`GPIO54`). -- **Flashing it, with the link up**: ESP-Hosted's transport OTA, its `host_performs_slave_ota` example, which ESPHome wraps as the `esp32_hosted` update platform. This is the path for users once one board works, and a candidate for MoonLight itself: carry the slave image and offer the update on the WiFi card, as ESPHome does. -- **Say which failure `wifiCoproc` shows**: `no version reply` covers two cases, a link that never came up and a C6 too old to answer the version query (or answering 0.0.0), and only the first stops WiFi. `coprocessorWifi()` in `platform_esp32.cpp` checks the transport state first and reports `link down` or `C6 too old to report`, so a reporter's System card answers what today needs a serial log. -- **Then**: the round-3 regression (associates on rc1, not on the v6.1 pin) gets measured on a board with a known-good link, and the two defects in the next entry get a repro. - -What #146 taught, for any P4 change: before the scheduler starts, the P4 has 148 KB of internal RAM (retention, RTC and SPM; the 384 KB L2 region still holds the startup stacks), and every global constructor allocates from it. ESP-Hosted's took 123 KB until `CONFIG_ESP_HOSTED_MEMPOOL_PREFER_SPIRAM` moved its buffers to PSRAM. A boot that asserts in `vApplicationGetIdleTaskMemory` is that budget exhausted; a `heap_caps_get_free_size(MALLOC_CAP_INTERNAL)` report from a constructor of priority 101 and an `ESP_SYSTEM_INIT_FN` at stage 999 brackets it. - -## P4 WiFi cascade into a dead co-processor link aborts the board (2026-09-08) - -Bench, MHC-WLED ESP32-P4 shield on `esp32p4rev1-eth-wifi`, no Ethernet cable: the NetworkModule -cascades to WiFi, and the board reboots every ~22 s with `task_wdt: main (CPU 0)` while `IDLE0` -runs. The main task is not spinning, it is BLOCKED: on the P4 every `esp_wifi_*` call is forwarded -over SDIO to the ESP32-C6 by `esp_wifi_remote`, and this shield's C6 never completes the ESP-Hosted -handshake (`E H_API: ESP-Hosted link not yet up` at boot), so `esp_wifi_init` and the calls after it -each wait out their timeout, on the render thread, past the 5 s watchdog. The wait is -esp_hosted's `transport_drv.c` slave-ready loop: 200 ms polls, a slave reset every 50 of them (the -`Reset slave using GPIO[54]` lines at 10 s and 21 s), up to MAX_RETRY_TRANSPORT_ACTIVE = 100 -polls, so worst case ~20.0 s in the caller's task, which is ours. Not the HWLOOP/FFT -erratum: audio was ruled out by the watchdog text itself (a blocked main task with idle running, -not a spinning core). Control experiment, same image on the bench P4 (.139, same shield model, -esp_hosted host 2.12.13): its SDIO card init succeeds (`Card init success, TRANSPORT_RX_ACTIVE`), -it never associates either, but the main task keeps ticking, it falls back to its own AP, and it -rejoins Ethernet when the cable returns. The new shield never prints a card-init success: its C6 -does not answer the bus (no slave firmware, or one that does not match the 2.12.x host). - -Two defects are ours, whatever the C6 carries: - -1. **The cascade does not consult the link.** `coprocessorWifi()` in `platform_esp32.cpp` already - asks the C6 for its firmware version, bounded to two attempts, exactly to detect an absent or - incompatible slave. The WiFi cascade never asks; it walks straight into `esp_wifi_init`. Gate the - cascade on that answer (or on `esp_hosted` reporting the link up) and degrade to "no network, - C6 not answering" as a Network status. -2. **A failing cascade must not abort.** Robustness says degrade visibly, never crash. Even with the - gate, a link that dies later would hit the same watchdog: the forwarded calls need to run off the - render thread, or with a timeout shorter than the watchdog, so the worst case is a status line. - -Practical today: the shield runs the eth-only image without WiFi, or its C6 gets the ESP-Hosted slave -firmware flashed (the `ships: false` note on the variant in `build_esp32.py` says why that is not -yet reproducible). The Improv script's eth-only rule and the catalog's two firmwares for the shield -are already in place. - -**Everything tried on 2026-09-08 was reverted; both P4s now run `esp32p4rev1-eth` and are stable.** -The tree is back to its pre-attempt state except for the catalog, which now lists BOTH P4 firmwares -for the MHC-WLED shield so the installer offers the WiFi variant at all (it listed only the eth one, -which is why the WiFi image could not be picked). What the day established, so the next attempt does -not repeat it: - -- **The C6 link is INTERMITTENT on this shield, not simply dead.** After the vendor C6-update tool - ran (it never completed an OTA: it looped on "Not able to connect with ESP-Hosted slave device"), - the link came up and the board reached a DHCP lease on WiFi, first attempt, no retries. A power - cycle later it was back to `sdmmc_send_cmd returned 0x107` and a reboot loop. Any future fix has - to survive a cold boot, not one lucky session. -- **A patient STA retry (MoonLight's 5 s cadence, ~2 min budget) was implemented and reverted.** It - never fired on either board: both connected on the first attempt when the link worked, and when it - did not the board died before the render loop. Keeping untested robustness was not worth the - surface. The reasoning still holds and is worth redoing WITH a repro: MoonLight retries forever - and never tears the radio down, and its own comment warns that toggling WiFi on a co-processor - board forces costly esp_hosted reinit cycles, which is exactly what our AP fallback does. -- **Espressif does not endorse retrying into readiness.** Their esp-hosted troubleshooting puts - "not able to connect with slave" and SDIO 0x107 down to wiring, pull-ups, signal integrity, or a - host/slave VERSION MISMATCH: "use the same version for master and slave". Our host is 2.12.13; the - shield's C6 is on the factory build the vendor tool calls 0.0.6 (target 2.0.17). Updating the C6 - needs Method 2 (direct USB/UART to the C6), since Method 1 needs the very link that is broken. - -**A fail-fast gate was tried on the bench (2026-09-08) and REVERTED.** The idea was right and both -signals were wrong. `platform::wifiHardwareReady()` gated the STA and AP init, and it STOPPED the -reboot loop dead: the shield ran 80 s with 0 reboots and 59 ticks, logging "WiFi co-processor link -not up" instead of aborting. But on the bench P4 (.139), whose link demonstrably works, the same -predicate read FALSE at its 26 s cascade and refused WiFi on a healthy board. Two signals were -tried, neither is usable as a readiness test: - -- **`ESP_HOSTED_EVENT_TRANSPORT_UP`**, subscribed on the default event loop (both lazily and from - `ensureNetifInit`, i.e. before esp_hosted's task posts it). Never observed arriving. esp_hosted - posts through its own `g_h.funcs->_h_event_post` indirection; where that lands was not chased. -- **`esp_hosted_get_coprocessor_fwversion` returning a non-zero version.** Also false on .139, which - matches what `coprocessorWifi()` already records: on a live link this RPC times out rather than - answering, which is why that function is bounded to two attempts. - -**Not a regression: the RELEASED v4.0.0 image fails the same way on this shield** (bench, same day). -Flashed from the web installer after an erase, it logs `App version: v4.0.0` and then: - -``` -W (13438) H_SDIO_DRV: Reset slave using GPIO[54] -W (13438) gpio: conflict found for GPIO[54] -E (14988) sdmmc_io: sdmmc_io_rw_extended: sdmmc_send_cmd returned 0x107 (ESP_ERR_TIMEOUT) -E (14988) H_SDIO_DRV: failed to read registers -``` - -and reboots without ever reaching the render loop. The same v4.0.0 image serves WiFi on the bench -P4 (.139) with no SDIO error and no GPIO 54 conflict, and no MoonLight config on any P4 entry -references GPIO 54 (esp_hosted drives it as the slave reset). The two boards differ in silicon -revision: this shield is chip rev **v1.0**, .139 is **v1.3**. So the C6 side of this shield does not -come up, which is a board/slave-firmware matter rather than anything in our WiFi path, and the -firmware's job is only to degrade rather than reboot. - -**The product owner reports this same shield ran WiFi under MoonLight on IDF 5.5**, which makes a -dead C6 unlikely and points at host-side SDIO configuration on 6.1. What was checked (2026-09-08): - -- The SDIO data pins are NOT board-preset dependent: they come from the SLOT choice - (`ESP_HOSTED_SDIO_SLOT_1`, fixed silicon pins), so swapping `ESP_HOSTED_P4_DEV_BOARD_*` presets - would not move them. The preset mostly moves SPI pins, which we do not use. -- `ESP_HOSTED_SDIO_GPIO_RESET_SLAVE` defaults to **54 on any P4** regardless of preset, so the - `gpio: conflict found for GPIO[54]` line is esp_hosted resetting the slave twice, not a wrong pin - from our config. No MoonLight P4 entry references 54. - -**Our SDIO configuration is not the difference.** Diffed against a known-good local reference -(`ewowi/FlowFields/sdkconfig.esp32-p4`, an IDF 5.5-era P4 build), every hosted setting is IDENTICAL: -pins (CLK 18, CMD 19, D0-D3 14-17), `GPIO_RESET_SLAVE` 54, `RESET_ACTIVE_HIGH=y`, `CLOCK_FREQ_KHZ` -40000, `SLOT_1`, `4_BIT_BUS`, `RESET_DELAY_MS` 1500, `RX_STREAMING_MODE`. So reset polarity, reset -pin and SDIO clock are all ruled out as differences, and the identical released v4.0.0 image works -on .139 and not on this shield. Same firmware, same config, two boards, two outcomes: what remains -is on the board side (C6 slave firmware, power/strapping, or the SDIO traces on this revision), and -the only MoonLight work left is the degrade-instead-of-reboot fix above. - -What esp_hosted's private `is_transport_tx_ready()` reports is the signal the slave-ready loop -itself polls, but it lives in a PRIVATE include dir (`host/drivers/transport`, not in the -component's `pub_include`), so reaching it means either adding that dir to our include path or -asking upstream to export a readiness getter. That is the next thing to try, and it needs the -.139-still-associates control run alongside the shield-stops-rebooting one: the fix is only right -when BOTH hold. - -## The persisted `firmware` variant survives a flash to a different variant (2026-09-08) - -`SystemModule` writes the compile-time `kFirmwareName` into its `firmware` control at -`defineControls()`, and its own comment states the intent: "written from kFirmwareName on every boot -rather than read from the file: the compile-time constant is the truth". But the control is -`addText`, which is PERSISTED, and the config load runs after `defineControls()`, so a saved value -from a previous image overwrites the compile-time one. - -Bench (MHC-WLED P4 shield, flashed from `esp32p4rev1-eth-wifi` to `esp32p4rev1-eth`): the Firmware -card correctly reported `esp32p4rev1-eth` (it reads the running image), while `/api/modules/System` -still reported `esp32p4rev1-eth-wifi` from the old config. The two disagreed on the same board, and -the stale one is the field an outside reader trusts. - -Why it matters beyond cosmetics: the comment explains this value exists so **MoonBase** can narrow -the recovery image list to one variant. A stale value points a recovering board at the wrong image, -which is the failure that list exists to prevent (picking an `esp32s3-n16r8` build for a Zero -installs a flash layout the board does not have). - -The fix has to keep the value readable by another image (that is why it is persisted at all) while -making the compile-time constant win: re-assert `kFirmwareName` after the config load rather than -only at `defineControls()`, and pin it with a test that loads a config naming a DIFFERENT variant -and checks the control still reads the compiled one. - -## MoonCloud: share a MoonLive script over MoonTalk (2026-09-10) - -Sketched 2026-09-10 while Talk was built. **The unique feature nobody else has**: MoonLight ships a scripting language whose programs are about a kilobyte of text, and a message board between devices. Together they mean a script someone wrote on their wall is one tap from running on yours. - -**What makes it plausible.** The scripts are tiny: the shipped `.mle` files run 780 to 1907 bytes and the whole library of twenty-odd is 36 KB. A script is self-contained by design, so the text is the artifact. And the device already compiles and runs arbitrary script text safely, which is the hard half and is done. - -**The design work is that a script is five to seven times a message.** Talk caps a message at 280 characters, deliberately: a chat message is a sentence, and one caller must not fill the table. Three options: - -- **A second endpoint** (`/api/script`) with its own size cap and table, and a message that references it by id. The chat table stays a chat table, a script is fetched when someone wants it, and a board read stays small. Most work, cleanest shape, and the recommendation. -- **An attachment column on the message**, capped separately. One table and one post, at the cost of every board read carrying script text unless the query is careful. -- **Share a URL.** A script already lives in the user's File Manager and a device could serve it over the LAN. Free, and limited to one network, which is the case that makes the feature interesting. - -**A shared script is code from a stranger**, in a way chat text is not. Three questions the design answers rather than discovers: - -1. **Loading is explicit and reversible.** A script arrives as something to look at, and taking it leaves what the user already has intact. `/moonlive` (user) shadowing `/.moonlive` (factory) is the existing trap. -2. **The device is the sandbox.** MoonLive reaches no filesystem and no network, so the blast radius of a hostile script is the LEDs and the render budget. Worth confirming rather than assuming: a script that never yields is a watchdog reset, which is a denial of service on someone's wall. -3. **Attribution and removal.** Any sender id can be claimed, and a message stays once posted. Fine for chat, weaker for code. - -Depends on Talk shipping and on the server being deployed. diff --git a/docs/work/future/backlog-drivers.md b/docs/work/future/backlog-drivers.md new file mode 100644 index 000000000..83123c5b7 --- /dev/null +++ b/docs/work/future/backlog-drivers.md @@ -0,0 +1,590 @@ +# Backlog: Drivers + +To-build items for Drivers, grouped by module, each with its fields on one line. Other areas: [the future index](index.md). + +## DriverBase + +### A control change reaches a driver at the frame boundary (2026-10-21) + +Kind: Restructure + +With the render split on, a driver's `tick()` runs on the encode task while its `onControlChanged`, `onCorrectionChanged`, `tick20ms` and `tick1s` run on the main loop, and only structural changes and `setEnabled` park the encode task first. +An audit of `src/light/drivers/` found the main loop writing state the encode task reads: `DriverBase`'s `correction_` (rebuilt on a control change, ramped by `tick20ms`, `motionHeld` set by `tick1s`), `effectiveBrightness_`, the `wire_` buffer a channel-count change reallocates, the `start` and `count` window; HueDriver's pairing and light tables; NetworkSendDriver's `dest_` addresses on the inline send path and its `corrected_` buffer; PwmLightDriver's `lights_` and `duty_`; RmtLedDriver's and ParallelLedDriver's whole geometry through `parseConfig` and `reinit`, ParallelLedDriver calling `parseConfig` and `setStatus` from the encode task itself; RtspDriver's and ParallelLedDriver's `setStatus` from `tick()`; PreviewDriver's `downscale_` and `keptIdxAllocFailed_`. +The textbook move is the one live reconfiguration already promises, a change applied on the next frame: the main loop records the change, and `Drivers::tick` applies it at the frame boundary, after `quiesceEncode()` and before it notifies the worker, so a driver's state has one writer at a time. The thread sanitizer lane reports none of these today, since no test changes a control while the encode task runs; the first test that does pins the move. +The same pass covers ParallelLedDriver's `peripheral` swap, which disengages the render split through the quiesce hook and, as `peripheral` is not in its `affectsPrepare`, leaves it off until another prepare runs. + +### An identity correction is one copy (2026-10-14) + +Kind: Restructure + +Every driver runs `Correction::apply` on every light, even when the correction changes nothing: brightness 255 (global and local), the linear curve, a fixture profile whose channels match the layer's, and no dimmer, white or motion channels. +The hottest-path rule says that case is a copy: `Correction` knows at rebuild whether it is the identity, and a driver that sends its buffer whole (Network Send, the panel cards) copies the layer's bytes instead of looking each one up. +The default CIE curve is not the identity at 255, so the skip applies only with the linear curve. It applies to every driver once the sender already sends the output's byte order; network and panel drivers then send one copy, while LED strips still encode bits but drop the per-byte lookup and reordering. + +### Stop reading fixtureRef after the release that follows 2026-10-05 + +Kind: Restructure · Trigger: the release after 2026-10-05 + +- **Stop reading `fixtureRef` after the release that follows 2026-10-05.** A driver saves its profile's name in `fixture` since then, and `DriverBase` still reads the hidden `fixtureRef` an older config saved beside a row number. With the next release out, remove the control and `DriverBase::adoptLegacyName` with its two calls, and add a `migrate.js` entry mapping `fixtureRef` onto `fixture` so an older backup still restores. + +## GpioDmaPeripheral + +### A '595 expander on the Teensy's GPIO-DMA bus + +Kind: Feature · Platform: Teensy + +`supportsPinExpander()` is false, so a Teensy drives 16 strands at most. The port write carries no shift clock of its own, so the clock has to come from the writes themselves, at a DMA rate not yet measured. TriantaduoWS2811 drives 32 strands from 3 pins through FlexIO clocking '595s at zero CPU cost, and FlexIO is what the Teensy plan set aside for want of free contiguous pins beside the W5500. The reasoning: [the LED-driver analysis](analysis-leddriver.md#the-pin-expander-on-the-other-peripherals). + +## HlsDriver + +### HLS upscaling is cache-hostile on large walls (2026-08-28) + +Kind: Investigation + +`HlsDriver`'s `scale` control replicates each light into a scale x scale block. Measured on the +bench P4 at 128x128: **~1 ms per frame at 1:1, ~60 ms at scale 4** (a 512x512 output). The frame +is 16x larger, so ~16 ms would be the honest cost; the extra 4x is the access pattern. The loop +walks LIGHTS and writes each block as `scale` short rows, the first filled and the rest copied +from it by `memcpy`, scattered across a 786 KB buffer, so consecutive lights touch distant addresses and every write misses cache, where the +1:1 path writes straight through sequentially. + +**Not urgent, because the default path never hits it:** auto-scale only engages on walls below +the encoder's floor ([watching the output](../../reference/watching-the-output.md)), where the output is small by construction. The expensive case is a manual scale on an already-large wall, which is also +where upscaling has the least to offer. + +**The fix when it earns its place:** iterate the OUTPUT rows rather than the input lights, so +writes are sequential: for each output row, walk its source row once and emit `scale` copies of +each light's color, then `memcpy` that finished row to the remaining `scale - 1` rows of the +block. Same output, one pass through the destination in address order. + +## Hub75Driver + +### The shift-register driver init + +Kind: Feature + +A HUB75 "panel" is a family, and the differences are not discoverable from its dimensions. FM6124 and ICN2038S panels need a different init sequence; WLED-MM picks FM6124 for "outdoor" panels, and its comment says the symptom of the wrong choice is a dark panel or pastel colors. Build trigger: a panel showing that. + +### Repaint only what changed + +Kind: Investigation + +`tick()` corrects and encodes the entire frame every time, whatever moved. The library tracks a dirty bit per pixel in `setPixelColor` and repaints only those in `show()`. For a full-frame effect the two are comparable; for a sparse overlay, a clock, or a mostly-static scene, the difference is the whole encode. + +Worth measuring before building: our encode is a tight loop over a packed buffer, and a dirty-bit test per pixel is not free either. The win is real only where the frame is mostly unchanged. + +### Depth that degrades instead of failing + +Kind: Feature + +`bitDepth` is a user control with a hard ceiling, so asking for more than the wall can hold fails init with a memory error. WLED-MM reduces depth automatically by pixel count (8 bits under `MAX_PIXELS_8BIT`, then 6, 4, 3), so a large wall gets a dimmer picture rather than no picture. + +That fits the allocate-and-degrade rule every other output seam here follows. It interacts with the weighting item below, since the library's `lsbMsbTransitionBit` is the same trade expressed as refresh rather than as memory, so build them together rather than adding two overlapping automatic reductions. + +### Four-scan panels whose physical layout is not their logical one + +Kind: Feature · Trigger: someone owns such a panel + +Some panels scan in a pattern where the physical row order does not match the logical image, and driving them without remapping produces a scrambled picture that looks like a wiring fault. The library handles this with `VirtualMatrixPanel` and `setPhysicalPanelScanRate` (FOUR_SCAN_32PX_HIGH, FOUR_SCAN_64PX_HIGH), and WLED-MM exposes them as bus types 105 to 108. + +Our `Hub75Geometry::rowsPerScan()` handles a panel driving more than two rows per address step, which is the common multi-pair case, but not an arbitrary remap. Build trigger: someone owns such a panel. The remap belongs in the Layout rather than the driver, since it describes where lights physically are, which is what a Layout is for. + +### Repeated planes: full top brightness and 8-bit depth + +Kind: Feature · Trigger: a wall that reads too dim at 6-bit, or a user who needs 8-bit gradients + +The planes are weighted by their lit window ([HUB75 panels](../../reference/hardware/hub75-panels.md)). That costs top brightness, since the lower planes are dark for most of their pass: full white is about 47% of the row time at 4-bit and 33% at 6-bit. It also caps depth at 6, where plane 0 of a 64-column row still lights 2 words. The reference library repeats the upper planes instead, and windows only the lowest (`lsbMsbTransitionBit`, chosen automatically for a refresh floor): about 95% top brightness and 8-bit depth, at roughly 290 Hz for one 64x64 panel. + +**The approach: repeat DMA descriptors, not data.** Plane `p` is stored once and the descriptor chain points at it 2^p times, so the weighting costs descriptors rather than frame memory. mrcodetastic's ESP32-HUB75-MatrixPanel-DMA does exactly this (`configureDMA`, the `for (int k = 0; k < (1 << (i - lsbMsbTransitionBit - 1)); k++)` loop), and it carries a refinement worth taking with it: `lsbMsbTransitionBit` is a dial, not a constant. Planes below it show once each; planes above get full weighting. The library raises it until a configured minimum refresh is met, trading exactness at the low end for rate. That makes depth and refresh one control instead of two fighting ones. + +**What it costs here: a new LCD_CAM backend.** The blocker is not the weighting loop, it is that `esp_lcd_panel_io_tx_color` owns its descriptors and gives no way to aim several at one buffer. The library programs the silicon directly instead (`gdma_new_channel`, `gdma_connect(GDMA_TRIG_PERIPH_LCD)`, hand-linked `dma_descriptor_t.next`). So this is the same step this project already took for WS2812, from `platform_esp32_i80.cpp` (675 lines, esp_lcd) to `platform_esp32_moon_i80.cpp` (1,952 lines, own GDMA), with the same hazards that took bench bisects to find there: re-mounting the chain per frame, terminator placement, producer/consumer headroom. + +**Parlio cannot express it.** Its only repeat primitive is `loop_transmission`, which repeats the whole buffer forever with no per-plane control. Weighted depth would be an LCD_CAM capability, and the `peripheral` select has to say so rather than offering a choice that silently changes the picture. + +Build trigger: a wall that reads too dim at 6-bit, or a user who needs 8-bit gradients. It stays automatic, chosen from `bitDepth` and a refresh floor, with no new setting. + +## HueDriver + +### Migrate HueDriver's name buffers to ScratchBuffer (finish the ScratchBuffer sweep) + +Kind: Restructure + +`HueDriver::ensureNameBuffers()` / `freeNameBuffers()` is the alloc-in-build / free-in-release / null-guard bundle that `ScratchBuffer` absorbs — and it does **no** `dynamicBytes` accounting, so the module's memory readout under-reports its Hue-light name buffers. It was skipped in the ScratchBuffer migration (its buffers are `char[]` name tables, not the grid-sized state the sweep targeted). Now that `DriverBase::release()` chains to `MoonModule::release()` (fixed on PR #43), a `ScratchBuffer` member here would free correctly on disable and self-report its bytes. Mechanical, one-file, behaviour-preserving. Flagged by the 👾 Reviewer on PR #43. + +## MoonI80Peripheral + +### The ring's refill jitter (2026-10-22, from the internal RAM plan) + +Kind: Investigation · Platform: S3 + +LCD-MM's refill interrupt fires every 89 µs at 3-row slices, yet almost every second it goes about 1.1 ms without firing. +So `ringAuto` keeps 54 rows of lead ([LED drivers](../../reference/hardware/led-drivers.md)), where hpwit's S3 driver runs on 10. + +Measured with `ringDbg`'s `gap`, `ml` and `c` fields. The gap ends mid-frame as often as at the idle tail. + +Ruled out: +- the flash cache: `co` stays 0; +- the core: registered on core 1 it was the same, and it broke the cache guard, so it stays on core 0. + +Next, the gap measured against the frame's own events, the arm and the snapshot, then against other interrupts on core 0. + +A lead of 10 rows would free most of the ring's internal RAM. + +### MoonI80 streaming ring: open instruments and cleanups + +Kind: Investigation · Platform: S3 + +The ring ships and is wall-verified through 48 strands × 256 (12,288 lights). The mechanism lives in [MoonI80Peripheral.h](../../../src/light/drivers/MoonI80Peripheral.h) and [LED drivers](../../reference/hardware/led-drivers.md); the design arc in `docs/work/past/plans/`. Open items: + +- **Last-8-panels white flash, the "44-46 flash" (OPEN: the lanes ≥ 32 encode path).** On the 48×256 wall, brief WHITE/bright flashes (not wrong colors) over an otherwise-correct image, confined to strands 40-47 (the last 8 panels), mostly panels 44-46, wandering within the last 8. **Brightness-gated: clean below 5, flashes at ≥5**: since brightness scales pixel values through a LUT, below 5 the frame collapses to near-all-zero bits, so the gate really means *the flash needs SET (one) data bits*. **The same physical wall runs clean on hpwit's driver, so it is OUR driver, not the hardware.** **A live pin reorder located it:** the reorder moved the strands and the flash stayed with lanes 32 to 47, so it is the lanes ≥ 32 (`maskHi32`) encode path under the wall's exact active set, not the last '595, GPIO 17 or the latch bit. Ruled out by live hardware tests (do NOT re-chase): (1) the encode source (PSRAM vs internal; the ISR-source staging fix did nothing, reverted); (2) the ISR/lapping regime (the flash is on prime-encoded rows too); (3) it being a tail-*rows* phenomenon; (4) shift-clock margin via `shiftOverclock` (already at the 20 MHz OFF setting, still flashes); (5) bus-bit latch adjacency (a `latchBitHigh` diagnostic moved the latch to bit 7, still flashes). The encoder passed a host fuzz over RANDOM masks (`ParallelSlots.h` byte-for-byte correct), which never hit the wall's trigger: next, reproduce on the host with the wall's real geometry, active set and content, starting at the 64-bit mask round-trip (`maskLo32 | maskHi32 << 32` in `ParallelLedDriver.h` against the encoder's re-split in `ParallelSlots.h`). All diagnostics from this investigation are reverted, and the tree is clean. +- **MoonI80 direct mode flickers below ~30 lights on a PSRAM frame buffer (OPEN, cause unproven).** The write-up, what is established and the theory to test: [lessons.md](../../work/past/lessons.md#mooni80-flickers-below-30-lights-on-a-psram-frame-buffer-open-cause-unproven). Next instrument: `loopbackTest` + `loopbackIntrusive`, which captures what the peripheral actually emitted and separates a corrupt frame from a stalled transfer (needs the RX jumper pin — the SE16 routes its LED outputs, so pick one that can read back). +- **Ring bus init hard-fails instead of stepping down when `ringBufs` is raised past what RAM allocates** (wall went dark until the control was lowered again; the pool alloc steps down but a later allocation, likely the descriptor link list, does not). A control change must degrade, never dark the output. +- **~1-frame white/colored flash every ~5 s** seen at some configs — plausibly fixed by the frame-close latch word (a strand whose last data bit ended HIGH missed its reset that frame); soak-observe on the wall before closing. +- **Prime barrier fps cost.** The ring's prime holds a busy-wait until the previous frame's deterministic wire end (`waitWireDrained` in `primeRingRange`). The wait is ~0 when the snapshot + render gap already span the wire, but on fast frames it serializes wire → prime and caps fps at 1/(wire + snapshot + prime). If the fps work wants that overlap back, the barrier can go finer-grained (per-buffer: buffer b is safe once the drain passes slice `b + nSlices − ringBufs`) — measure first. +- **Multi-strand loopback**: the instrument drives one `loopbackStrand`, so it is structurally blind to a multi-strand fault (it passed while the wall was visibly corrupt). +- **Shift-mode loopback host coverage**: nothing in the suite drives shift-mode loopback end-to-end through the mock bus — the detection gap that let two loopback bugs live unnoticed. (The stall bug itself is fixed: capture-first alloc + pool step-down; verified on two boards.) +- **Loopback teardown leak + heap fragmentation**: cycling the private-bus loopback drops ~80 KB internal and the heap stays fragmented (measured maxBlock 13–44 KB with 240–310 KB free). Capture-first + step-down made runs reliable despite it, but the leak itself stands — chase when the loopback is next touched. +- **Ride-the-live-ring loopback — parked**: built (`loopbackIntrusive` + the snapshot pattern hold) but an RMT-RX cannot capture on a GPIO the LCD_CAM is actively driving (reads 0 sym). Revival path: briefly output-detach just the RX pin for the capture window. +- **Diagnostic surface** (`ringDbg` incl. tw/ts/tp + sg/se, the dbg statics, the `kCyPerUs = 240` hardcode): kept deliberately through the tuning era; gate/remove when the ring fps work closes. +- **fps header reports the module TICK rate**, not the frame rate (`frameTime` is the real one); any fps claim predating 2026-07-17 reads with that in mind. +- **`ringAuto` "just works" — verify the geometry pick on smaller configs.** The pick is wall-verified at 48×256; the smaller common configs (16×256 and below) still need a bench pass. If any has a strictly better geometry `ringAuto` misses, teach it that ONE *measured* rule (no per-effect/per-density heuristics — the flicker was never geometry). `ringAuto` itself stays visible as the recourse until this proves it always picks right, then it can go expert-only too. + +### The ring's memory readout counts one buffer, not the pool (CodeRabbit, 2026-09-09) + +Kind: Bug · Platform: S3 + +`LedPeripheral::busHeapBytes()`, which MoonI80 does not override, counts `busCapacity()`, +the size of ONE buffer, twice when `busBuffer(1)` exists, which +is right for the double-buffered path and wrong for the ring, where `createRingState` allocates +`ringBufs` slices (up to 30 at the 48x256 geometry) plus the shared zero-pad. So a ring board +under-reports its DMA memory by roughly the buffer count, on the card that exists to make exactly +that number visible. + +Not a correctness bug: the memory is spent either way, and nothing sizes an allocation from this +value. It is a reporting bug in the one readout a user consults before adding lights. + +**What it takes:** a `busHeapBytes` override on MoonI80 (the ring knows `ringBufs * bufBytes + pad` +at creation). Wants a ring-mode +accounting test alongside, since the existing ones only cover the non-ring path. Touching +`createRingState` means a bench pass on the wall, which is why this is its own change. + +### MoonI80 ring: the first frame after a rebuild trips a transient give-up status (2026-07-16) + +Kind: Bug · Platform: S3 + +A cosmetic residual left after the rebuild-wedge fix (below): on boot, and for a beat after any shift-ring rebuild, the driver shows **"output stalled"** even though `wireUs` reports live completions — the *first* frame after a fresh ring build occasionally misses its completion window and trips the dead-frame give-up before the ring settles, so the stale error latches until the next interaction clears it. It is NOT the old wedge (that stayed dead until reboot; this self-clears on any control edit and the ring is genuinely driving underneath). Two clean fixes to weigh: (a) don't count the very first frame after a rebuild toward `deadFrames_` (give the fresh ring one grace frame), or (b) have the give-up retry re-derive status the moment `wireUs` shows a real completion. Low priority — the LEDs drive correctly; only the status string is briefly wrong. + +## NetworkSendDriver + +### ArtPoll discovery: know which tubes are alive + +Kind: Feature + +`NetworkSendDriver` unicasts to a list of receivers (`hosts` + `lightsPerHost`), which is the Art-Net-4-conformant model. What it cannot do is **tell whether a receiver is actually there**: UDP is fire-and-forget, so a dead tube is invisible to the sender. The spec's own answer is discovery — *"The transmitting device must regularly ArtPoll the network to detect any change in devices which are subscribed"* — and it is the natural next increment. + +**Why it earns its place (product-owner experience, 2026-07-13):** a dead tube in a prior setup *made the other tubes hiccup*. The mechanism is that a send to an unresolvable address stalls or errors **inside the frame loop**, delaying the packets for every destination after it — so one dark tube degrades the live ones, every frame. The send loop tolerates that (a failed send drops only that packet), but tolerating is not the same as **knowing**: with discovery we can simply **skip destinations that haven't answered**, which removes the stall at its source rather than absorbing it. + +**Scope (~130–150 lines + tests; the receive half already exists).** `ArtNetPacket.h` already builds+parses, `UdpSocket` already binds and reports the sender's IP: +- **ArtPoll send** — broadcast a 14-byte poll every ~3 s (this is the one packet Art-Net *does* broadcast, by design). +- **ArtPollReply parse** — fixed-layout 239-byte reply: node name, IP, and its subscribed universes. +- **A node table** — IP → last-seen; mark offline after ~9 s of silence (3 missed polls, the spec's own cadence). +- **Use it:** skip offline destinations in the send loop; surface the live/dead list as a read-only status. **Bonus, and arguably the real prize: auto-populate `hosts`** — the user stops typing addresses at all, which is how a professional controller behaves. + +Do it as its own increment. The multi-destination unicast it builds on has shipped. + +## PanelCardDriver + +### MoonLight hangs when a USB network adapter is re-plugged (2026-09-28) + +Kind: Bug · Platform: desktop + +Unplugging and re-plugging a USB Ethernet adapter while PanelCardDriver is active wedges the whole application. Measured: the render task stops logging entirely (0 bytes in 8 s), one thread spins at 74% of a core while the other two sit in `Wait`, the listener still accepts so connections pile up unanswered (11 of them), and HTTP never returns, tested to 45 s. The last line written is always `NetworkModule: Ethernet up (recovered from Idle)`, so it enters the spin somewhere in the Ethernet-recovery path on the render thread rather than blocking on a lock. + +Reproduced once, on a bench with a Realtek USB GbE adapter feeding a ColorLight card. The trigger is ordinary: anyone driving panels from a USB dongle does this. The suspects are the pcap calls that recovery reaches, since `ethBindRawInterface` runs `pcap_findalldevs` and `pcap_open_live` on an adapter that has just re-enumerated, and both are called from `prepare()` on the render task. Worth a second reproduction with a debugger attached before guessing further: a spinning thread with a live listener is a loop, not a deadlock, and the loop has a home. + +### The interface picker re-enumerates adapters on every control rebuild (2026-09-28) + +Kind: Bug · Platform: desktop + +`rawInterfaces()` caches nothing, and `defineDriverControls()` calls it on every rebuild of a driver that owns an interface Select. On Windows `pcap_findalldevs` opens each adapter in turn and measures ~2.3 s, so every control change on PanelCardDriver, every `/api/types` that builds the type catalog, and every UI refresh paid it. Measured with a panel driver present: `POST fixture` 2.2 to 6.7 s, `/api/types` 2.3 to 4.4 s, `/api/state` 2.2 s. The same payloads on a config with no panel driver: 23 to 36 ms. POSIX is not affected, since it enumerates with `getifaddrs`. + +A cache with a 60 s window took those to 8 to 35 ms and was reverted before the release as too much risk for hardware-critical code. Two things learned from doing it: the window has to outlast a person pausing to think, because a 3 s one leaves every deliberate control change paying full price; and skipping the rebind needs `ethLinkUp()` in the condition, because an open pcap handle whose link is down still wants rebinding and guarding on "already bound" alone removes the only recovery a user has. Scope any cache to Windows, since it buys POSIX nothing and costs it hot-plug latency. + +### Multi-card walls: does a daisy chain work today? + +Kind: Investigation · Trigger: ask before building + +The ColorLight format has **no card addressing in the PIXEL path**: the destination MAC is a fixed +constant and every card filters on it, so every card on a segment shows the same image. A user with +six cards on a switch observed exactly that. + +The DISCOVERY path does distinguish them. A discovery reply (0x08) carries a controller number at +payload offset 0x62, and the acknowledgement echoes it plus one, which is how a sender tells several +cards apart. Documented by a reader of [Harald Kubota's protocol +write-up](https://hkubota.wordpress.com/2022/01/31/winter-project-colorlight-5a-75b-protocol/) and +confirmed by its author. That is an identity, not a destination: it does not let a sender aim pixel +data at one card, so the same-image behavior above stands. It is what a per-card brightness or +color-temperature feature below would key on. + +The industry-standard answer is **daisy-chaining** — a sending card's ports each drive a chain, and each card takes its region by position in the chain. That user works around it with per-card VLANs and a managed switch instead, which he built for throughput and for per-card color-temperature grouping across mixed panel batches; he described it as his own solution, not a standard. + +**Establish first whether a daisy chain already works with MoonLight** (one contact has a 96K daisy-chained rig). If the cards self-assign by chain position, the standard multi-card case is already solved and nothing is needed. Only if it does not work is there a feature here, and it should follow the daisy-chain standard rather than the VLAN workaround. 802.1Q tagging is technically a clean fit for a raw-L2 sender (the tag is part of the Ethernet header, the switch strips it before the card, so card firmware is unaffected), but it serves one bespoke architecture. + +### Smaller asks from the same thread + +Kind: Feature + +- **Read the wall layout from the ColorLight cards.** The cards can report their configuration and at least one user's own tool already does it; it would remove the manual layout step. +- **Per-card color temperature and brightness**, via the ColorLight sync-packet bytes, grouped by sync group — used to color-match mixed panel batches live. + +### Configure a ColorLight card without Windows (2026-10-16, asked) + +Kind: Feature + +Asked on Discord by robwomp: a way to set up ColorLight receiving cards from a Linux command line, so a ready-made MoonLight image (a Pi running the desktop build with PanelCardDriver) could configure the card itself and make a card and its panels plug and play. + +Today the card's configuration (panel size, panel count, scan, driver IC) is written once with ColorLight's LEDVision, and its firmware with LED Upgrade, both Windows-only ([panel cards §4 and §7](../../reference/hardware/panel-cards.md)). The protocol for those writes is ColorLight's own and undocumented; the pixel and discovery frames are known from [Harald Kubota's write-up](https://hkubota.wordpress.com/2022/01/31/winter-project-colorlight-5a-75b-protocol/), the configuration frames are not. + +The route: capture what LEDVision sends while it writes a known configuration (Wireshark on the card's Ethernet), decode the frames, and replay them. [REA](https://github.com/morluto/rea) (MIT) does the decode step: its packet mode reads the capture through TShark and reports repeated values and field candidates, run from the terminal (`npx rea-agents …`) rather than installed into the agent's configuration. Its static .NET inspection could read how LEDVision builds the frames, if LEDVision is .NET and its license allows it. Then replay them from PanelCardDriver as a "configure card" action taking the wall's panel size, count, scan and IC. Reading the configuration back (the "read the wall layout" ask above) shares the same decoding. The firmware path stays with LED Upgrade: a bad write bricks a card, which a configuration write does not. Ask before building: it needs a card on the bench and a Windows machine running LEDVision for the capture. + +## ParlioPeripheral + +### A '595 expander on Parlio + +Kind: Investigation · Platform: P4 + +`supportsPinExpander()` is false, so a P4 on Parlio drives 16 strands at most. Parlio generates its pixel clock itself and `platform_esp32_parlio.cpp` leaves `clk_out_gpio_num` unconnected since WS2812 ignores it; connected, it is the '595's shift clock, and the latch rides as a bus bit as it does on `i80`. Whether the clock output and the bit packing carry the expander's eight words a slot is a bench question, and the eightfold frame meets the single-transaction ceiling the chunked-transfer item lifts. The reasoning: [the LED-driver analysis](analysis-leddriver.md#the-pin-expander-on-the-other-peripherals). + +### P4 Parlio streaming ring: lift the ceiling past about 21K lights + +Kind: Feature · Platform: P4 + +**Port the ring concept to the P4 Parlio path**, to drive far more than its current whole-frame ceiling. troyhacks' MoonLight Parlio driver reaches **~21K LEDs RGB (~16K RGBW)** at 16 lanes — but NOT by materialising the whole encoded frame: he stages into a **fixed ~512 KB PSRAM buffer** and DMAs it out in **64 KB chunks** (`max_transfer_size = 65535`), so the DMA never needs the whole frame contiguous. That is the SAME idea as our MoonI80 ring, applied to Parlio on the P4 (where — unlike the S3 shift clock — the DMA *can* sustain PSRAM reads at the WS2812 rate). His ceiling is a *chosen buffer size*, not a hardware wall, so it caps at ~21K. + +**Two tiers, in order:** +1. **Match him (moderate):** chunk our Parlio DMA the way he does (bounded PSRAM staging + 64 KB bursts) instead of one contiguous encoded frame — this alone lifts our P4 Parlio ceiling to his ~16–21K. Our Parlio path already uses PSRAM; the change is the chunked-DMA transfer shape, not a new architecture. +2. **Beat him (the ring):** run our MoonI80 **streaming ring** on P4 Parlio — small internal buffers, refilled per drain from a PSRAM source, holding internal RAM *constant* regardless of light count (the hpwit model our ring already implements). troyhacks' 512 KB buffer is still *bounded*; the ring is not, so this goes past ~21K to light-count-independent. It reuses the ring mechanics we already have (linear self-terminating chain, per-drain refill, drain-count termination); the P4 Parlio backend gets a ring variant beside its whole-frame path, the way MoonI80 has both. + +**Prerequisite / sequencing:** tier 2 wants the **ISR-refill + `MM_HOT`** work done first (same as the classic ring above), since a high-rate Parlio refill benefits from the ISR-grade determinism. Confirm the CURRENT P4 Parlio tested ceiling first (re-measure on the P4 bench) before claiming a head-to-head — the ~21K figure is troyhacks' *buffer-fit* limit, not a verified-on-wire number, and his code steps the clock down at 256/512 LEDs per lane (signal integrity at long strands), which our own measurement must account for. Reference: the parlio source is `src/MoonLight/Nodes/Drivers/parlio.cpp` in the predecessor; our Parlio backend is `platform_esp32_parlio.cpp` + `ParlioLedDriver.h`. + +## PreviewDriver + +### Preview does not resume after a long tab hibernation (observed once, 2026-08-27) + +Kind: Bug · Platform: desktop + +Desktop instance at 1024x1024: after the browser tab sat backgrounded for about an hour, the +preview pane stayed static on return. PreviewDriver ticked at ~1 us (no standing request served) +while a FRESH WebSocket client received frames normally, and the Drivers card showed the +encode-worker-stalled latch. A page refresh reportedly did NOT revive it; toggling `multicore` +(a Drivers re-prepare) did. Suspects: the tab-hide hibernation path's tracked retry vs the +wake-up re-request, or per-driver lease state that only prepare() resets. Needs a reproduction +with the WS uplink logged before it can be fixed. + +### Self-describing preview frame header + +Kind: Restructure + +The preview wire format is a private opcode protocol: `0x02` per-frame channels, `0x03` coordinate table, each a hand-rolled byte layout, and the color payload is **always RGB** regardless of the buffer's `channelsPerLight`. Every new data kind (RGBW display, beam direction, …) means inventing another opcode and another fixed layout by hand. The minimal fix that stops that sprawl: a small **typed header** — `[type][format][count][stride]` where `format` enumerates `{RGB, RGBW, …}` — so one message kind carries any per-light channel layout and the browser shader reads `format` to interpret the payload. Do it concrete-first, when RGBW *display* (below) is actually wanted, not speculatively. Prereq for both items below. + +## RmtLedDriver + +### RMT over DMA on the S3/P4, with a completion callback (Funkelfetisch, July 2026) + +Kind: Feature · Platform: S3 + +Funkelfetisch's fork carries a finished branch, `codex/upstream-rmt-rgbw-performance`, that the +classic-ESP32 flicker work of 2026-09-05 makes worth adopting: on chips with RMT DMA +(`SOC_RMT_SUPPORT_DMA`: S3, P4) it sets `with_dma` with the IDF-recommended 1024-symbol block, so +the frame streams from RAM and the refill interrupt that causes flicker on a DMA-less chip does not +exist at all. It replaces the blocking `rmt_tx_wait_all_done` with `rmt_tx_register_event_callbacks` +(`on_trans_done`) plus a per-channel busy flag so the next tick skips while a frame is in flight, +and reports `"RMT DMA"` in the driver status so a user can see which path is live. Files: +`platform_esp32_rmt.cpp` (+105), `RmtLedDriver.h` (+75), `LedDriverConfig.h`, `Correction.h` +(RGBW profiles, a separate topic in the same branch), with unit tests. + +What it does NOT address: on the classic ESP32 the DMA half compiles to nothing, and nothing in it +moves the channel's interrupt off core 0 (the root cause found on the Dig-Next-2, fixed by +creating the channel from core 1). The two are complementary, one driver with the right answer +per chip: DMA where the silicon has it, the core-1 refill where it does not. Adopt his DMA and +callback path and keep the core hop. Study, do not copy: write it against the seam as it stands, +credit the branch. + +Re-read against the wire-byte driver (2026-09-09), which changed two of the assumptions above. The +driver ships the correction's wire bytes and the peripheral expands them +([RmtLedDriver.h](../../../src/light/drivers/RmtLedDriver.h)). So (a) his `mem_block_symbols = 1024` is now the +only symbol memory in play and is cheap, and (b) `rmt_transmit` already takes the bytes, so the DMA +path needs no buffer change, only `flags.with_dma` plus the callback. The `txInFlight_` guard STAYS +whatever happens: the peripheral streams straight out of the driver's frame buffer, so a rebuild +that frees it mid-frame still tears; his busy flag would replace the blocking wait, not that guard. +The win left on the table is the blocking `rmtWs2812Wait` on S3/P4, which today costs the tick the +whole wire time of the longest strand. + +### RmtLedDriver's out-of-memory status has no test, and cannot have one yet (2026-09-09) + +Kind: Feature + +`frameUnusable_` reports a frame buffer the driver could not get (or, on classic, one that landed in +PSRAM where the refill cannot read it), and two guards keep that error from being overwritten by the +resting "driving N of N" status. All of it shipped unpinned, and not by oversight: the desktop +`allocInternal` is a flat `trackedAlloc` that never refuses and ignores `setTestMaxAllocBlock`, and +`ptrIsPsram` is hard-coded false. A test written against that would pass whatever the driver did. +`unit_JsonSink_overflow` shows the trap: it grows until a 64-bit host might refuse and accepts "no +refusal" as a pass, so it tests nothing on a machine with RAM. + +**What it takes:** a cap inside the desktop `allocInternal` that `setTestMaxAllocBlock` (or a new +`setTestInternalCap`) drives, so a test can make the frame allocation fail deterministically, plus a +`setTestPsramFrom(ptr)` seam so the PSRAM branch is reachable off-target. Then three cases: the +status appears, `prepare()` does not replace it with "driving N of N", and `reinit()` does not clear +it (the bug the bench found: the error retracted microseconds after it was set). A platform change +with its own review, which is why it is here and not in the change that shipped the status. + +### RMT int_ena read-modify-write race (classic ESP32, level-5 refill) + +Kind: Bug · Platform: classic + +- **RMT `int_ena` read-modify-write race (classic ESP32, level-5 refill).** `RMT.int_ena` is one register written by both the render task (arming a frame) and the level-5 refill handler (disarming a finished one), through `rmt_ll_enable_interrupt`'s `|=` / `&=`. A handler firing between the task's read and its write loses the task's update, leaving a channel armed or silent. Never observed: the window is a few instructions and the two writes rarely target one channel, so the symptom would be a stuck channel after hours rather than anything the bench shows. Left unfixed deliberately, because the two obvious guards are both wrong here and each was tried on hardware: `portENTER_CRITICAL_ISR` spins on a lock the level-5 handler has just preempted (it runs above `XCHAL_EXCM_LEVEL` 3 by design) and deadlocks the core, and a compare-and-swap builds but crashes, since `S32C1I` addresses only data memory and a peripheral register raises `EXCCAUSE` 3. The remaining candidate is masking to level 5 (`XTOS_SET_INTLEVEL`) around the two-instruction update, which needs no lock and no atomic bus access; it compiles but is unproven on hardware and wants a soak before it displaces firmware that is flicker-free on two boards. + +## Across modules + +### Clocked and one-lane strips over SPI + +Kind: Feature · Platform: all + +APA102, SK9822 and HD107S take a clock and a data line, which is SPI at tens of MHz, so one pin pair carries ten thousand lights where WS2812 carries 2,048; the P4's octal SPI adds eight data lines for a large bank. The same peripheral drives one WS2812 lane on MOSI, each bit encoded in three or four SPI bits at 2.4 to 3.2 MHz by DMA with no CPU: the route for a chip with two RMT channels such as the C3, and the one every Raspberry Pi shares. Both are one driver behind a platform seam in the init, buffer, transmit and wait shape the other LED peripherals use, with a fixture profile per chip type; `PinList.h` already carries the higher cap a clocked type passes. + +### Frames to a board over USB serial, the desktop as renderer + +Kind: Feature · Platform: desktop + +A desktop has no LED pins and reaches a board through the network drivers today. A board on a USB cable is the other route: the desktop renders and sends RGB frames over USB serial, and the board clocks them out through its own LED driver. An S3's native USB-Serial-JTAG sustains about 12 Mbit/s, which carries about 10,000 lights at 50 fps; a UART bridge (CP210x, CH340) about 1 Mbit/s, so past that Art-Net, E1.31 or DDP over Ethernet to several boards stays the scale route. It needs the board-side receiver, the serial sibling of `NetworkReceiveEffect`, and a desktop driver beside `NetworkSendDriver`. + +### MoonI80 prime-only ring: no stall backstop (sibling-path gap) + +Kind: Bug · Platform: S3 + +**Found:** 👾 Reviewer, pre-commit on the whole-frame stall fix (2026-07-22). + +The MoonI80 wait-timeout backstop (`moonI80Ws2812Wait`, `platform_esp32_moon_i80.cpp`) now recovers a lost/coalesced EOF on **two** of the driver's three completion paths: the lapping ring (`nSlices > ringBufs`, oracle-gated) and the whole-frame path (`!isRing`). The **prime-only ring** (`isRing && nSlices <= ringBufs`) falls through both conditions, so a lost terminator-EOF there leaves `busy` stuck true with no recovery — and every prime/arm/transmit-ring path refuses under `busy`, the same permanent-wedge class the whole-frame fix just closed. + +Mitigated in practice: prime-only fires exactly one EOF per frame (no intra-frame coalescing), so the lost-EOF trigger is far rarer than on the whole-frame or lapping paths. But it is the same defect, and per the CLAUDE.md sibling-path rule (a cross-cutting recovery that core owns for one path should cover the sibling path, not be re-implemented per case) the backstop should extend to it rather than leave a third path uncovered. + +**Fix:** widen the backstop's condition so a stuck prime-only ring finalizes too — likely a single "any ring frame whose wire time has elapsed with `busy` still set" oracle that subsumes both ring branches, calling the shared `finalizeStalledTransfer`. Verify on the expander wall (prime-only = the small-strand ring config), since the ring recovery is not desktop-testable. + +**Related (same file, same wall dependency):** the whole-frame backstop stops the hardware before draining the FIFO and the EOF ISR drops a firing against an empty FIFO, which rejects a late EOF that arrives after recovery. A tighter guarantee — an ISR that has *already passed* the empty-FIFO guard cannot then clear a freshly-recovered `busy` or give `wireFree` for the next transfer — would need explicit serialization (disable the EOF interrupt across finalize+rearm, or an explicit recovery/rearm state the ISR checks). The window is extremely narrow (an ISR in-flight at the instant of finalize) and stopping GDMA+LCD first already disables the source; the hardened version is an ISR-concurrency change that must be proven on the wall, not landed blind. + +### A power budget the device knows about (2026-09-02) + +Kind: Feature + +**Measured on MM-StadBeest** (LightCrafter 16, 1440 lights, through the new `power.mls` readout): + +| brightness | rail | current | +|---|---|---| +| 16 | 4.86 V | 0.9 A | +| 60 | 3.9 V | 4.3 A | +| 120 | (browned out) | | + +Current is roughly linear in the control value, so the usable range is compressed into the bottom of +the travel and the top of the slider is a power hazard rather than a setting. The board crashed +twice during this session at brightnesses a user would reasonably try. + +**What prevents the crash: a power budget from measured values.** A board that measures its input voltage or current dims when the voltage sags below a threshold or the current passes a limit (see the power-monitoring entry below). A board without sensing relies on safe mode and the fade-in, which bring it back reachable after any supply failure. + +NOTE the rail sag above is NOT the supply's fault: an LRS-350-5 delivers 60 A, and this browned out +at 4.3 A. Roughly 0.25 ohm of series resistance in the feed, so wiring and injection points, which +is worth measuring before tuning anything in firmware. + +**Measured again 2026-10-09**, full white on all four channels: 4.7 V at 0.5 A, 4.1 V at 9 A, 3.3 V at 18.4 A, held for minutes while USB powered the board. The feed is now about 0.08 ohm (2.5 m of 2 mm² speaker cable each way), about 26 W of heat at full load. Powered from the feed alone, the board hangs under that sag rather than resetting, since the chip's supply stays above the brownout level; raising that level to 2.98 V, the S3's level 3, still hung without a reset. Two power cycles bring a hung board back dark and reachable through [safe mode](../../reference/troubleshooting.md) (bench-verified). What remains is the device acting on what it measures: dimming when the input sags or the current passes a limit, on boards that measure. + +### Graceful blank-on-stall: a stalled bus darkens the strip (2026-07-15) + +Kind: Feature + +When the bus stalls mid-frame the WS2812 strip is left holding **random / max-brightness lights** (often all-white — the all-ones failure pattern) that only a **power cycle** clears. That is a robustness gap: WS2812s latch their last received color and hold it until re-clocked or power-cycled, so a frame that dies mid-stream leaves every light past the failure point stuck bright. The give-up guard today stops *spending the render thread* on a dead bus (correct) but does nothing about the *strip's* state, so the user sees a wall of garbage LEDs and reaches for the plug. + +**The fix: on give-up (and on a rebuild that SHRINKS the reachable range), clock out ONE clean all-black frame** — every lane LOW → every light receives 0,0,0 → the strip goes dark. This turns "stall = a wall of random bright LEDs until power-cycle" into "stall = strip cleanly dark," which is the honest *degraded, not crashed* state the *[Robustness](../../explanation/architecture/moonmodule.md#robustness)* rule asks for. It also covers the PO's specific case (drop `ledsPerPin` 256→128 and the abandoned 128–256 range stays lit): a full-length black frame on the shrinking rebuild blacks the whole physical strip once, no boundary to compute. + +**The load-bearing caveat:** if the bus is wedged *because it cannot complete a transfer*, a black frame may not clock out either — so this is a best-effort **attempt**, not a guarantee: try the black frame on give-up; if it clocks, the strip darkens; if the DMA is truly dead, we are no worse off than today (and the rebuild-wedge fix above is the real cure for *that* class). Note it must be a genuine transmitted frame (all lanes driven LOW through the normal encode+transmit), not merely zeroing the DMA buffer — the strip only changes on a clocked frame. Pin it with a test: after `kDeadFramesBeforeGiveUp` dead frames, the driver emits one all-zero frame through the transmit seam (the mock asserts a zero frame was handed to the bus), and a subsequent recovery resumes normal content. + +### Classic-ESP32 shift-register ring on raw I2S: the high-light-count classic driver + +Kind: Feature · Platform: classic + +**The PO wants shift-register output on classic ESP32 boards at the MAXIMUM light count**, which the whole-frame `i80` backend cannot deliver: its frame buffer must be internal RAM, which caps it near 2048 lights ([performance.md](../../reference/performance.md#multi-pin-led-driving-all-three-peripherals-128128-grid)). Above that, classic needs the **hpwit refill-ring model**: a handful of tiny internal DMA buffers (~1.2 KB total, light-count-independent) refilled by the I2S EOF ISR, which transposes the next pixel row out of a **PSRAM** framebuffer as it goes. The DMA never touches the source array — only the CPU/ISR does — so the source lives in PSRAM and internal RAM stays constant. This is how hpwit (and WLED-MM/MoonLight on his driver) reach **48×256 ≈ 12K lights at ~100 fps on classic silicon with WiFi up**, with the same '595 expander. + +**Why it must be a SEPARATE driver, not a flag on `I80LedDriver`:** `esp_lcd` owns the classic I2S DMA and only does whole-frame, so the ring cannot be bolted onto the esp_lcd path — it needs a second classic driver written on the **raw I2S registers** (`i2s_ll` / the LCD-mode register file directly, below esp_lcd, the way the S3/P4 MoonI80 backend sits below esp_lcd on LCD_CAM). Its ISR is small enough to fit the classic's ~70 KB IRAM (hpwit's does), and — the key move — it registers the interrupt **without** `ESP_INTR_FLAG_IRAM` (his source comment: removed "to avoid Cache Disabled but Cached Memory Region Accessed") so the refill ISR is legally permitted to read the PSRAM framebuffer. The trade it accepts vs. our whole-frame path: it **gives up the whole-frame path's WiFi-underrun immunity** (a WiFi burst that starves the refill ISR can glitch a frame), which the ring mitigates with a tunable buffer-count cushion (`nbDmaBuffer`, hpwit's default 6). For a ≤2K-light WiFi-busy install the whole-frame i80 is still the better choice; the ring is for the >2K-light case classic cannot otherwise reach. + +**Reference (study, don't copy; write fresh against our architecture):** the line-by-line source read is in [the LED-driver analysis](analysis-leddriver.md#the-classic-esp32-past-2048-lights). The S3/P4 MoonI80 ring is the closest in-tree prior art for the ring mechanics (linear self-terminating chain, per-drain refill, drain-count termination), but its refill is a task and its buffers are internal-only *because the LCD_CAM GDMA can't sustain a PSRAM read at the shift clock*; the classic I2S ring is the inverse (PSRAM framebuffer legal, ISR refill mandatory), so it borrows the *shape* but not the constraints. Do the S3/P4 **ISR-refill + `MM_HOT`** work first (it proves the ISR-refill pattern in-tree on the friendlier unified-DIRAM chips); the classic raw-I2S ring is the next tier up, reusing that pattern where IRAM is genuinely tight. + +**Why MoonI80 cannot serve the classic, and what this driver inherits (2026-09-06).** `MoonI80` +is written against **LCD_CAM**: it drives the GDMA link list and the LCD registers directly +(`gdma_link_*`, `lcd_ll_*`) to bypass `esp_lcd`'s per-transaction peripheral reset. The +classic ESP32 has no LCD_CAM at all; its i80 is the **I2S** block in LCD mode, a different +peripheral with its own register file (`i2s_ll_*`) and its own DMA, so none of MoonI80's code +applies and `MoonLedDriver::lanesAvailable()` reports `platform::lcdLanes`, which is 0 there. The +picker hides the backend rather than gating it, which is why the classic has exactly one parallel +route today: `esp_lcd`'s I2S backend, whole-frame, internal-RAM-only, capped near 2048 lights. +This driver is the second route, and it stands in the same relation to `esp_lcd` on I2S as MoonI80 +does on LCD_CAM: same shape, no shared code. + +Two things it inherits from the 2026-09-06 i80 work, both worth keeping. It should claim **I2S +instance 1** and leave 0 for audio, for the reason [platform_esp32_i80.cpp](../../../src/platform/esp32/platform_esp32_i80.cpp) records, +and owning the peripheral directly +it can simply ASK for instance 1 rather than steering `esp_lcd` by parking instance 0, which is the +workaround the current backend needs. And it inherits the package-aware pin refusal: a pin the +package lacks wedges the flash cache silently, which cost a full day of bisection on the +ESP32-PICO-V3-02. + +### Multicore step 2b: a ping-pong second output buffer + +Kind: Feature · Trigger: workload-gated + +A second output buffer, whose recoverable wait [`renderWait`](../../reference/performance.md#multicore-the-whole-output-stage-on-core-1-multicore-step-2) measures, costs one full frame buffer (~48 KB at 16K lights). A **light effect on many lights** (output-bound — a solid color or slow gradient across 16K lights) shows 6–11 ms of wait, which 2b recovers whole. So it's a real but narrow win: **gated on the product owner naming that light-effect/many-lights workload as worth the 48 KB**, not on a generic fps gain. + +### Brightness belongs on a fixture's DIMMER channel, not only in the color values (WANTED) + +Kind: Feature + +`Correction::apply()` holds a profile's `Dimmer` channel wide open at 255 and puts brightness into +the R/G/B/W values through `briLut`. That is correct output and it is the only rule that serves +every fixture (an addressable strip has no dimmer channel), but it is not how a lighting console +drives a fixture that HAS one, and it costs real quality: + +- **Resolution.** At 10% brightness the colors are driven at 0-25, about 25 usable levels instead + of 255. A slow fade to black steps visibly; the dimmer channel exists so colors keep full 8-bit + range while intensity varies. +- **Dimming quality.** A moving head's dimmer is often 16-bit or curve-corrected for the LED's + response. Linear scaling of 8-bit color values is the crudest dimming there is, and it is worst + at the low end where the eye is most sensitive. +- **Matching.** A PAR with a real dimmer and a strip without one, both at 20%, do not read as the + same brightness: one dims in the LED driver, the other in integer arithmetic. + +**The rule to implement:** when a profile declares a `Dimmer` role, route the driver's brightness to +that channel and drive the colors at full saturation; keep today's behavior (scale the colors) +only where the fixture has no dimmer. The per-light color values still carry the effect's own +shading, so an effect that paints one light dark stays dark. Watch the one case where fixture and +light are not 1:1: several light cells behind ONE master dimmer cannot express per-cell brightness +through it, so those keep the color-scaling path. + +Found on the bench 2026-08-28 wiring the first moving head. Note the dimmer was not written AT ALL +before that day (the shipped `IRGB` profile could never light a fixture); writing it at 255 is the +fix that made a fixture light, not the finished design. + +### Pan/tilt travel is hardcoded, and positioning is 8-bit (WANTED) + +Kind: Feature + +Two fidelity gaps in how an aim becomes a real beam, both found on the bench 2026-08-29 reviewing +the preview's beam cones. + +**Travel is assumed, not declared.** `EffectBase` and the preview both hardcode *540 degrees of +pan, 180 of tilt*, which is the common mid-size convention and happens to match the bench fixture. +Real heads vary: 630 pan on larger bodies, 360 or 530 on compact ones, 270 tilt on beam fixtures. +Nothing declares which fixture is plugged in, so the preview draws every rig with the bench head's +travel and silently lies about where a beam points. Zero point varies too (some fixtures put +mechanical zero at DMX 0 rather than centering at 128), and the axis direction is a setting on the +fixture's own display (`rPAN` / `rTIL` on the bench head), so even one fixture has no fixed mapping. + +**The DMX byte is already the right abstraction, and it is why nothing can over-rotate.** An effect +never emits an angle: `MovingHeadEffect::axis()` maps its sweep onto a 0..255 byte and clamps, so +0 and 255 *are* the mechanical stops whatever they are in degrees, and the head cannot be asked +past its travel. A sweep that reaches a stop flattens there (the beam parks) rather than wrapping, +which is what the fixture itself does. Degrees exist only for DRAWING. + +So the fix is scoped to the preview, and the open question is where travel is declared: + +- **`PreviewDriver` controls** (`panTravel`, `tiltTravel` in degrees, sent in the aim message) — + small, live-reconfigurable like every other setting, and honest that travel is a property of the + fixture someone plugged in. The leanest thing that stops the lie. +- **The fixture profile**: correct once two different heads run at once, but a profile is a + *channel-role* layout today, and carrying physical travel widens what a profile means. Belongs + with the [fixture model](backlog-layouts.md#fixture-model-moving-heads-and-beams), not before it. + +**Positioning is 8-bit while the fixture offers 16.** The bench head has a fine channel for each +axis ([light fixtures reference](../../reference/hardware/light-fixtures.md)); both repeat the coarse byte, so pan resolves +to 540/256 = about 2.1 degrees per step. Across a room that is a visible jump on a slow sweep, and +it is the bigger fidelity win of the two. Needs a 16-bit path from the effect's sweep through +`FixtureChannels` to the profile's fine-channel roles, so it is the larger job. + +### RS-485 and DMX-512 wired output: the physical-DMX driver + +Kind: Feature + +MoonLight already speaks DMX **over the network** (Art-Net / sACN via `NetworkReceiveEffect`). The missing half is **wired DMX-512 out**: driving DMX fixtures (moving heads, par cans, wired pixel controllers) directly over an RS-485 differential pair, which is what the RS-485 hardware on carrier boards like the [MHC-WLED ESP32-P4 shield](../../reference/hardware/mhc-wled-esp32-p4-shield.md) is *for*. DMX-512 is a 250 kbps async serial frame (a break + mark-after-break + 513 bytes: start code + 512 channels) shipped over RS-485 — the textbook fixture-control transport. A DMX driver would map the light buffer (or a fixture/attribute model — see the [Fixture model — moving heads, beams](backlog-layouts.md#fixture-model-moving-heads-and-beams) item below) to DMX channels and clock the frame out a UART in RS-485 mode. + +**What it needs that we don't have yet:** +- **A `platform::` UART-RS485 seam.** The ESP32 UART has a hardware RS-485 half-duplex mode (`uart_set_mode(UART_MODE_RS485_HALF_DUPLEX)`) that auto-drives the transceiver's **DE/RE** (driver-enable / receiver-enable) line — the thing our current pin handling has no concept of (we drive pins as plain GPIO). A DMX driver is where DE/RE control first earns its place, and only for a **bidirectional** channel: firmware DE/RE toggling is what lets one channel switch Tx↔Rx without a hardware switch. A **fixed-transmit** channel needs none — its transceiver is hard-wired to drive. On the [MHC-WLED ESP32-P4 shield](../../reference/hardware/mhc-wled-esp32-p4-shield.md) that split is physical, with a mechanical slide switch on the one bidirectional channel. Firmware DE/RE control is what a board would need to make a channel bidirectional *without* such a switch. +- **The DMX frame timing** — the break/MAB is generated by a baud-rate switch or a GPIO toggle around the UART frame; standard, host-testable as an encoder. +- **A fixture/channel-mapping model** — trivial for a dumb pixel-per-channel strip, real work for typed fixtures (pairs with the moving-head fixture-model item; a wired-DMX driver and a network-DMX(Art-Net) input would share that fixture model). + +**The channel-mapping half is unblocked.** The per-light encode path handles an arbitrary channel count ([lessons.md](../../work/past/lessons.md)). A DMX universe is exactly that model: a light with `channelsPerLight = ` (16-ch moving head, 7-ch par, …), and the buffer's bytes ARE the DMX channel values. So a DMX driver's "map the buffer to channels" step is now the trivial part — it ships the light buffer's bytes straight into the 512-channel frame. What remains genuinely new is the **transport** (the RS-485 UART seam + break/MAB timing) and the **typed-fixture model** (naming which channel is Pan vs Dimmer — the moving-head fixture item), not the encode. + +**Can a board drive XLR fixtures directly? Yes, with an RS-485 transceiver — that's the one required part.** DMX-512 is RS-485: a *differential* pair (D+/D−, ±2–6 V), not the 3.3 V single-ended UART the MCU emits, so an MCU TX pin can NOT wire straight to XLR. A transceiver chip (MAX485 / SN75176 / THVD-class, ~$0.50) sits between the UART and the connector and drives the differential pair; the DE/RE line (the UART-RS485 seam above) flips it Tx↔Rx. **3-pin XLR** carries it: pin 1 = ground, pin 2 = D−, pin 3 = D+. With a transceiver present, daisy-chaining ~10 moving heads (10 × 16 ch = 160, inside one 512-channel universe) over standard DMX in→out is well within the RS-485 limits (32 unit loads / 1200 m); the last fixture wants a 120 Ω terminator (a fixture/cable concern, not the MCU). So whether a catalog board can drive XLR *directly* hinges on one schematic question: does it carry an RS-485 transceiver + XLR/terminal (then yes, direct, as [the MHC-WLED ESP32-P4 shield](../../reference/hardware/mhc-wled-esp32-p4-shield.md) does), or only the WS2812 level-shifted outputs (then a ~$0.50 breakout is needed). + +Sequencing: it's a **driver** (`src/light/drivers/`) + a platform UART-RS485 seam + a fixture model shared with the Art-Net path — the buffer→channel encode is already done. Plan when a DMX fixture is actually on the bench and a catalog board's `supported`/`planned` list points at wired DMX. The [PinsModule pin-assignment work](backlog-core.md#pinsmodule-strict-reject-on-add-mode) covers the RS485/DMX TX/RX/DE slot; this is the driver that consumes it. + +### One encode, two readers: HLS and RTSP sharing the encoder (2026-09-22) + +Kind: Restructure + +The claim ([watching the output](../../reference/watching-the-output.md)) refuses the second of HLS and RTSP. The better end state is the one [the RTSP plan](../past/plans/Plan-20260922%20-%20RTSP%20video%20out%20on%20the%20P4%20and%20the%20desktop%20(shipped).md) describes and the P4 already half-implements: **one encode, two readers**, each taking what it needs from the same encoded frame, which is cheaper than either driver running alone twice over. + +What makes it a design task rather than a patch: the two drivers must **agree** on geometry, fps and bitrate, and nothing makes them agree now. HLS derives its bitrate from the grid and picks an encoder by name; RTSP takes the frames raw and names no encoder. The open questions are whose configuration wins when both are enabled, what happens when one changes `targetFps` mid-stream, and whether a reader that stops should relax the shared settings. A refcount alone answers none of those, which is why the claim ships first. + +Build trigger: someone wanting both streams from one device at once. Until then the claim is the honest behavior. + +### Spout and Syphon as video outputs, only if proven + +Kind: Feature · Platform: desktop · Trigger: only if proven + +One user runs OBS → Spout → his own VLAN-tagged card driver; he asked whether MoonLight could be a Spout source. Spout (Windows, DirectX/OpenGL) and Syphon (macOS, Metal/OpenGL) share a GPU texture zero-copy, so they are lower latency and bit-exact, but they are **same-machine only**, are **two** platform implementations, and leave **Linux and the Pi with nothing**. At LED-wall pixel counts (a 256x256 wall is 65K pixels) the latency difference is far below one frame of the render loop, so it does not decide the choice; coverage does. A Spout user is also reachable through [NDI](../../reference/watching-the-output.md) in one hop, since OBS, Resolume and TouchDesigner all speak both. + +Also note MoonLight renders into a CPU buffer, so a Spout/Syphon path would upload to the GPU purely to hand off, spending the zero-copy advantage it was chosen for. + +### Chunked transfer (Step 4): the 16K lever + +Kind: Feature + +- **Chunked transfer (Step 4) — the 16K lever, and now the ONE mechanism behind three separate ceilings.** Split a frame into transactions the DMA can actually swallow, feeding them back-to-back. It was scoped as a Parlio fix; it is really a **core-path** fix, and the shift-register expander is only its third beneficiary. + + **The three ceilings it lifts, all unshifted-first:** + 1. **P4 Parlio: ~4,096 lights**, a contiguous-internal-block limit rather than the 65,535-byte cap ([performance.md § Multi-pin](../../reference/performance.md#multi-pin-led-driving-all-three-peripherals-128128-grid)). So chunking is **the only path to the 16×1024 = 16,384 lights the 16-lane widening promised.** This is the headline win and has nothing to do with shift registers. + 2. **Classic ESP32: 2,048 lights.** Its I2S DMA cannot reach PSRAM at all, so the frame must fit internal RAM. The **staged** form below lifts it: the DMA only ever reads small INTERNAL chunks that the CPU fills from a PSRAM frame. + 3. **The 74HCT595 expander.** `i80` holds the whole frame and caps near 96 lights per strand; `MoonI80` streams it ([LED drivers](../../reference/hardware/led-drivers.md#wiring-a-74hct595-pin-expander)). An add-on, and explicitly **not** the reason to build this. + + **Two distinct limits, one idea — keep them straight.** For **Parlio** the constraint is *transaction size* (contiguous block + 65,535 bytes), so chunking means smaller transactions. For **i80** there is no single-shot cap at all (it chains DMA descriptors) — there the constraint is *where the DMA reads from*, so the win comes from **staging**: keep the frame in PSRAM, but have the CPU copy it a chunk at a time into small internal-RAM buffers that the DMA reads. Same mechanism, different reason, and conflating the two is what muddled the shift-register investigation. + + **Prior art, and why staging is the shape.** hpwit's drivers never DMA from PSRAM: the pixel data may live there, but an EOF ISR transposes it into small **internal bounce buffers** that the DMA clocks out (`owner_check = false`, a fixed ring, built once). Espressif do the same thing under a different name — the RGB-LCD driver's **bounce buffers**. The one place hpwit *does* DMA straight from PSRAM is the **P4 Parlio** path, and he chunks it (≤64 KB via IDF's parlio driver) — which is consistent with our own finding that the P4 handles a PSRAM whole-frame shift transfer fine while the S3 does not. The staged form gets hpwit's underrun immunity **without leaving `esp_lcd`** (still IDF-maintained, still one code path across classic/S3/P4, no raw-register driver) and **without putting the CPU back on a per-LED ISR deadline** — a bulk sequential PSRAM→internal copy per chunk is what PSRAM is good at, unlike a real-time streaming read. + + **The two mechanisms do DIFFERENT jobs, and the 100 fps target needs both.** The WS2812 wire time is a physical constant — 30 µs per light, serial down each strand — so the *only* lever on frame rate is **lights per strand**, never the chip: + + | strands × lights | total | wire time | fps ceiling | + |---|---|---|---| + | 16 × 1024 (direct, 16 GPIOs) | 16,384 | 30.7 ms | **33 fps** | + | **48 × 256 (6 pins × '595)** | **12,288** | **7.7 ms** | **130 fps** ✅ *(hpwit + PO proved this in practice — StarLight)* | + | **64 × 256 (8 pins × '595)** | **16,384** | **7.7 ms** | **130 fps** ✅ | + + So: **chunking/staging buys LIGHTS (memory); the expander buys FRAMES PER SECOND (short strands).** Neither substitutes for the other, and 12–16K at 100+ fps needs both — the expander's own ~145 KB frame is exactly the one that does not work from PSRAM today, so it *depends* on the staging fix. (A third piece is needed too: at 130 fps the CPU encode must finish in <7.7 ms, and it measures ~24 ms at 16K — that is what the multicore render↔encode split exists for.) + + **The classic ESP32 is a target, not a write-off.** It is routinely dismissed for work like this, and the dismissal is wrong: hpwit and the PO have *run* 48 strands × 256 at ~100 fps on classic silicon (StarLight), with the same '595 expander, while WiFi was up. 240 MHz, two cores, and a 30 µs/light budget is a lot of headroom. The thing that would stop us is **our own encode cost**, which is software we control, not a property of the chip — and the ~24 ms/16K figure quoted in the multicore analysis is (a) measured on a **P4**, not a classic, and (b) **pre-dates the SWAR transpose** that shipped since. Do not carry that number into a classic-ESP32 feasibility argument; measure the real one on the real chip. What the classic genuinely needs is the **staged** form of chunking (its I2S DMA cannot reach PSRAM at all), which this item provides. + + **Build and prove chunking on the unshifted path first** (Parlio 4,096 → 16,384 is the measurable win, on proven code), then let shift mode inherit it — that is a sequencing rule about *where to de-risk the mechanism*, **not** a claim that the expander is optional. It is not: it is the only route to 100 fps at this scale without spending 48+ GPIOs. Correct WS2812 inter-chunk timing is the one hard constraint: the lines must idle LOW for < 300 µs between chunks or the strand latches mid-frame. The driver already rejects an over-limit frame with a loud status. Measured detail: [performance.md § Multi-pin](../../reference/performance.md#multi-pin-led-driving-all-three-peripherals-128128-grid). + +### 16 bits at the output: what remains + +Kind: Feature + +- **16 bits at the output: what remains (2026-10-05).** Fine roles and the 16-bit curve table shipped, as [FixtureProfiles](../../moonmodules/light/supporting.md#fixtureprofiles) describes; the 16-bit Layer stays stashed, since it measured +56% frame cost. + - Verify on a WS2816 strip that the dark end of a fade is smooth, with the built-in `GRB 16-bit`. + - TM1814 and TM1914: an inverted line and a settings preamble before the pixels (8 bytes of current, 6 of mode, each followed by its inverse), as NeoPixelBus sends them; a driver protocol, not a profile. + - UCS7604: its RGB or RGBW mode is set by configuration, so it needs its datasheet before a profile. + - WS2916A-RGBW (Worldsemi): 16-bit GRBW with a 5-bit current gain per channel. Worldsemi's ESP32 example sends a 32-bit gain header once per frame, then 64 bits per pixel, MSB first. + - The header holds the G, R, B and W gains, 5 bits each, with the remaining 12 bits set to 1. + - A bit is 1.3 µs (T0H 300 ns, T1H 700 ns), and the reset is longer than 280 µs, past WS2812's. + - Needs a per-frame header in the LED drivers, the gains as controls, and a `GRBW 16-bit` profile; check our reset against 280 µs. + - WS2915 (Worldsemi): five channels, RGB plus two independent whites, 5-bit gain and 16-bit data, 5 to 48 V. Its frame layout is unpublished; the WS2916A's header with a fifth gain is the likely shape, to confirm from the datasheet. + - WS2916B: RGB at 16 bits (48-bit pixels), 3.3 to 5.5 V. + - Sources: [Worldsemi product list](https://www.world-semi.com/) and [Worldsemi's examples](https://github.com/WorldsemiLED/worldsemi-led-examples); the 4 kHz PWM reported for the WS2915 is unconfirmed. + - Temporal dithering on an 8-bit wire, fed by the same 16-bit table. + - Pan and tilt beyond 8 bits, once a layer slot carries motion wider than 8 bits; today their fine byte repeats the coarse one. diff --git a/docs/work/future/backlog-effects.md b/docs/work/future/backlog-effects.md new file mode 100644 index 000000000..e5e5406ff --- /dev/null +++ b/docs/work/future/backlog-effects.md @@ -0,0 +1,194 @@ +# Backlog: Effects + +To-build items for Effects, grouped by module, each with its fields on one line. Other areas: [the future index](index.md). + +## AuroraEffect + +### Aurora hitches on the desktop and not on the S3 (2026-09-04) + +Kind: Bug · Platform: desktop + +A periodic hitch in the desktop preview that the same firmware at 64x64 on an S3 does not show. Ruled out by measurement: integer overflow, the drift sawtooth wrap (7 of 255 worst case), field speed (the desktop moves 15x less per frame), the contrast window's easing, and the preview transport at a large grid. The one difference left is about 385 fps against 25, so the next step is a desktop frame-rate cap: if the hitch tracks render rate, something is per-frame where it should be per-unit-time. A weighted layer crossover (cubed weights, 2.7x less frame-to-frame change for 3.4% more time per pixel) was built and reverted; it is worth another look once the hitch is understood. + +## BlurzEffect + +### BlurzEffect: a compounding blur has no rate carry + +Kind: Bug + +`Layer::fadeToBlackBy` takes a rate per reference frame and the Layer scales it once, the pattern every fade uses ([architecture § Where the machinery lives](../../explanation/architecture/moonlight.md)). `draw::blur` does not fit it: blur COMPOUNDS, so applying it twice at half strength is not one blur at full strength, and the fractional carry that makes a fade frame-rate independent produces the wrong result. BlurzEffect is the effect this bites. The open question is what the right construct is — a per-frame blur budget, a single blur at an accumulated strength, or leaving blur explicitly frame-gated and documenting it as not-a-rate. + +## BouncingBallsEffect + +### BouncingBalls onto the particle kernel: a rework, not a swap + +Kind: Restructure · Platform: all + +The effect computes each ball's height from the closed-form projectile equation in float, exact and frame-rate independent, so a plain Euler swap is a downgrade: against the closed form it drifts 8 cm on a 1 m trajectory after one second, more than a pixel on a 16-row panel. What the kernel adds is what the closed form cannot: balls that collide with each other and move sideways instead of each in its own vertical lane. The effect keeps up to 16 balls per column in per-column arrays, so the rework is one shared pool plus a decision about how many balls a wide fixture gets, judged on the panel against the original and landing only if at least as beautiful. + +## DemoReelEffect + +### DemoReel: a 3D test for the hosted D1/D2 extrude + +Kind: Feature + +DemoReel extrudes a hosted D1/D2 effect after its tick; its test hosts on a 2D grid only. Pin it with a DemoReel test that hosts a D1 and a D2 effect on a 3D grid and checks the spread. + +## FixedPointEffect + +### FixedPoint fades outside the Layer's aggregation (2026-09-07) + +Kind: Restructure + +`FixedPointEffect` calls `draw::fade` directly where every other fading effect calls +`layer()->fadeToBlackBy`, so it skips what the Layer adds: the per-frame aggregation (N effects on +one layer cost one buffer pass, gentlest rate wins), the framerate scaling, the sub-unit carry, and +the buffer-generation bump. Invisible until a second effect shares the layer. + +The swap is one line and was tried: it makes the effect **3.1x brighter at high framerate than at +low**, against the 1.35x band `unit_Effects_framerate` enforces. The two calls mean different +things. `draw::fade(cv, n)` applies n every frame; `fadeToBlackBy(n)` is a RATE per reference frame +that Layer scales by elapsed time. The `fade` control (default 70, range 0..255) was tuned against +the first meaning, so moving it needs the default re-tuned and the trail looked at on a device, not +a silent swap. + +Worth doing, as its own change: it is the last effect outside the shared fade path, and while it +stays outside, "every fade goes through the Layer" is not true. + +## FluidEffect + +### Speed up the fluid solver: 4 fps at 128x128, and it is not the divide (2026-09-05) + +Kind: Feature + +Measured on an S31 (RISC-V, 320 MHz, octal PSRAM at 200 MHz), `iterations` 5, depth 1: + +| grid | Fluid tick | fps | cycles per cell-update | +|---|---|---|---| +| 32x32 | 8.5 ms | 109 | 151 | +| 64x64 | 38 ms | 24 | 159 | +| 128x128 | 204 ms | 4 | 205 | + +A cell-update is four loads, three adds, a multiply and a store: under 20 cycles of arithmetic. It +costs **151 even at 32x32**, where the whole working set is small enough to cache, so the loop +itself is roughly 8x more expensive than the work it does. Memory adds a further 35% by 128x128 but +is not the wall: at 4 fps the solver moves ~25 MB/s, about 5% of what this PSRAM delivers. + +**A wrong turn worth recording.** The first diagnosis blamed the 64-bit divide in `relax()`, on the +reasoning that Xtensa has no integer divide instruction. It was implemented (a power-of-two shift +dispatched once per call, bit-exact over 8M values) and measured on the board: **no change, 326 ms +before and after**, so it was reverted. Two errors: the S31 is RISC-V rather than Xtensa, and at +151 cycles per update the divide was never the dominant term. A desktop measurement could not have +caught either, since arm64 divides in hardware; only the board settles it. + +**Allocation placement is worth 1.6x, and nobody chose it.** Same firmware, same grid, same +controls: **326 ms after a fresh boot, 204 ms after resizing the grid to 32 and back to 128**, +reproducible across reboots. The solver's six buffers are ~638 KB and land in PSRAM either way, but +where they land at boot is slower than where they land once the heap has moved. Whatever is done +about speed, this says a boot-time allocation can be paying a large penalty invisibly, and it is +worth understanding before optimizing the loop around it. + +Ordered by expected return: + +1. **Cut the 64-bit arithmetic in the inner loop.** Every cell computes an `int64` shift, an + `int64` multiply and an `int64` divide on a 32-bit core, where each is several instructions and + a register pair. This is the most likely source of the 151 cycles. A 32-bit formulation, or a + narrower intermediate with a proven bound, is the first thing to measure. Precedent: the SWAR + work found the 32-bit pair form bit-identical and 41% smaller. +2. **Hoist `idx()`.** The loop addresses five neighbors per cell through `idx(x, y)`, each a + multiply-add. Walking row pointers instead is the standard fix and removes most of the address + arithmetic. +3. **Understand the allocation-placement effect above**, since it is worth more than most loop + tuning and costs nothing to trigger deliberately once understood. +4. **Solve the pressure at half resolution.** Pressure is smooth, so a half-scale solve with a + bilinear upsample of its gradient costs a quarter of the cells. Same trade `fieldScale` already + makes for noise fields, measured 3.0x there. Changes the picture slightly, unlike 1 to 3. +5. **Fewer iterations, documented per target.** Linear in cost: 5 to 2 is 2.5x, and the picture + gets springier. The card should say what a target can afford rather than leaving a user to find + 4 fps. +6. **Question whether the full solver belongs on this class of board at all.** See the ColorTrails + entry below: a separable noise advection gets a flowing, swirling picture for two passes over + the grid and no solve. The fluid's own header already calls it a desktop and P4 effect. + +**Why `fluid.mle` runs at 13 fps while the compiled effect runs at 3.** They are not the same +algorithm: the script is a curl flow, not a solver +([generative effects](../../explanation/generative-effects.md)). Per frame at 128x128 the script visits ~16k cells +and divides nowhere; the compiled solver visits ~429k, of which 318k carry the 64-bit divide. That +is 27x the work, and the measured gap is only 4.3x because the interpreter gives most of it back in +dispatch overhead. So a COMPILED curl effect would beat both. What curl cannot do is what a solver +does: no pressure, no interaction between jets, and no vortex forming out of the flow's own +history. Whether that is worth 27x is a question for the product owner's eyes. + +**Measure on hardware, not on the desktop**: this is an in-order-core property and the desktop +divides in hardware, which is exactly how the wrong diagnosis above survived a desktop check. +Record before and after in performance.md per target. + +## NetworkReceiveEffect + +### E1.31 multicast receive (IGMP join) + +Kind: Feature + +NetworkReceiveEffect accepts E1.31 via unicast only — the same scope MoonLight ships. Multicast senders address the per-universe group `239.255.{universe_hi}.{universe_lo}`, which a receiver must join via IGMP. **The platform half of this now exists**: `UdpSocket::joinMulticast()` shipped with the WLED audio-sync work (2026-08-29) and is used in anger there, on both desktop and ESP32. What is left for E1.31 is the join-per-accepted-universe bookkeeping, not the socket support. Add when a multicast-only sender actually shows up on a bench; until then the spec documents "point sACN senders at the device's IP". + +## Across modules + +### The catalog sweep: every natural-motion effect onto the power functions + +Kind: Restructure · Platform: all + +[Migrating an effect](../../moonmodules/light/power-functions.md#migrating-an-effect) asks for it; [the inventory](analysis-effects-power-functions-inventory.md) records per effect what it uses today and which kernels it could run on, and a row is deleted when its effect is rewritten. Batches go by kernel: the noise effects, the polar and oscillation effects, then Echo, Lissajous and PaintBrush onto `advect`, `decay` and `lineAA`, each compared on the panel against how it ran and re-baselined with the reason. An effect a showcase supersedes, as Trails supersedes Lissajous, is deleted rather than kept beside it. + +### Moving-head effects from MoonLight, including two of troyhack's (2026-09-04) + +Kind: Feature + +MoonLight has several moving-head effects that have no equivalent here, two of them troyhack's. +Migrate them all, on the power functions per the standing mandate rather than traced across. + +### Sprite follow-ups + +Kind: Feature · Platform: P4 + +Deliberately deferred when sprites landed: P4 PPA acceleration behind the same `draw::sprite` +signature (the 2D-DMA blitter the WLED-MM-P4 world uses via LovyanGFX; ours would sit in the +platform layer, no vendored GFX library), Porter-Duff alpha when a real consumer arrives, and +MoonLive sprite data (needs the stage-3 builtin table + arrays). + +### A real 2D/3D PacMan + +Kind: Feature + +The migrated 1D PacMan and 1D Ant effects were removed — a chase rendered on a single strip reads as a blob of moving dots, not a game, so they weren't worth keeping. A proper **2D (or 3D) PacMan** — an actual maze, Pac-Man and ghosts navigating it, power dots, the blue-ghost flee state — would be a genuinely fun signature effect. Build it fresh as a grid game (not a strip port); the WLED/MoonLight 1D versions are reference for the state machine only, not the rendering. + +### Add real z-axis variation to 2D effects + +Kind: Feature + +Only **NoiseEffect**, **PlasmaEffect** and **RipplesEffect** have z-aware math. The other honest-D2 effects use `Layer::extrude` to duplicate the z=0 plane, so every z-slice is identical on 3D layers. Candidates for genuine D3 promotion: Metaballs/GlowParticles (add z to blob coordinates), Plasma palette/Spiral (add z-driven phase term), Fire (z-drift heat grid), Rings/LavaLamp/Checkerboard/Particles (add z to each element). Prioritise after seeing real 3D installations; each promoted effect also needs its `dynamicBytes` budget for the full 3D buffer. + +### worley cellular noise, when an effect needs it + +Kind: Feature · Trigger: an effect needs it + +- **`worley` cellular noise, when an effect needs it** (deferred 2026-08-07, PO). The one power function the plan names that stayed unbuilt on purpose. Worley (cellular / Voronoi) noise measures the distance to the nearest of a set of scattered feature points, which is what produces the look nothing else does — cracked mud, scales, stained glass, a caustic. The fields we have cannot fake it: `fbm` and `warp` are smooth by construction, so their creases never form cells. + + **Why it waits.** An industry-standardness audit (Fable, 2026-08-07) put it explicitly on the "do NOT add yet" list: a practitioner does not consider it table stakes, and every other field function in the library earned its place by having a caller. Building it now would make it the only entry whose justification is "the canon has one". The trigger to build it is an effect that wants cells — at which point it lands with that effect, the way `hashInt` landed with Dissolve and `sampleWrap` with Echo. + + **What it costs when it comes:** the standard cheap form is a 3×3 neighborhood scan around the sample's cell, hashing each neighbor's feature point and keeping the nearest distance — nine `hashInt` calls and nine distance tests per pixel, so meaningfully dearer than one noise sample. `dist16` and `hashInt` already exist, so the function itself is short; the cost question is what it does to a per-pixel budget on a large fixture. + +### 16-bit noise: warp16 / turbulence16 + +Kind: Feature · Trigger: an effect wants a large smooth field + +- **16-bit noise: `warp16` / `turbulence16`** (2026-08-08), unbuilt, to match the 8-bit compositions. The trigger is an effect that wants a large smooth field; it lands with that effect and its measurements, the way the 8-bit compositions landed with theirs. + +### Clear the grid on an effect's FIRST frame + +Kind: Bug + +- **Clear the grid on an effect's FIRST frame** (2026-08-08). Fourteen effects still show the previous effect's picture on the frame right after a switch: BouncingBalls, Fireworks, FreqMatrix, FreqSaws, GEQ, GEQ3D, GameOfLife, Lissajous, NoiseMeter, PaintBrush, Random and three more. Most predate the power-function branch. + + **What the user sees.** Switching to one of these leaves the old frame underneath for a moment — an audio effect with no signal yet, or a simulation still seeding, paints nothing and inherits whatever was there. Owning the background is each effect's job ([Layer.h](../../../src/light/layers/Layer.h)). + + **Why it waits.** It is fourteen effects' worth of change across audio-reactive and simulation families, each needing its own judgement about whether to clear, fade, or seed differently — not a mechanical sweep. `unit_Effects_gridsweep.cpp` already measures it (`afterFirst`) and asserts only the settled frame, so the number is visible without blocking. diff --git a/docs/work/future/backlog-layouts.md b/docs/work/future/backlog-layouts.md new file mode 100644 index 000000000..4b1d9a3fc --- /dev/null +++ b/docs/work/future/backlog-layouts.md @@ -0,0 +1,25 @@ +# Backlog: Layouts + +To-build items for Layouts, grouped by module, each with its fields on one line. Other areas: [the future index](index.md). + +## Across modules + +### Render larger than the fixture and pool down onto a few lights (Hue, PAR) + +Kind: Feature · Platform: all + +A fire or plasma effect on a virtual 32x32 grid driven onto ten Hue bulbs or a handful of DMX PARs, each light showing the mean of the non-black pixels in its region, so the fixture reads as one coherent wash instead of ten stray samples. It is a Layer feature, not a modifier: a modifier's live pass gathers one source coordinate per destination, and the logical box never exceeds the physical light count, so there is nothing to pool from. Two parts: a render box allowed to exceed the physical layout, and an N:1 averaging map beside the four mapping types, with regions cut per axis from the physical dimensions and no user control. The pooling math and the region split are pure and host-testable. The design and the architecture findings: [the downscale study](analysis-virtual-layer-downscale.md). + +### Fixture model: moving heads and beams + +Kind: Feature + +Today a "light" is a point at a static coordinate with a color. A **moving head** is a fixture that emits a *beam* in a direction it controls live (pan + tilt), plus color, beam-width, etc. — per-light **vector** state, not just color, and a different draw (a cone/ray, not a disc). The static-positions-`0x03` + color-`0x02` split can't express "this fixture's beam now points here." The industry-standard model is **DMX/GDTF fixtures**: a fixture has a position *and* a set of typed attributes (color, pan, tilt, beam). The preview becomes a fixture renderer (disc for a pixel, cone for a beam); this is also the "make Preview a general-purpose module, not light-specific" goal. A domain-model change (the fixture/attribute model), not just transport. Plan when moving heads are actually on the bench. + +**Sub-item — per-emitter targets, and fixing the Yellow/UV RGB-synthesis placeholder.** `Correction::apply()` today synthesizes every non-RGB emitter (White, WarmWhite, Yellow, UV) from RGB via the one `whiteMode` gate. That conflates two different physics: **White/WarmWhite are broadband illumination** with a real achromatic basis (`min(R,G,B)` is a sound approximation — warm vs cold differ only in phosphor CCT a byte value can't express), but **Yellow/Amber (~590 nm) and UV (~400 nm) are saturated/out-of-gamut emitters with no honest RGB pre-image.** Yellow's `min(R,G)` stand-in reads greener than a true amber die AND fires on far too much (any red+green content — yellows, whites, skin tones — muddies the fixture); UV's blue-excess is an eyeball hack. Amber is a *real, common, targetable* emitter (RGBA / RGBAW PARs ship a dedicated ~590 nm die), so the right model is an **effect that targets amber DIRECTLY** (a typed attribute), not RGB-and-synthesize. The fixture model is where that lands. **Decision to make when built:** whether Yellow/UV should default to **off** (0, like `whiteMode::None`) until an effect drives them — more honest than always firing a wrong approximation — vs. keeping the "light it up to eyeball the wiring" placeholder. **Practical test to run then:** on a real RGBA/RGBAW+UV PAR, compare the synthesized amber against a directly-driven amber die (does `min(R,G)` look acceptably yellow, or muddy?), and confirm the White subtraction under `Accurate` reads correct with all emitters present. Rationale + the current formulas live in `Correction.h`'s emitter-field comment. + +### Mixing light types in one Layouts + +Kind: Investigation + +Today each layout child describes one light type (all LED strips, or all par lights), and the current model is one Layouts container per light type. Whether a single Layouts should hold mixed types (LED strips + par lights together), and how the per-channel layout would reconcile across them, isn't designed. Deferred until a concrete need forces it; it's adjacent to the fixture model above (a real fixture/attribute model may reframe how mixed types are expressed). diff --git a/docs/work/future/backlog-light.md b/docs/work/future/backlog-light.md deleted file mode 100644 index 2c57aaa5a..000000000 --- a/docs/work/future/backlog-light.md +++ /dev/null @@ -1,1150 +0,0 @@ -# Backlog: light domain - -Forward-looking to-build items for the **light domain** (`src/light/`: drivers, effects, layouts, modifiers, preview) and its sensors. The core/infrastructure counterpart is [backlog-core.md](backlog-core.md); cross-domain items are in [backlog-mixed.md](backlog-mixed.md). Index + overview: [README.md](index.md). Completed items are removed. - -- ❌ **Cap the particle frame scale** (open): `FrameTime` spends a whole stall in one frame, so an - 80 ms hiccup moves every particle **6.7x** its usual distance in a single step (measured). That is - the rule working as designed, and it keeps the trajectory correct in real time, but a particle - INTEGRATES the gap where a shader just redraws from the clock and skips a frame invisibly. So - particles are the first effect class that makes system jitter visible, and they did: they exposed - a 1 Hz LittleFS scan on the render thread (since fixed) and they still show the previewer's - socket write and a UI reload. - - The fix would clamp the scale a pool sees to ~2 reference frames, trading real-time accuracy for - smoothness. **Deliberately not done**: it makes motion lie about elapsed time, which - [the tick-rate rule](../../explanation/architecture/index.md) exists to prevent, and every stall it - hides is a real defect somewhere else that would stop being visible. WLED-PS takes the opposite - side (`ParticleSystem2D::update()` advances a fixed amount per call, with no `millis()` anywhere), - so its motion speed is a property of the frame rate. **Build trigger**: a stall we cannot remove - at its source, on hardware a user actually has. - -## Effects - -### Network Receive on a P4 past 3,000 packets a second (2026-10-26) - -Below about 3,000 packets a second a P4 receives every frame; past it packets go, more for Art-Net than for DDP, as [the table](../../reference/performance.md#network-receive) shows. -The measured split, from a `--task-cpu-stats` build: the Ethernet receive task and lwIP's take about 160 µs of core 1 per packet, and Network Receive's task tens of microseconds per socket call on top, a `select` hundreds. -Both network tasks may run on either core, yet stay on core 1 with core 0 idle. -Two levers, each its own change: the network tasks on core 0, where pinning them by `ETH_MAC_FLAG_PIN_TO_CORE` and lwIP's task affinity kept Ethernet from coming up, so that needs its own look first; and lwIP's raw `udp_recv` callback writing into staging from lwIP's task, with no socket, no `select` and no receive task. -Ranking the Ethernet task below the receive task, and lwIP in IRAM, were measured and do not help. - -### The ring's refill jitter (2026-10-22, from the internal RAM plan) - -LCD-MM's refill interrupt fires every 89 µs at 3-row slices, yet almost every second it goes about 1.1 ms without firing. -So `ringAuto` keeps 54 rows of lead (3 × 21 buffers, 37 KB on shiffy), where hpwit's S3 driver runs on 10. - -Measured with `ringDbg`'s `gap` (within a frame, `@` the slice it ended at), `ml` (the fewest slices of lead) and `c` (the interrupt's core). The gap ends mid-frame as often as at the idle tail. - -Ruled out: -- the flash cache: `co` stays 0; -- the core: registered on core 1 it was the same, and it broke the cache guard, so it stays on core 0; -- `mmSnap`: removed. - -Next, the gap measured against the frame's own events, the arm and the snapshot, then against other interrupts on core 0. - -A lead of 10 rows would free most of the ring's internal RAM. - -### A preview GIF for every MoonLive script (2026-10-19) - -The 140 compiled effects each show a GIF on their catalog card (`docs/assets/light/effects/.gif`, 25 MB in all); the 49 MoonLive effect scripts show none. -The pictures belong in `docs/assets/moonlive/effects//.gif`, mirroring the script subfolders, never in `moonlive/` itself, which the build globs and the UI fetches file by file. -One MoonDeck script makes them all: `screenshot_modules.py` learns to step through the MoonLive catalog, capturing from the preview socket (`/wsp`, requests `[0x51][stride][fps]` and `[0x52][stride]`) the way the hpwit GIFs of 2026-10-18 were made. -At 0.5 to 1 MB each that is 25 to 50 MB more, so a smaller format (128 px, 2 s) is part of the decision. -**Decide first** where they show: a MoonLive scripts page with a card per script grouped by subfolder, or the script picker previewing one before its download. - -### Retro looks on a picture: scanlines, chroma bleed, tracking glitches (robwomp, 2026-10-21) - -Asked for on Discord by robwomp, who loves the glitchy analog and digital look of the 90s and 00s and has Rockchip boards with HDMI and CSI inputs. -A captured picture becomes worth watching on lights once it can be degraded on purpose: scanlines, a color channel bleeding sideways, a frame that tears and rolls like a VHS tracking error. -Each is a modifier on what the Picture effect draws, so it works on any source, a capture, a GIF or a browser tab. -Ask robwomp which looks matter most before building, and have them tested on robwomp's capture hardware. - -### Effects and Drivers find their source one way, not two (2026-10-08) - -Effects finds the top-level Layouts, and Drivers the top-level Effects, by type through `Scheduler::topOfType` when nothing was injected, which is what lets a state document build a tree with no wiring. -`setLayouts` and `setEffects` remain beside it for the unit-test rigs, 172 calls in about 40 files, so two mechanisms do one job. -The fix: a rig adds its containers to a Scheduler, as the scenario runner does through `createTopLevel`, and the setters go. - -### Moving-head effects from MoonLight, including two of troyhack's (2026-09-04) - -MoonLight has several moving-head effects that have no equivalent here, two of them troyhack's. -Migrate them all, on the power functions per the standing mandate rather than traced across. - -### RMT over DMA on the S3/P4, with a completion callback (Funkelfetisch, July 2026) - -Funkelfetisch's fork carries a finished branch, `codex/upstream-rmt-rgbw-performance`, that the -classic-ESP32 flicker work of 2026-09-05 makes worth adopting: on chips with RMT DMA -(`SOC_RMT_SUPPORT_DMA`: S3, P4) it sets `with_dma` with the IDF-recommended 1024-symbol block, so -the frame streams from RAM and the refill interrupt that causes flicker on a DMA-less chip does not -exist at all. It replaces the blocking `rmt_tx_wait_all_done` with `rmt_tx_register_event_callbacks` -(`on_trans_done`) plus a per-channel busy flag so the next tick skips while a frame is in flight, -and reports `"RMT DMA"` in the driver status so a user can see which path is live. Files: -`platform_esp32_rmt.cpp` (+105), `RmtLedDriver.h` (+75), `LedDriverConfig.h`, `Correction.h` -(RGBW profiles, a separate topic in the same branch), with unit tests. - -What it does NOT address: on the classic ESP32 the DMA half compiles to nothing, and nothing in it -moves the channel's interrupt off core 0 (the root cause found on the Dig-Next-2, fixed by -creating the channel from core 1). The two are complementary, one driver with the right answer -per chip: DMA where the silicon has it, the core-1 refill where it does not. Adopt his DMA and -callback path and keep the core hop. Study, do not copy: write it against the seam as it stands, -credit the branch. - -Re-read against the wire-byte driver (2026-09-09), which changed two of the assumptions above. The -driver no longer holds pre-expanded symbols at all: it ships the correction's wire bytes and the -peripheral expands them (IDF bytes encoder on the DMA chips, the level-5 refill on classic), so the -frame buffer is ~3 bytes per light rather than 96. So (a) his `mem_block_symbols = 1024` is now the -only symbol memory in play and is cheap, and (b) `rmt_transmit` already takes the bytes, so the DMA -path needs no buffer change, only `flags.with_dma` plus the callback. The `txInFlight_` guard STAYS -whatever happens: the peripheral streams straight out of the driver's frame buffer, so a rebuild -that frees it mid-frame still tears; his busy flag would replace the blocking wait, not that guard. -The win left on the table is the blocking `rmtWs2812Wait` on S3/P4, which today costs the tick the -whole wire time of the longest strand. - -### A script's setControl rebuilds a control subtree on every write (2026-09-06) - -Measured on the P4 at .139: **2 fps**, with `MoonLive-2` at 251 ms and `MoonLive-3` at 245 ms per -tick, together 498 ms of a 506 ms frame, and HTTP down to a 0.5 s round trip because it is served -from the same loop. Both services ran `sweep.mls`, whose `tick20ms` writes four faders. Two copies -at 50 Hz is 400 control writes a second, and `Scheduler::setControl` calls `rebuildControls()` -unconditionally on each one: - -``` -clearControlsRecursive(); // wipes this module's controls AND every child's, recursively -defineControls(); // then rebuilds them all -``` - -So each fader write tears down and re-creates the whole `Control` subtree. A person moving a slider -does this a few times a second and nobody notices; a script at 50 Hz multiplies it by hundreds. - -Two things to fix, and they are independent. **The rebuild should be conditional**: a `live` -control's value change cannot alter the schema, and `rebuildControls` already computes -`schemaSignature()` before and after to decide whether to notify, so it knows. Rebuilding only when -the shape can actually change (what `setLive` and `affectsPrepare` already distinguish) removes the -cost for every value write, scripted or human. **And the script tick is too fast for its job**: a -fader sweep does not need 50 Hz, so `sweep.mls` should run on a slower tick, which also caps the -damage any future script can do through this path. - -Not a regression from the RMT work: it predates it and was found while measuring an unrelated slow -board. - -### Speed up the fluid solver: 4 fps at 128x128, and it is not the divide (2026-09-05) - -Measured on an S31 (RISC-V, 320 MHz, octal PSRAM at 200 MHz), `iterations` 5, depth 1: - -| grid | Fluid tick | fps | cycles per cell-update | -|---|---|---|---| -| 32x32 | 8.5 ms | 109 | 151 | -| 64x64 | 38 ms | 24 | 159 | -| 128x128 | 204 ms | 4 | 205 | - -A cell-update is four loads, three adds, a multiply and a store: under 20 cycles of arithmetic. It -costs **151 even at 32x32**, where the whole working set is small enough to cache, so the loop -itself is roughly 8x more expensive than the work it does. Memory adds a further 35% by 128x128 but -is not the wall: at 4 fps the solver moves ~25 MB/s, about 5% of what this PSRAM delivers. - -**A wrong turn worth recording.** The first diagnosis blamed the 64-bit divide in `relax()`, on the -reasoning that Xtensa has no integer divide instruction. It was implemented (a power-of-two shift -dispatched once per call, bit-exact over 8M values) and measured on the board: **no change, 326 ms -before and after**, so it was reverted. Two errors: the S31 is RISC-V rather than Xtensa, and at -151 cycles per update the divide was never the dominant term. A desktop measurement could not have -caught either, since arm64 divides in hardware; only the board settles it. - -**Allocation placement is worth 1.6x, and nobody chose it.** Same firmware, same grid, same -controls: **326 ms after a fresh boot, 204 ms after resizing the grid to 32 and back to 128**, -reproducible across reboots. The solver's six buffers are ~638 KB and land in PSRAM either way, but -where they land at boot is slower than where they land once the heap has moved. Whatever is done -about speed, this says a boot-time allocation can be paying a large penalty invisibly, and it is -worth understanding before optimizing the loop around it. - -Ordered by expected return: - -1. **Cut the 64-bit arithmetic in the inner loop.** Every cell computes an `int64` shift, an - `int64` multiply and an `int64` divide on a 32-bit core, where each is several instructions and - a register pair. This is the most likely source of the 151 cycles. A 32-bit formulation, or a - narrower intermediate with a proven bound, is the first thing to measure. Precedent: the SWAR - work found the 32-bit pair form bit-identical and 41% smaller. -2. **Hoist `idx()`.** The loop addresses five neighbors per cell through `idx(x, y)`, each a - multiply-add. Walking row pointers instead is the standard fix and removes most of the address - arithmetic. -3. **Understand the allocation-placement effect above**, since it is worth more than most loop - tuning and costs nothing to trigger deliberately once understood. -4. **Solve the pressure at half resolution.** Pressure is smooth, so a half-scale solve with a - bilinear upsample of its gradient costs a quarter of the cells. Same trade `fieldScale` already - makes for noise fields, measured 3.0x there. Changes the picture slightly, unlike 1 to 3. -5. **Fewer iterations, documented per target.** Linear in cost: 5 to 2 is 2.5x, and the picture - gets springier. The card should say what a target can afford rather than leaving a user to find - 4 fps. -6. **Question whether the full solver belongs on this class of board at all.** See the ColorTrails - entry below: a separable noise advection gets a flowing, swirling picture for two passes over - the grid and no solve. The fluid's own header already calls it a desktop and P4 effect. - -**Why `fluid.mle` runs at 13 fps while the compiled effect runs at 3.** They are not the same -algorithm, and the script is not a faster fluid: it is not a solver at all. `fluid.mle` calls -`flowCurl`, which is curl noise, a divergence-free velocity read analytically from noise -derivatives in one advection pass, with no solve. Per frame at 128x128 the script visits ~16k cells -and divides nowhere; the compiled solver visits ~429k, of which 318k carry the 64-bit divide. That -is 27x the work, and the measured gap is only 4.3x because the interpreter gives most of it back in -dispatch overhead. So a COMPILED curl effect would beat both. What curl cannot do is what a solver -does: no pressure, no interaction between jets, and no vortex forming out of the flow's own -history. Whether that is worth 27x is a question for the product owner's eyes. - -**Measure on hardware, not on the desktop**: this is an in-order-core property and the desktop -divides in hardware, which is exactly how the wrong diagnosis above survived a desktop check. -Record before and after in performance.md per target. - -## Drivers - -### A control change reaches a driver at the frame boundary (2026-10-21, from the PR 150 thread audit) - -With the render split on, a driver's `tick()` runs on the encode task while its `onControlChanged`, `onCorrectionChanged`, `tick20ms` and `tick1s` run on the main loop, and only structural changes and `setEnabled` park the encode task first. -An audit of `src/light/drivers/` found the main loop writing state the encode task reads: `DriverBase`'s `correction_` (rebuilt on a control change, ramped by `tick20ms`, `motionHeld` set by `tick1s`), `effectiveBrightness_`, the `wire_` buffer a channel-count change reallocates, the `start` and `count` window; HueDriver's pairing and light tables; NetworkSendDriver's `dest_` addresses on the inline send path and its `corrected_` buffer; PwmLightDriver's `lights_` and `duty_`; RmtLedDriver's and ParallelLedDriver's whole geometry through `parseConfig` and `reinit`, ParallelLedDriver calling `parseConfig` and `setStatus` from the encode task itself; RtspDriver's and ParallelLedDriver's `setStatus` from `tick()`; PreviewDriver's `downscale_` and `keptIdxAllocFailed_`. -The textbook move is the one live reconfiguration already promises, a change applied on the next frame: the main loop records the change, and `Drivers::tick` applies it at the frame boundary, after `quiesceEncode()` and before it notifies the worker, so a driver's state has one writer at a time. The thread sanitizer lane reports none of these today, since no test changes a control while the encode task runs; the first test that does pins the move. -The same pass covers ParallelLedDriver's `peripheral` swap, which disengages the render split through the quiesce hook and, as `peripheral` is not in its `affectsPrepare`, leaves it off until another prepare runs. - -### An identity correction is one copy (2026-10-14) - -Every driver runs `Correction::apply` on every light, even when the correction changes nothing: brightness 255 (global and local), the linear curve, a fixture profile whose channels match the layer's, and no dimmer, white or motion channels. -The hottest-path rule says that case is a copy: `Correction` knows at rebuild whether it is the identity, and a driver that sends its buffer whole (Network Send, the panel cards) copies the layer's bytes instead of looking each one up. -The default CIE curve is not the identity at 255, so the skip applies only with the linear curve. It applies to every driver once the sender already sends the output's byte order; network and panel drivers then send one copy, while LED strips still encode bits but drop the per-byte lookup and reordering. - -### Per-die color trims, white balance on the output (2026-10-19, from #139) - -A trim per LED die corrects a strip's color cast: `balanceRed`, `balanceGreen` and `balanceBlue` turn a die down, and `whiteLevel` trims the white die of an RGBW strip, which the RGB trims cannot reach. [#139](https://github.com/MoonModules/MoonLight/pull/139) built it folded into the same table fill as the brightness curve, so the hot path stays one lookup per channel. It is output calibration for every board, so it is a topic of its own, decided when a strip with a visible cast is on the bench. - -### Drivers' palette and fixture-profile lists, sent once rather than in every view (2026-10-13) - -Drivers is the largest root in the state, 35 KB of the Teensy's view where Services is 10 KB, and two lists are 25 KB of it, mostly repetition. -The `palette` options carry 66 palettes with 16 hex colors each for the dropdown's swatches, which change only with the firmware or a palette script: the state carries the names, and the page fetches the colors from a route the browser caches across reloads. -FixtureProfiles' `profiles` summary is 1.9 KB, and its `detail`, 14.5 KB, repeats each expanded row's editor fields, their name, type and bounds, for every profile: the row schema is sent once, as `optionSets` already sends the role names, and each row carries its values. -Together Drivers falls to about 12 KB, and the largest single control, the block a streamed state still needs whole, from 15 KB to a few. - -### LED output from a Raspberry Pi's own pins (robwomp, 2026-10-11) - -Suggested on Discord after the Teensy port: a Pi runs MoonLight today as the Linux desktop build, so its LEDs are reached only through the network drivers. -Driving a strip from the Pi's own header needs the platform backend that does not exist yet (`src/platform/rpi/`, the same prerequisite as the sensor item below), and a `LedPeripheral` behind it. -The routes differ by model: on a Pi 4 and earlier, DMA through the PWM, PCM or SPI peripheral, the way rpi_ws281x does; on a Pi 5 the header sits behind the RP1 chip, whose PIO blocks are the route. -SPI is the one path every model shares, one lane per SPI bus with each WS2812 bit encoded in SPI bits, and the place to start. -Portable GPIO libraries (WiringPi, libgpiod) are too slow for LED timing, so they serve buttons and relays, not this. -On a Pi 5, own PIO programs reach the hardware through the kernel's PIO driver, which piolib shows; Adafruit's PioMatter proves HUB75 that way, and HUB75 on a Pi is a second backend once this one exists. -The research, with sources: [the LED-driver analysis](leddriver-analysis-bottom-up.md#raspberry-pi). - -### Logarithmic brightness, and a power budget the device knows about (2026-09-02) - -`Drivers.brightness` scales the output linearly, and perceived lightness is not linear: the eye is -closer to logarithmic, so the bottom quarter of the slider spends half the power budget for a modest -visible change while the top half buys little and costs a lot. - -**Measured on MM-StadBeest** (LightCrafter 16, 1440 lights, through the new `power.mls` readout): - -| brightness | rail | current | -|---|---|---| -| 16 | 4.86 V | 0.9 A | -| 60 | 3.9 V | 4.3 A | -| 120 | (browned out) | | - -Current is roughly linear in the control value, so the usable range is compressed into the bottom of -the travel and the top of the slider is a power hazard rather than a setting. The board crashed -twice during this session at brightnesses a user would reasonably try. - -**Two pieces, and the second is what actually prevents the crash:** - -- **A gamma or log curve** from the control value to the output duty, so equal slider steps look like - equal brightness steps. The open question is WHERE: on the `brightness` control (every driver - inherits it, but the number then means something different to OSC, MQTT and every saved preset) or - in each driver's output stage (no meaning change, duplicated per driver). -- **A power budget from measured values.** A board that measures its input voltage or current dims when the voltage sags below a threshold or the current passes a limit (see the power-monitoring entry below). A board without sensing relies on safe mode and the fade-in, which bring it back reachable after any supply failure. - -NOTE the rail sag above is NOT the supply's fault: an LRS-350-5 delivers 60 A, and this browned out -at 4.3 A. Roughly 0.25 ohm of series resistance in the feed, so wiring and injection points, which -is worth measuring before tuning anything in firmware. - -**Measured again 2026-10-09**, full white on all four channels: 4.7 V at 0.5 A, 4.1 V at 9 A, 3.3 V at 18.4 A, held for minutes while USB powered the board. The feed is now about 0.08 ohm (2.5 m of 2 mm² speaker cable each way), about 26 W of heat at full load. Powered from the feed alone, the board hangs under that sag rather than resetting, since the chip's supply stays above the brownout level; raising that level to 2.98 V, the S3's level 3, still hung without a reset. Safe mode counts a power loss within a minute of starting, and the lights rise over five seconds, so two power cycles bring a hung board back dark and reachable (bench-verified). What remains is the device acting on what it measures: dimming when the input sags or the current passes a limit, on boards that measure. - -### SE16 / LightCrafter power monitoring: sense pins now free, module still to build (2026-09-02) - -Both boards carry voltage and current sensors, and MoonLight records the pins -(`MoonBase/Modules/ModuleIO.h`, board presets): **SE 16 V1 voltage GPIO 8, current GPIO 9; -LightCrafter 16 voltage GPIO 5, current GPIO 6**. - -Those pins were occupied by our LED driver's `clockPin`/`dcPin`, which looked like a hard conflict -until the meaning of those controls settled it: they are the **sacrificial** WR and DC lines -`esp_lcd` mandates to build an i80 bus, toggled harmlessly with nothing wired to them -(`MultiPinLedDriver::addBusControls`). Any free GPIO does, so they moved rather than the sensors: -SE16 to 16/17 (it has 4/16/17 spare, so its native USB on 19/20 stays free) and LightCrafter to -19/20 (its only spare pair, and it uses the UART bridge on 43/44 anyway). - -**A view exists:** `moonlive/services/power.mls` reads the LightCrafter's GPIO 5 and 6 with the board's own conversions and shows volts and amps, bench-read on the StadBeest (2026-10-09). It shows and does not act; acting is the power-budget entry above. - -**Still open:** `AnalogService` shipped (plan step 3) and is host-verified, so the consumer exists. -What remains is per board: `voltagePin`/`currentPin` in the two device definitions, "Power -monitoring" moving from `planned` to `supported`, a scale factor for each (the divider ratio and -shunt value, which are NOT in MoonLight's preset and need the schematic), and a reading checked -against a meter. - -Worth doing because it also gives the ADC seam a real bench rig: an on-board analog signal beats a -hand-wired potentiometer. NOTE the pin move is unverified on hardware, both boards being offline -when it was made: confirm LED output still works after the change before trusting it. - -### LightCrafter 16: questions for the maker (2026-10-07) - -The [LightCrafter 16 page](../../reference/hardware/lightcrafter-16.md) holds what the catalog and limpkin's pull requests say. -These are open, to ask limpkin: - -- The module part, the level shifter, the output voltage and the connectors. -- The power input: the connector, the voltage range, the fusing and the reverse-polarity protection. -- Which USB-C connector is the S3's own USB and which the serial bridge, and which chip the bridge is. -- Whether the S3's own port supplies 5 V to a plugged-in device, such as a MIDI desk. -- The RS-485 transceiver and the Hall current sensor parts. -- A schematic and a pin-layout image. - -The device model uses GPIO 19 and 20 as the LCD driver's unused signal lines, which are the S3's own USB data pins; that choice is unverified on hardware, and a USB desk on the board would need them. - -### MoonI80 streaming ring — 48×256 shipped; open instruments and cleanups - -The ring's two regimes ship and are wall-verified through 48 strands × 256 (12,288 lights): prime-only when the frame fits the pool, the clock-oracle lapping ring above it (the near-prime pool — the ISR encodes only `nSlices − ringBufs` slices per frame), with `ringAuto` deriving the geometry per config and `shiftOverclock` trading the fps ceiling against '595 shift margin. The mechanism lives in the code + the technical page; the design arc in `docs/work/past/plans/` (the MoonI80 plans, all marked). Open items: - -- **Last-8-panels white flash, the "44-46 flash" (OPEN: the lanes ≥ 32 encode path).** On the 48×256 wall, brief WHITE/bright flashes (not wrong colors) over an otherwise-correct image, confined to strands 40-47 (the last 8 panels), mostly panels 44-46, wandering within the last 8. **Brightness-gated: clean below 5, flashes at ≥5**: since brightness scales pixel values through a LUT, below 5 the frame collapses to near-all-zero bits, so the gate really means *the flash needs SET (one) data bits*. **The same physical wall runs clean on hpwit's driver, so it is OUR driver, not the hardware.** **A live pin reorder located it:** the reorder moved the strands and the flash stayed with lanes 32 to 47, so it is the lanes ≥ 32 (`maskHi32`) encode path under the wall's exact active set, not the last '595, GPIO 17 or the latch bit. Ruled out by live hardware tests (do NOT re-chase): (1) the encode source (PSRAM vs internal; the ISR-source staging fix did nothing, reverted); (2) the ISR/lapping regime (the flash is on prime-encoded rows too); (3) it being a tail-*rows* phenomenon; (4) shift-clock margin via `shiftOverclock` (already at the 20 MHz OFF setting, still flashes); (5) bus-bit latch adjacency (a `latchBitHigh` diagnostic moved the latch to bit 7, still flashes). The encoder passed a host fuzz over RANDOM masks (`ParallelSlots.h` byte-for-byte correct), which never hit the wall's trigger: next, reproduce on the host with the wall's real geometry, active set and content, starting at the 64-bit mask round-trip (`maskLo32 | maskHi32 << 32` in `ParallelLedDriver.h` against the encoder's re-split in `ParallelSlots.h`). All diagnostics from this investigation are reverted, and the tree is clean. -- **MoonI80 direct mode flickers below ~30 lights on a PSRAM frame buffer (OPEN, cause unproven).** Ten GRBW lights on an SE16 (S3, octal PSRAM, one strand) flicker continuously and periodically report `no LED output` — the dead-frame guard after 8 consecutive `busWait` timeouts, i.e. the transfer never signalled completion. Above ~30 lights it is clean, and **i80 and RMT drive the same wiring perfectly**, so it is not the encoder, the layout, the wire, or the strip. **Established:** moving the buffer from PSRAM to internal RAM makes it stop outright (bench-verified). **Not established:** why only small frames — the stall-to-frame ratio (2.9% at 10 lights), the wait budget (*more* generous at small sizes: 55x the wire time vs 2.8x at 1000 lights) and PSRAM alignment padding (zero; the frame is already 64-byte aligned) were all checked and all fail to explain it. Leading untested theory: a short frame gives the DMA no runway to prefetch through a PSRAM/cache-contention stall. **An internal-RAM fallback for small frames was written, measured and deliberately reverted** — internal RAM is the scarce pool, MoonI80 targets large fixtures, and a short strand is the i80 backend's job; a patch that spends scarce RAM to hide an unexplained cause is worse than the open bug. Next instrument: `loopbackTest` + `loopbackIntrusive`, which captures what the peripheral actually emitted and separates a corrupt frame from a stalled transfer (needs the RX jumper pin — the SE16 routes its LED outputs, so pick one that can read back). Full write-up in [lessons.md](../../work/past/lessons.md). -- **Ring bus init hard-fails instead of stepping down when `ringBufs` is raised past what RAM allocates** (wall went dark until the control was lowered again; the pool alloc steps down but a later allocation, likely the descriptor link list, does not). A control change must degrade, never dark the output. -- **~1-frame white/colored flash every ~5 s** seen at some configs — plausibly fixed by the frame-close latch word (a strand whose last data bit ended HIGH missed its reset that frame); soak-observe on the wall before closing. -- **Prime barrier fps cost.** The ring's prime holds a busy-wait until the previous frame's deterministic wire end (`waitWireDrained` in `primeRingRange` — the barrier that keeps the next prime off buffers the DMA is still draining; the frame's last slices lap into the FIRST buffers, so the prime hits them first and no counter sees the repaint). The wait is ~0 when the snapshot + render gap already span the wire, but on fast frames it serializes wire → prime and caps fps at 1/(wire + snapshot + prime). If the fps work wants that overlap back, the barrier can go finer-grained (per-buffer: buffer b is safe once the drain passes slice `b + nSlices − ringBufs`) — measure first. Do NOT drain-gate `done` in the ISR instead (deadlocks; wall-measured as flicker-then-"no LED output"). -- **Multi-strand loopback**: the instrument drives one `loopbackStrand`, so it is structurally blind to a multi-strand fault (it passed while the wall was visibly corrupt). -- **Shift-mode loopback host coverage**: nothing in the suite drives shift-mode loopback end-to-end through the mock bus — the detection gap that let two loopback bugs live unnoticed. (The stall bug itself is fixed: capture-first alloc + pool step-down; verified on two boards.) -- **Loopback teardown leak + heap fragmentation**: cycling the private-bus loopback drops ~80 KB internal and the heap stays fragmented (measured maxBlock 13–44 KB with 240–310 KB free). Capture-first + step-down made runs reliable despite it, but the leak itself stands — chase when the loopback is next touched. -- **Ride-the-live-ring loopback — parked**: built (`loopbackIntrusive` + the snapshot pattern hold) but an RMT-RX cannot capture on a GPIO the LCD_CAM is actively driving (reads 0 sym). Revival path: briefly output-detach just the RX pin for the capture window. -- **Diagnostic surface** (`ringDbg` incl. tw/ts/tp + sg/se, the dbg statics, the `kCyPerUs = 240` hardcode): kept deliberately through the tuning era; gate/remove when the ring fps work closes. -- **fps header reports the module TICK rate**, not the frame rate (`frameTime` is the real one); any fps claim predating 2026-07-17 reads with that in mind. -- **`ringAuto` "just works" — verify the geometry pick on smaller configs.** `ringAuto` (default on) derives `ringRows`/`ringBufs`/`ringPadUs`, and those manual knobs are already dev-only (`setAdvanced`, expert mode). The pick is wall-verified at 48×256; the smaller common configs (16×256 and below) still need a bench pass. If any has a strictly better geometry `ringAuto` misses, teach it that ONE *measured* rule (no per-effect/per-density heuristics — the flicker was never geometry). `ringAuto` itself stays visible as the recourse until this proves it always picks right, then it can go expert-only too. (The fork-join flicker that once blocked this shipped fixed — snapshot fork removed.) - -### PSRAM-at-shift-clock: verified NOT a viable lever for more lights/strand (research, 2026-07-16) - -A recurring idea is to "borrow from direct mode": direct mode streams a huge frame straight from PSRAM (2048 lights, clean; SE16 drives 8192 lights direct/whole-frame at ~19.5 ms), so could shift mode run a lower pclk and stream its whole frame from PSRAM too, trading fps for unlimited length? **Verified answer: no.** The shift pclk is bounded by the WS2812 waveform, not by the '595 and not by divider elegance: slot = 8 bus words / pclk, and *lowering* the clock lengthens T0H toward the max-white washout. The practical floor is the `shiftOverclock`-OFF rate, 20 MHz (T0H 400 ns — wall-verified; 16 MHz is already all-white), which barely dents the PSRAM demand. There is no shift pclk that is both slow enough to stream from contended PSRAM and fast enough to keep T0H under the 0-vs-1 threshold. - -The bandwidth arithmetic (datasheet-derived): DMA demand = bus-bytes × pclk. Direct 8/16-bit = 2.67/5.33 MB/s; shift 8/16-bit = **26.7 / 53.3 MB/s**. S3 OPI PSRAM (octal, 80 MHz DDR) is 160 MB/s *theoretical* but only **~40–84 MB/s sustained/contended** in practice (Espressif's external-RAM guide: DMA-to-PSRAM bandwidth "is very limited, especially when the core is trying to access external RAM at the same time"; PSRAM shares the flash cache region). So direct demand sits far under the floor (streams fine — proven), while shift 16-bit demand *exceeds* the ~40 MB/s contended floor and shift 8-bit sits inside the underrun zone once WiFi/HTTP/CPU cache traffic competes. Because WS2812 is one unbroken self-clocked stream, one FIFO underrun garbles the rest of the frame. This is **datasheet-consistent with**, and MEASURED to match controlled A/B (board B, same PSRAM/chain, only the clock varied: 2.67 MHz PSRAM drives, 26.67 MHz PSRAM never completes at any size) and the 2026-07-16 `forceRing` re-confirmation (whole-frame at 2880 stalls). **Proven:** the effect (PSRAM stalls at the shift clock, drives at the direct clock). **Not instrumented (needs a bench measurement if ever doubted):** the exact mechanism — contended-sustained-rate FIFO underrun vs PSRAM read latency vs cache/MMU contention — was inferred from the clock being the sole variable, never isolated with underrun/bandwidth counters. - -**Conclusion — this does not open a new path; the proper ring fix already is the path.** The internal-RAM footprint of the ring is NOT set by light count: the ring transposes from a PSRAM-resident source into a small fixed internal buffer pool, so PSRAM is never on the DMA's read path at all. The 240-light wall is the `kRingBufs=16` no-reuse stopgap (the wrap read-while-write race), NOT the ring's design — and "more buffers" is a confirmed dead end. The shipped ring (above) holds internal RAM constant at arbitrary light count, which is exactly the "unlimited lights/strand" the PSRAM-hybrid idea was reaching for — obtained the correct way, at the mandatory shift clock, without PSRAM on the read path. **Action: none — the ring shipped; the "lower shift pclk + PSRAM whole-frame" hybrid is closed as physically blocked and should not be re-attempted.** (If the mechanism is ever contested, the one bench measurement worth doing is registering GDMA underrun/FIFO-empty counters at 26.67 MHz whole-frame-PSRAM to distinguish underrun from latency — but it would not change the conclusion.) - -### RmtLedDriver's out-of-memory status has no test, and cannot have one yet (2026-09-09) - -`frameUnusable_` reports a frame buffer the driver could not get (or, on classic, one that landed in -PSRAM where the refill cannot read it), and two guards keep that error from being overwritten by the -resting "driving N of N" status. All of it shipped unpinned, and not by oversight: the desktop -`allocInternal` is a flat `trackedAlloc` that never refuses and ignores `setTestMaxAllocBlock`, and -`ptrIsPsram` is hard-coded false. A test written against that would pass whatever the driver did. -`unit_JsonSink_overflow` shows the trap: it grows until a 64-bit host might refuse and accepts "no -refusal" as a pass, so it tests nothing on a machine with RAM. - -**What it takes:** a cap inside the desktop `allocInternal` that `setTestMaxAllocBlock` (or a new -`setTestInternalCap`) drives, so a test can make the frame allocation fail deterministically, plus a -`setTestPsramFrom(ptr)` seam so the PSRAM branch is reachable off-target. Then three cases: the -status appears, `prepare()` does not replace it with "driving N of N", and `reinit()` does not clear -it (the bug the bench found: the error retracted microseconds after it was set). A platform change -with its own review, which is why it is here and not in the change that shipped the status. - -### The ring's memory readout counts one buffer, not the pool (CodeRabbit, 2026-09-09) - -`ParallelLedDriver::driverHeapBytes()` derives its DMA figure from `peripheral_->busCapacity()`, -which is `st->cap`: the size of ONE buffer. It then adds a second when `busBuffer(1)` exists, which -is right for the double-buffered path and wrong for the ring, where `createRingState` allocates -`ringBufs` slices (up to 30 at the 48x256 geometry) plus the shared zero-pad. So a ring board -under-reports its DMA memory by roughly the buffer count, on the card that exists to make exactly -that number visible. - -Not a correctness bug: the memory is spent either way, and nothing sizes an allocation from this -value. It is a reporting bug in the one readout a user consults before adding lights. - -**What it takes:** an aggregate size on the peripheral (the ring knows `ringBufs * bufBytes + pad` -at creation) rather than arithmetic in the driver, which cannot see the pool. Wants a ring-mode -accounting test alongside, since the existing ones only cover the non-ring path. Touching -`createRingState` means a bench pass on the wall, which is why this is its own change. - -### MoonI80 ring — boot / first-frame-after-rebuild trips a transient give-up status (2026-07-16) - -A cosmetic residual left after the rebuild-wedge fix (below): on boot, and for a beat after any shift-ring rebuild, the driver shows **"output stalled"** even though `wireUs` reports live completions — the *first* frame after a fresh ring build occasionally misses its completion window and trips the dead-frame give-up before the ring settles, so the stale error latches until the next interaction clears it. It is NOT the old wedge (that stayed dead until reboot; this self-clears on any control edit and the ring is genuinely driving underneath). Two clean fixes to weigh: (a) don't count the very first frame after a rebuild toward `deadFrames_` (give the fresh ring one grace frame), or (b) have the give-up retry re-derive status the moment `wireUs` shows a real completion. Low priority — the LEDs drive correctly; only the status string is briefly wrong. - -### MoonI80 whole-frame async double-buffer corrupts output on the ESP32-S31 (2026-07-23) - -`doubleBuffer` (default on) runs the async deferred-wait path (`tickAsync`): encode frame N+1 into buf[1] while the GDMA clocks frame N out of buf[0], costing `max(encode, wire)` per tick instead of `encode + wire`. On the **MoonI80** backend (our own GDMA below esp_lcd) this is a genuine speed win and is **clean on the S3 and P4** — bench-verified on a P4 driving a 64-light strip on Parlio-adjacent pins, and it's what lifted the P4 whole-board rate 48→76 fps. **On the ESP32-S31 it flickers** (corrupted/torn frames), on **any** pin — bench-isolated on GPIO 60 *and* GPIO 42, so it is NOT pin 60 (the onboard WS2812) and NOT the ring (the ring path is unreached here: expander off → `wantsRing()` false → whole-frame). Turning `doubleBuffer` OFF (single-buffer `tickSync`) is clean on the S31. So the fault is **chip-specific**: MoonI80's hand-rolled async alternation reprograms the GDMA link list per frame and relies on GDMA EOF-in-start-order semantics + a fixed DMA→FIFO settle delay (`esp_rom_delay_us` before `lcd_ll_start`), both tuned on S3/P4; the S31 is a newer RISC-V chip whose GDMA/LCD_CAM revision evidently differs (EOF timing or settle requirement), so the async path displays a still-in-flight or half-encoded buffer. **This is bench-verification territory** — do NOT "fix" it from code reading (the whole GDMA/ring subsystem is bench-proven and code-read corrections to it have been wrong before); it needs a logic-analyzer / GDMA-underrun-counter pass on the S31 to find the exact EOF-timing or settle-delay difference. **Decision (PO, 2026-07-23):** leave `doubleBuffer` default-on (it is a real, correct win on i80 / Parlio / MoonI80-on-S3/P4 — confirmed all three peripherals genuinely honor the second buffer, NOT i80-only), and backlog this. Two fixes to weigh when picked up: (a) **chip-gate** — MoonI80's whole-frame `busInit` declines buf[1] on the S31 only (`if constexpr (platform::isEsp32S31)` → falls to clean `tickSync`), preserving the async win everywhere it works and losing nothing on the S31 (async never worked there); or (b) **root-cause** the S31 GDMA timing so async works there too (recovers the S31 speed, more bench work). Workaround today: on the S31, MoonI80 with `doubleBuffer` OFF, or use `i80`/`Parlio` (both clean with double-buffer on the S31). - -### Graceful blank-on-stall — a stalled bus should DARKEN the strip, not leave it lit with garbage (2026-07-15) - -When the bus stalls mid-frame the WS2812 strip is left holding **random / max-brightness lights** (often all-white — the all-ones failure pattern) that only a **power cycle** clears. That is a robustness gap: WS2812s latch their last received color and hold it until re-clocked or power-cycled, so a frame that dies mid-stream leaves every light past the failure point stuck bright. The give-up guard today stops *spending the render thread* on a dead bus (correct) but does nothing about the *strip's* state, so the user sees a wall of garbage LEDs and reaches for the plug. - -**The fix: on give-up (and on a rebuild that SHRINKS the reachable range), clock out ONE clean all-black frame** — every lane LOW → every light receives 0,0,0 → the strip goes dark. This turns "stall = a wall of random bright LEDs until power-cycle" into "stall = strip cleanly dark," which is the honest *degraded, not crashed* state the *[Robustness](../../explanation/architecture/moonmodule.md#robustness)* rule asks for. It also covers the PO's specific case (drop `ledsPerPin` 256→128 and the abandoned 128–256 range stays lit): a full-length black frame on the shrinking rebuild blacks the whole physical strip once, no boundary to compute. - -**The load-bearing caveat:** if the bus is wedged *because it cannot complete a transfer*, a black frame may not clock out either — so this is a best-effort **attempt**, not a guarantee: try the black frame on give-up; if it clocks, the strip darkens; if the DMA is truly dead, we are no worse off than today (and the rebuild-wedge fix above is the real cure for *that* class). Note it must be a genuine transmitted frame (all lanes driven LOW through the normal encode+transmit), not merely zeroing the DMA buffer — the strip only changes on a clocked frame. Pin it with a test: after `kDeadFramesBeforeGiveUp` dead frames, the driver emits one all-zero frame through the transmit seam (the mock asserts a zero frame was handed to the bus), and a subsequent recovery resumes normal content. - - -### Classic-ESP32 shift-register ring on raw I2S — the high-light-count classic driver (WANTED) - -**The PO wants shift-register output on classic ESP32 boards at the MAXIMUM light count**, which the current whole-frame `I80LedDriver` (esp_lcd i80 → I2S on classic) cannot deliver: its frame buffer must be **internal RAM** (the classic I2S DMA cannot read PSRAM — IDF rejects it outright), so it scales against a ~76 KB wall and caps at **~2048 lights**. Above that, classic needs the **hpwit refill-ring model**: a handful of tiny internal DMA buffers (~1.2 KB total, light-count-independent) refilled by the I2S EOF ISR, which transposes the next pixel row out of a **PSRAM** framebuffer as it goes. The DMA never touches the source array — only the CPU/ISR does — so the source lives in PSRAM and internal RAM stays constant. This is how hpwit (and WLED-MM/MoonLight on his driver) reach **48×256 ≈ 12K lights at ~100 fps on classic silicon with WiFi up**, with the same '595 expander. - -**Why it must be a SEPARATE driver, not a flag on `I80LedDriver`:** `esp_lcd` owns the classic I2S DMA and only does whole-frame, so the ring cannot be bolted onto the esp_lcd path — it needs a second classic driver written on the **raw I2S registers** (`i2s_ll` / the LCD-mode register file directly, below esp_lcd, the way the S3/P4 MoonI80 backend sits below esp_lcd on LCD_CAM). Its ISR is small enough to fit the classic's ~70 KB IRAM (hpwit's does), and — the key move — it registers the interrupt **without** `ESP_INTR_FLAG_IRAM` (his source comment: removed "to avoid Cache Disabled but Cached Memory Region Accessed") so the refill ISR is legally permitted to read the PSRAM framebuffer. The trade it accepts vs. our whole-frame path: it **gives up the whole-frame path's WiFi-underrun immunity** (a WiFi burst that starves the refill ISR can glitch a frame), which the ring mitigates with a tunable buffer-count cushion (`nbDmaBuffer`, hpwit's default 6). For a ≤2K-light WiFi-busy install the whole-frame i80 is still the better choice; the ring is for the >2K-light case classic cannot otherwise reach. - -**Reference (study, don't copy; write fresh against our architecture):** the line-by-line source read is in [led-driver-psram-ring-analysis.md](led-driver-psram-ring-analysis.md); the framing is [MoonI80Peripheral](../../../src/light/drivers/MoonI80Peripheral.h) (which calls the internal-RAM-ring-with-CPU-refill "the only thing that can ever work on the classic ESP32," deferred to a phase 2). The S3/P4 MoonI80 ring is the closest in-tree prior art for the ring mechanics (linear self-terminating chain, per-drain refill, drain-count termination), but its refill is a task and its buffers are internal-only *because the LCD_CAM GDMA can't sustain a PSRAM read at the shift clock*; the classic I2S ring is the inverse (PSRAM framebuffer legal, ISR refill mandatory), so it borrows the *shape* but not the constraints. Do the S3/P4 **ISR-refill + `MM_HOT`** work first (it proves the ISR-refill pattern in-tree on the friendlier unified-DIRAM chips); the classic raw-I2S ring is the next tier up, reusing that pattern where IRAM is genuinely tight. - -**Why MoonI80 cannot serve the classic, and what this driver inherits (2026-09-06).** `MoonI80` -is written against **LCD_CAM**: it drives the GDMA link list and the LCD registers directly -(`gdma_link_*`, `lcd_ll_*`) to bypass `esp_lcd`'s per-transaction peripheral reset. The -classic ESP32 has no LCD_CAM at all; its i80 is the **I2S** block in LCD mode, a different -peripheral with its own register file (`i2s_ll_*`) and its own DMA, so none of MoonI80's code -applies and `MoonLedDriver::lanesAvailable()` reports `platform::lcdLanes`, which is 0 there. The -picker hides the backend rather than gating it, which is why the classic has exactly one parallel -route today: `esp_lcd`'s I2S backend, whole-frame, internal-RAM-only, capped near 2048 lights. -This driver is the second route, and it stands in the same relation to `esp_lcd` on I2S as MoonI80 -does on LCD_CAM: same shape, no shared code. - -Two things it inherits from the 2026-09-06 i80 work, both worth keeping. It should claim **I2S -instance 1** and leave 0 for audio, for the reason recorded in the instance-split entry above -(instance 0 alone carries the PDM converters, nothing needs 1), and owning the peripheral directly -it can simply ASK for instance 1 rather than steering `esp_lcd` by parking instance 0, which is the -workaround the current backend needs. And it inherits the package-aware pin refusal: a pin the -package lacks wedges the flash cache silently, which cost a full day of bisection on the -ESP32-PICO-V3-02. - -### P4 Parlio streaming ring — lift the P4 Parlio ceiling past ~21K to light-count-independent (WANTED) - -**Port the ring concept to the P4 Parlio path**, to drive far more than its current whole-frame ceiling. troyhacks' MoonLight Parlio driver reaches **~21K LEDs RGB (~16K RGBW)** at 16 lanes — but NOT by materialising the whole encoded frame: he stages into a **fixed ~512 KB PSRAM buffer** and DMAs it out in **64 KB chunks** (`max_transfer_size = 65535`), so the DMA never needs the whole frame contiguous. That is the SAME idea as our MoonI80 ring, applied to Parlio on the P4 (where — unlike the S3 shift clock — the DMA *can* sustain PSRAM reads at the WS2812 rate). His ceiling is a *chosen buffer size*, not a hardware wall, so it caps at ~21K. - -**Two tiers, in order:** -1. **Match him (moderate):** chunk our Parlio DMA the way he does (bounded PSRAM staging + 64 KB bursts) instead of one contiguous encoded frame — this alone lifts our P4 Parlio ceiling to his ~16–21K. Our Parlio path already uses PSRAM; the change is the chunked-DMA transfer shape, not a new architecture. -2. **Beat him (the ring):** run our MoonI80 **streaming ring** on P4 Parlio — small internal buffers, refilled per drain from a PSRAM source, holding internal RAM *constant* regardless of light count (the hpwit model our ring already implements). troyhacks' 512 KB buffer is still *bounded*; the ring is not, so this goes past ~21K to light-count-independent. It reuses the ring mechanics we already have (linear self-terminating chain, per-drain refill, drain-count termination); the P4 Parlio backend gets a ring variant beside its whole-frame path, the way MoonI80 has both. - -**Prerequisite / sequencing:** tier 2 wants the **ISR-refill + `MM_HOT`** work done first (same as the classic ring above), since a high-rate Parlio refill benefits from the ISR-grade determinism. Confirm the CURRENT P4 Parlio tested ceiling first (re-measure on the P4 bench) before claiming a head-to-head — the ~21K figure is troyhacks' *buffer-fit* limit, not a verified-on-wire number, and his code steps the clock down at 256/512 LEDs per lane (signal integrity at long strands), which our own measurement must account for. Reference: the parlio source is `src/MoonLight/Nodes/Drivers/parlio.cpp` in the predecessor; our Parlio backend is `platform_esp32_parlio.cpp` + `ParlioLedDriver.h`. - - -### Multicore Step 2b — ping-pong second output buffer (deferred, workload-gated) - -The shipped render↔encode split (Step 2a, `multicore` control) uses one `Drivers::outputBuffer_`, so the composite is serialized at the boundary. A **second** output buffer would overlap even that (core 0 composites into B while core 1 encodes A), at the cost of one full frame buffer (~48 KB at 16K lights). The read-only `stall` KPI was built as the trigger metric, and it splits cleanly: a **heavy effect** (render-bound) shows `stall` ~1 µs (S3) / ~15 µs (classic) — 2b recovers *nothing*, core 0 already fills the encode window; a **light effect on many lights** (output-bound — a solid color or slow gradient across 16K lights) shows `stall` 6–11 ms — 2b recovers that whole wait. So it's a real but narrow win: **gated on the product owner naming that light-effect/many-lights workload as worth the 48 KB**, not on a generic fps gain. (Design context in the shipped Plan-20260713 - Multicore Step 2.) - -### Frame pacing — decided against (record) - -MoonLight targets a fixed 60 fps; MoonLight deliberately does not (settled with the PO 2026-07-12). The architecture is *render-uncapped + time-aware effects* (`beatsin8`/`millis()`-driven, a CLAUDE.md hard rule), so a whole-engine fps cap is redundant with that rule and would only *reduce* quality below the hardware ceiling; the LED wire rate already paces render physically (30 µs/light), and UI/WiFi responsiveness comes from the per-tick `vTaskDelay(1)` yield, not frame-rate control. Parked as a ~15-line opt-in (`targetFps=0` = unlimited default) *only if* a genuinely CPU-starved device ever appears. - -### Brightness belongs on a fixture's DIMMER channel, not only in the color values (WANTED) - -`Correction::apply()` holds a profile's `Dimmer` channel wide open at 255 and puts brightness into -the R/G/B/W values through `briLut`. That is correct output and it is the only rule that serves -every fixture (an addressable strip has no dimmer channel), but it is not how a lighting console -drives a fixture that HAS one, and it costs real quality: - -- **Resolution.** At 10% brightness the colors are driven at 0-25, about 25 usable levels instead - of 255. A slow fade to black steps visibly; the dimmer channel exists so colors keep full 8-bit - range while intensity varies. -- **Dimming quality.** A moving head's dimmer is often 16-bit or curve-corrected for the LED's - response. Linear scaling of 8-bit color values is the crudest dimming there is, and it is worst - at the low end where the eye is most sensitive. -- **Matching.** A PAR with a real dimmer and a strip without one, both at 20%, do not read as the - same brightness: one dims in the LED driver, the other in integer arithmetic. - -**The rule to implement:** when a profile declares a `Dimmer` role, route the driver's brightness to -that channel and drive the colors at full saturation; keep today's behavior (scale the colors) -only where the fixture has no dimmer. The per-light color values still carry the effect's own -shading, so an effect that paints one light dark stays dark. Watch the one case where fixture and -light are not 1:1: several light cells behind ONE master dimmer cannot express per-cell brightness -through it, so those keep the color-scaling path. - -Found on the bench 2026-08-28 wiring the first moving head. Note the dimmer was not written AT ALL -before that day (the shipped `IRGB` profile could never light a fixture); writing it at 255 is the -fix that made a fixture light, not the finished design. - -### Blending adds motion channels, which is meaningless for aim (WANTED) - -`blendMap` treats a light as `n` opaque bytes, so ADDITIVE blending sums pan and tilt across -layers, and the separable modes (multiply, screen, darken, lighten, difference, exclusion) combine them the same way. Two layers driving one moving head aim it at neither position, and saturate to hard-over -as soon as both are moderately positioned. - -Only the combining modes are wrong. **Opacity blending on motion is a feature and must be kept**: it is an -interpolation, so a layer fading in sweeps the head smoothly from the old aim to the new one, -which is what a console does and better than a snap. The rule (architecture.md) is that -interpolating ops are valid on every channel while accumulating ops are valid only on emissive -ones, so the fix is narrow: in the additive and separable paths, ASSIGN motion channels (topmost writer wins) -instead of combining them, leaving the opacity path alone. Lighting consoles call the same split HTP and LTP: intensity merges highest-takes-precedence, attributes latest-takes-precedence. A discrete attribute such as a gobo slot does not interpolate either, so it snaps rather than fades. - -Not yet reachable in a harmful way: it needs two enabled layers on a fixture carrying motion -channels, and the single-layer path is a memcpy. Worth doing with the motion-writer work, since -both need `FixtureChannels`' offsets on the blend path. - -### Zoom, rotate and gobo have no writer (WANTED) - -Pan and tilt now have a writer: an effect sets them through `EffectBase::setPan`/`setTilt` and -`Correction::apply()` maps the layer slots onto the fixture's channels (`MovingHeadEffect` is the -worked example, bench-driven). **`Zoom`, `Rotate` and `Gobo` still have none**: a profile can map -them and `Correction` carries their offsets, but no effect writes them, so they sit at 0. - -This is the other half of driving a moving head, and it is a domain question, not a plumbing one: a -light is a point with a color, while a moving head is a fixture that emits a BEAM in a direction it -controls live. The backlog's fixture-model item ("moving heads, beams", per-emitter targets) is -where the model belongs; this entry records the concrete gap in the meantime. Bench fixture and its -channel map: [light fixtures reference](../../reference/hardware/light-fixtures.md). - -### Pan/tilt travel is hardcoded, and positioning is 8-bit (WANTED) - -Two fidelity gaps in how an aim becomes a real beam, both found on the bench 2026-08-29 reviewing -the preview's beam cones. - -**Travel is assumed, not declared.** `EffectBase` and the preview both hardcode *540 degrees of -pan, 180 of tilt*, which is the common mid-size convention and happens to match the bench fixture. -Real heads vary: 630 pan on larger bodies, 360 or 530 on compact ones, 270 tilt on beam fixtures. -Nothing declares which fixture is plugged in, so the preview draws every rig with the bench head's -travel and silently lies about where a beam points. Zero point varies too (some fixtures put -mechanical zero at DMX 0 rather than centering at 128), and the axis direction is a setting on the -fixture's own display (`rPAN` / `rTIL` on the bench head), so even one fixture has no fixed mapping. - -**The DMX byte is already the right abstraction, and it is why nothing can over-rotate.** An effect -never emits an angle: `MovingHeadEffect::axis()` maps its sweep onto a 0..255 byte and clamps, so -0 and 255 *are* the mechanical stops whatever they are in degrees, and the head cannot be asked -past its travel. A sweep that reaches a stop flattens there (the beam parks) rather than wrapping, -which is what the fixture itself does. Degrees exist only for DRAWING. - -So the fix is scoped to the preview, and the open question is where travel is declared: - -- **`PreviewDriver` controls** (`panTravel`, `tiltTravel` in degrees, sent in the aim message) — - small, live-reconfigurable like every other setting, and honest that travel is a property of the - fixture someone plugged in. The leanest thing that stops the lie. -- **The fixture profile**: correct once two different heads run at once, but a profile is a - *channel-role* layout today, and carrying physical travel widens what a profile means. Belongs - with the [fixture model](#fixture-model-moving-heads-beams-long-term), not before it. - -**Positioning is 8-bit while the fixture offers 16.** The bench head has a fine channel for each -axis ([light fixtures reference](../../reference/hardware/light-fixtures.md)); both sit unused, so pan resolves -to 540/256 = about 2.1 degrees per step. Across a room that is a visible jump on a slow sweep, and -it is the bigger fidelity win of the two. Needs a 16-bit path from the effect's sweep through -`FixtureChannels` to the profile's fine-channel roles, so it is the larger job. - -### Built-in fixture profiles never reach a device that has a saved config (WANTED) - -`FixtureProfilesModule` seeds its built-ins only when the profile list is empty -(`if (count_ == 0) seedBuiltins()`), and the persisted list replaces them wholesale. So a newly -shipped built-in profile appears only on a device that has never saved one: every existing device -keeps the older set forever. Adding the mini moving head's profile on 2026-08-28 needed a hand-patch -of the bench device's `Drivers.json`, which does not scale past one board. - -**The fix:** merge by name at boot, seeding any built-in the saved list does not already carry, so -shipping a fixture profile reaches existing devices on their next update. A user's own profiles and -their edits to a built-in must survive that merge untouched. - -### ArtPoll discovery — know which tubes are alive (next increment on NetworkSendDriver) - -`NetworkSendDriver` now unicasts to a list of receivers (`ips` + `lightsPerIp`), which is the Art-Net-4-conformant model. What it cannot do is **tell whether a receiver is actually there**: UDP is fire-and-forget, so a dead tube is invisible to the sender. The spec's own answer is discovery — *"The transmitting device must regularly ArtPoll the network to detect any change in devices which are subscribed"* — and it is the natural next increment. - -**Why it earns its place (product-owner experience, 2026-07-13):** a dead tube in a prior setup *made the other tubes hiccup*. The mechanism is that a send to an unresolvable address stalls or errors **inside the frame loop**, delaying the packets for every destination after it — so one dark tube degrades the live ones, every frame. The send loop is now written to tolerate that (a failed `sendToAddr` drops that packet and moves on, and under `multicore` the whole send is off the render core), but tolerating is not the same as **knowing**: with discovery we can simply **skip destinations that haven't answered**, which removes the stall at its source rather than absorbing it. - -**Scope (~130–150 lines + tests; the receive half already exists).** `ArtNetPacket.h` already builds+parses, `UdpSocket` already binds and reports the sender's IP: -- **ArtPoll send** — broadcast a 14-byte poll every ~3 s (this is the one packet Art-Net *does* broadcast, by design). -- **ArtPollReply parse** — fixed-layout 239-byte reply: node name, IP, and its subscribed universes. -- **A node table** — IP → last-seen; mark offline after ~9 s of silence (3 missed polls, the spec's own cadence). -- **Use it:** skip offline destinations in the send loop; surface the live/dead list as a read-only status. **Bonus, and arguably the real prize: auto-populate `ips`** — the user stops typing addresses at all, which is how a professional controller behaves. - -Do it as its own increment. The multi-destination unicast it builds on has shipped. - -### RS-485 / DMX-512 wired output (future) — the physical-DMX driver - -MoonLight already speaks DMX **over the network** (Art-Net / sACN via `NetworkReceiveEffect`). The missing half is **wired DMX-512 out**: driving DMX fixtures (moving heads, par cans, wired pixel controllers) directly over an RS-485 differential pair, which is what the RS-485 hardware on carrier boards like the [MHC-WLED ESP32-P4 shield](../../reference/hardware/mhc-wled-esp32-p4-shield.md) is *for*. DMX-512 is a 250 kbps async serial frame (a break + mark-after-break + 513 bytes: start code + 512 channels) shipped over RS-485 — the textbook fixture-control transport. A DMX driver would map the light buffer (or a fixture/attribute model — see the [Fixture model — moving heads, beams](#fixture-model-moving-heads-beams-long-term) item below) to DMX channels and clock the frame out a UART in RS-485 mode. - -**What it needs that we don't have yet:** -- **A `platform::` UART-RS485 seam.** The ESP32 UART has a hardware RS-485 half-duplex mode (`uart_set_mode(UART_MODE_RS485_HALF_DUPLEX)`) that auto-drives the transceiver's **DE/RE** (driver-enable / receiver-enable) line — the thing our current pin handling has no concept of (we drive pins as plain GPIO). A DMX driver is where DE/RE control first earns its place, and only for a **bidirectional** channel: firmware DE/RE toggling is what lets one channel switch Tx↔Rx without a hardware switch. A **fixed-transmit** channel needs none — its transceiver is hard-wired to drive. On the [MHC-WLED ESP32-P4 shield](../../reference/hardware/mhc-wled-esp32-p4-shield.md) that split is physical: GPIO 4, 22, 24 are fixed-transmit (no DE/RE control wanted), and only the switchable GPIO 3 channel is bidirectional — the shield handles it with a *mechanical* slide switch (which is how its loopback works). Firmware DE/RE control is what a board would need to make a channel bidirectional *without* such a switch. -- **The DMX frame timing** — the break/MAB is generated by a baud-rate switch or a GPIO toggle around the UART frame; standard, host-testable as an encoder. -- **A fixture/channel-mapping model** — trivial for a dumb pixel-per-channel strip, real work for typed fixtures (pairs with the moving-head fixture-model item; a wired-DMX driver and a network-DMX(Art-Net) input would share that fixture model). - -**The channel-mapping half is now unblocked.** The per-light encode path handles an arbitrary channel count as of 2026-07-13 (the WS2812 drivers' per-light scratch is heap-sized to `outChannels`, no fixed cap — the fix from the multi-channel-preset bootloop, see [lessons.md](../../work/past/lessons.md)). A DMX universe is exactly that model: a light with `channelsPerLight = ` (16-ch moving head, 7-ch par, …), and the buffer's bytes ARE the DMX channel values. So a DMX driver's "map the buffer to channels" step is now the trivial part — it ships the light buffer's bytes straight into the 512-channel frame. What remains genuinely new is the **transport** (the RS-485 UART seam + break/MAB timing) and the **typed-fixture model** (naming which channel is Pan vs Dimmer — the moving-head fixture item), not the encode. - -**Can a board drive XLR fixtures directly? Yes, with an RS-485 transceiver — that's the one required part.** DMX-512 is RS-485: a *differential* pair (D+/D−, ±2–6 V), not the 3.3 V single-ended UART the MCU emits, so an MCU TX pin can NOT wire straight to XLR. A transceiver chip (MAX485 / SN75176 / THVD-class, ~$0.50) sits between the UART and the connector and drives the differential pair; the DE/RE line (the UART-RS485 seam above) flips it Tx↔Rx. **3-pin XLR** carries it: pin 1 = ground, pin 2 = D−, pin 3 = D+. With a transceiver present, daisy-chaining ~10 moving heads (10 × 16 ch = 160, inside one 512-channel universe) over standard DMX in→out is well within the RS-485 limits (32 unit loads / 1200 m); the last fixture wants a 120 Ω terminator (a fixture/cable concern, not the MCU). So whether a catalog board can drive XLR *directly* hinges on one schematic question: does it carry an RS-485 transceiver + XLR/terminal (then yes, direct), or only the WS2812 level-shifted outputs (then a ~$0.50 breakout is needed). Confirm against the [MHC-WLED ESP32-P4 shield](../../reference/hardware/mhc-wled-esp32-p4-shield.md) schematic before treating direct-XLR as a shipping capability. - -Sequencing: it's a **driver** (`src/light/drivers/`) + a platform UART-RS485 seam + a fixture model shared with the Art-Net path — the buffer→channel encode is already done. Plan when a DMX fixture is actually on the bench and a catalog board's `supported`/`planned` list points at wired DMX. The [PinsModule pin-assignment work](backlog-core.md#pinsmodule-strict-reject-on-add-mode-the-one-remaining-increment) covers the RS485/DMX TX/RX/DE slot; this is the driver that consumes it. - -## Integration with other LED and visuals tools - -Distilled from a Discord thread with panel-card users (2026-08-24), where two people drove ColorLight walls from MoonLight and described the pipelines they already run. - -### Preview does not resume after a long tab hibernation (observed once, 2026-08-27) - -Desktop instance at 1024x1024: after the browser tab sat backgrounded for about an hour, the -preview pane stayed static on return. PreviewDriver ticked at ~1 us (no standing request served) -while a FRESH WebSocket client received frames normally, and the Drivers card showed the -encode-worker-stalled latch. A page refresh reportedly did NOT revive it; toggling `multicore` -(a Drivers re-prepare) did. Suspects: the tab-hide hibernation path's tracked retry vs the -wake-up re-request, or per-driver lease state that only prepare() resets. Needs a reproduction -with the WS uplink logged before it can be fixed. - -### One encode, two readers: HLS and RTSP sharing the encoder (2026-09-22) - -Both video drivers call `platform::encoderStart`, and the encoder is a single instance on every target: one ffmpeg child on the desktop, one hardware session on the P4. The second caller used to reconfigure the first's stream silently, so HLS kept serving at RTSP's geometry and bitrate without saying so. **Shipped instead: a claim**, so the second driver is refused with a visible status, the way a driver already reports a port another module holds. That makes the conflict impossible rather than unlikely, and it costs a guard. - -The better end state is the one the RTSP plan describes and the P4 already half-implements: **one encode, two readers**. `rtspTakeFrame` and `hlsSegment` read the same encoded frame today, so a device serving both would encode once and each reader would take what it needs, which is cheaper than either driver running alone twice over. - -What makes it a design task rather than a patch: the two drivers must **agree** on geometry, fps and bitrate, and nothing makes them agree now. HLS derives its bitrate from the grid and picks an encoder by name; RTSP takes the frames raw and names no encoder. The open questions are whose configuration wins when both are enabled, what happens when one changes `targetFps` mid-stream, and whether a reader that stops should relax the shared settings. A refcount alone answers none of those, which is why the claim ships first. - -Build trigger: someone wanting both streams from one device at once. Until then the claim is the honest behavior, and the refusal message names the other driver. - -### HLS upscaling is cache-hostile on large walls (measured, 2026-08-28) - -`HlsDriver`'s `scale` control replicates each light into a scale x scale block. Measured on the -bench P4 at 128x128: **~1 ms per frame at 1:1, ~60 ms at scale 4** (a 512x512 output). The frame -is 16x larger, so ~16 ms would be the honest cost; the extra 4x is the access pattern. The loop -walks LIGHTS and writes each block as `scale` separate short rows scattered across a 786 KB -buffer, so consecutive lights touch distant addresses and every write misses cache, where the -1:1 path writes straight through sequentially. - -**Not urgent, because the default path never hits it:** auto-scale only engages on walls below -the encoder's 80-pixel floor, where the output is small by construction (a 20x10 wall becomes -160x80, 38 KB). The expensive case is a manual scale on an already-large wall, which is also -where upscaling has the least to offer. - -**The fix when it earns its place:** iterate the OUTPUT rows rather than the input lights, so -writes are sequential: for each output row, walk its source row once and emit `scale` copies of -each light's color, then `memcpy` that finished row to the remaining `scale - 1` rows of the -block. Same output, one pass through the destination in address order. - -### Sprite follow-ups (draw::sprite + FlyingToasters shipped; spec + plan in the plans archive) - -Deliberately deferred when sprites landed: P4 PPA acceleration behind the same `draw::sprite` -signature (the 2D-DMA blitter the WLED-MM-P4 world uses via LovyanGFX; ours would sit in the -platform layer, no vendored GFX library), Porter-Duff alpha when a real consumer arrives, and -MoonLive sprite data (needs the stage-3 builtin table + arrays). - -### MoonLight as a video source — NDI first, Spout/Syphon only if proven (open) - -Users asked for MoonLight's rendered output to feed *their* tools, not the other way round. One runs OBS → Spout → his own VLAN-tagged card driver; he asked whether MoonLight could be a Spout source. Input is not the gap: `NetworkReceiveEffect` already binds Art-Net, E1.31/sACN and DDP at once and answers ArtPoll, so any controller can already drive MoonLight. - -**NDI is the recommended first implementation.** It is the AV industry's standard for video over IP, one implementation covers Windows, macOS, Linux and ARM, it discovers by name, and it crosses machines. Spout (Windows, DirectX/OpenGL) and Syphon (macOS, Metal/OpenGL) share a GPU texture zero-copy, so they are lower latency and bit-exact, but they are **same-machine only**, are **two** platform implementations, and leave **Linux and the Pi with nothing**. At LED-wall pixel counts (a 256x256 wall is 65K pixels) the latency difference is far below one frame of the render loop, so it does not decide the choice; coverage does. A Spout user is also reachable through NDI in one hop, since OBS, Resolume and TouchDesigner all speak both. - -**The license shapes the design, and the shape is already established here.** MoonLight is GPL-3.0 and the NDI runtime is proprietary, so MoonLight must not *redistribute* it: bundling would require MoonLight's own license to carry NDI's restrictions downstream, which GPL-3 forbids. The user installs the NDI runtime themselves, exactly as they already install **Npcap** for the panel-card driver, and MoonLight calls whatever is present. - -That is the arrangement `platform_desktop.cpp` uses for Npcap today: resolve the library with `LoadLibrary`/`dlopen` rather than linking it, declare the handful of functions with the library's own signatures rather than including its headers (so the SDK never becomes a build requirement for CI or contributors), and report the feature unavailable when it is absent instead of failing to link. Two independent installs that talk to each other, like Resolume on the same desktop. - -Also note MoonLight renders into a CPU buffer, so a Spout/Syphon path would upload to the GPU purely to hand off, spending the zero-copy advantage it was chosen for. - -### M5Stack Tab5 as a display target — MIPI-DSI, not the H.264 path (open) - -The Tab5 is an ESP32-P4 with a 1280x720 MIPI-DSI panel, and a P4 is already a supported target, so -the question is what its *screen* would show. The P4's H.264 block does not answer it: that encoder -exists to compress an incoming MIPI-CSI camera feed, and the P4 has no hardware H.264 **decoder** -at all (Espressif's own FAQ points at software decode, which will not hold 720p). Driving the panel -is the **MIPI-DSI** peripheral plus the PPA / 2D-DMA blitter, which take raw pixels and never touch -a codec. So the three things a Tab5 could be are separate pieces of work, and only the first is free: - -- **An HLS source**, like any other P4: it encodes its own rendered grid and streams to a TV. Its - panel is incidental, and this needs nothing beyond the P4 HLS work itself. -- **A local wall preview or touch console** — the interesting one, and the real ask: a `platform::` - MIPI-DSI display seam plus a UI on the panel. Related to the PPA acceleration noted under sprite - follow-ups above (same 2D-DMA block), and it is a display *output* seam MoonLight does not have - today; the nearest prior art is the WLED-MM-P4 world's LovyanGFX usage, which we would not vendor. -- **An HLS/video player**, showing another device's stream: blocked on the missing hardware decoder, - so not worth planning. - -### Multi-card walls — does a daisy chain work today? (open, ask before building) - -The ColorLight format has **no card addressing in the PIXEL path**: the destination MAC is a fixed -constant and every card filters on it, so every card on a segment shows the same image. A user with -six cards on a switch observed exactly that. - -The DISCOVERY path does distinguish them. A discovery reply (0x08) carries a controller number at -payload offset 0x62, and the acknowledgement echoes it plus one, which is how a sender tells several -cards apart. Documented by a reader of [Harald Kubota's protocol -write-up](https://hkubota.wordpress.com/2022/01/31/winter-project-colorlight-5a-75b-protocol/) and -confirmed by its author. That is an identity, not a destination: it does not let a sender aim pixel -data at one card, so the same-image behavior above stands. It is what a per-card brightness or -color-temperature feature below would key on. - -The industry-standard answer is **daisy-chaining** — a sending card's ports each drive a chain, and each card takes its region by position in the chain. That user works around it with per-card VLANs and a managed switch instead, which he built for throughput and for per-card color-temperature grouping across mixed panel batches; he described it as his own solution, not a standard. - -**Establish first whether a daisy chain already works with MoonLight** (one contact has a 96K daisy-chained rig). If the cards self-assign by chain position, the standard multi-card case is already solved and nothing is needed. Only if it does not work is there a feature here, and it should follow the daisy-chain standard rather than the VLAN workaround. 802.1Q tagging is technically a clean fit for a raw-L2 sender (the tag is part of the Ethernet header, the switch strips it before the card, so card firmware is unaffected), but it serves one bespoke architecture. - -### Smaller asks from the same thread - -- **Read the wall layout from the ColorLight cards.** The cards can report their configuration and at least one user's own tool already does it; it would remove the manual layout step. -- **Per-card color temperature and brightness**, via the ColorLight sync-packet bytes, grouped by sync group — used to color-match mixed panel batches live. - -### Configure a ColorLight card without Windows (2026-10-16, asked) - -Asked on Discord by robwomp: a way to set up ColorLight receiving cards from a Linux command line, so a ready-made MoonLight image (a Pi running the desktop build with PanelCardDriver) could configure the card itself and make a card and its panels plug and play. - -Today the card's configuration (panel size, panel count, scan, driver IC) is written once with ColorLight's LEDVision, and its firmware with LED Upgrade, both Windows-only ([panel cards §4 and §7](../../reference/hardware/panel-cards.md)). The protocol for those writes is ColorLight's own and undocumented; the pixel and discovery frames are known from [Harald Kubota's write-up](https://hkubota.wordpress.com/2022/01/31/winter-project-colorlight-5a-75b-protocol/), the configuration frames are not. - -The route: capture what LEDVision sends while it writes a known configuration (Wireshark on the card's Ethernet), decode the frames, and replay them. [REA](https://github.com/morluto/rea) (MIT) does the decode step: its packet mode reads the capture through TShark and reports repeated values and field candidates, run from the terminal (`npx rea-agents …`) rather than installed into the agent's configuration. Its static .NET inspection could read how LEDVision builds the frames, if LEDVision is .NET and its license allows it. Then replay them from PanelCardDriver as a "configure card" action taking the wall's panel size, count, scan and IC. Reading the configuration back (the "read the wall layout" ask above) shares the same decoding. The firmware path stays with LED Upgrade: a bad write bricks a card, which a configuration write does not. Ask before building: it needs a card on the bench and a Windows machine running LEDVision for the capture. - -## Sensors and audio-reactive input - -### The sensors an installation needs (2026-09-01) - -**Why this is a commitment rather than a wish list.** One of the intended uses is art installations, -and an installation people can interact with has to sense them: that is stated in -[the architecture](../../explanation/architecture/index.md#the-problem). Sensing is therefore part of the product, not a -convenience, and an input peripheral is first-class alongside an output driver. The scope is still -narrow on purpose: a lighting controller that senses its audience, not a home-automation platform. - -What shipped, and what a piece can already react to: **audio** (AudioService: RMS level and a 16-band -FFT, I2S mic or line-in, or a peer's stream over the network), **IR** (IrService, a learnable -remote), and **a button** (ButtonService on the GPIO seam, which covers foot pedals in momentary -mode). An **IMU** exists on an unmerged branch (GyroDriver, below). - -The sensors worth having next, in the order an installation asks for them: - -- **Motion / presence (PIR)**: the single most common interactive trigger: someone walks up, the - piece wakes. Electrically a digital pin, so `ButtonService` almost covers it; what differs is the - semantic (a level, not a press) and the hold time. MoonLight models it as its own pin role - (`pin_PIR`, "HIGH = lights on, LOW = lights off"), which is the shape to follow. -- **Distance (ultrasonic HC-SR04, or a ToF like the VL53L0X)**: turns presence into a continuous - value: a hand's distance drives brightness, a visitor's approach drives an effect parameter. The - ToF is I2C, which today means SCANNING only: the register-level read and write a sensor needs is - the unmerged GyroDriver prerequisite (see input-mapping-analysis.md and the scripted-sensors - plan), not something that exists. The ultrasonic is a trigger pulse and an echo-width measure, - which needs a timing seam the GPIO one does not yet provide. -- **Touch**: the classic ESP32's capacitive touch pins need no external part, so a conductive - surface becomes an input. The Dig-2-Go's own button is on a touch-capable pin, though it reads - fine as a plain digital switch. -- **Light level (LDR or a BH1750)**: an installation that dims itself to the room, and the obvious - companion to a piece that runs day and night. -- **Rotary encoder**: the physical knob for an installation without a screen, and the one input - that maps onto ControlModule's encoder bank directly. - -**None of these need new architecture**, which is the point of recording them together: each is a -Service under the core `Services` container. How it reaches the rest of the system depends on what -it produces. A THRESHOLD is an event ("closer than 50 cm"), and an event drives a control through -`Scheduler::setControl`, exactly as the button and infrared rows do. A continuous VALUE is not an -event: an effect reading a distance per frame needs the number, and pushing one through a control -per sample would serialize a stream through a settings path. That is published as a shared frame -the way `AudioService::latestFrame()` already does, and a sensor typically does both. What they need is a -platform seam per sensing modality (the GPIO seam shipped; I2C exists; a pulse-timing seam does -not), and a module each. - -### Read a vehicle's CAN bus and drive lights from it (2026-09-11) - -A MoonLight device in a car, reading the vehicle's own bus and driving aftermarket lighting from what it sees: underglow that pulses with engine RPM, sweeps with the indicator, flares on the brake. The vehicle to develop against is a VW Transporter (the product owner's). - -**Read-only, and that is the design, not a limitation.** Writing to a vehicle's bus reaches systems that are mandated safety equipment (indicators and brake lights are UNECE R48), usually on a segment shared with ABS and airbags, and modifying them is illegal in most jurisdictions. Reading has none of that exposure and gets the interesting half anyway: the car reports its state, MoonLight renders it. Anything the device drives is aftermarket lighting on its own output, untouched by the vehicle. - -**A Service, not a driver.** It senses rather than renders, so it belongs under the core `Services` container beside AudioService and ButtonService, and it produces both shapes the sensors entry above describes: an EVENT (indicator on, brake pressed) drives a control through `Scheduler::setControl`, while a continuous VALUE (RPM, speed) is published as a shared frame the way `AudioService::latestFrame()` is. Effects then read it, the input-mapping layer routes it, and a MoonLive script reads it as a system variable, so "underglow sweeps with the indicator" is a script someone writes and shares rather than firmware. - -**Two tiers, and only the first is write-once.** - -| | Standard | What it yields | How | -|---|---|---|---| -| OBD-II / UDS | Yes, legally mandated (EU: 2001 petrol, 2004 diesel) | RPM, speed, coolant, throttle, engine load | Request/response against ID `0x7DF`, so polling, a few times a second | -| Manufacturer body frames | No, proprietary per model and year | Indicators, brakes, doors, lights | Passive sniffing and reverse engineering | - -J1939 is NOT the answer here despite being the "CAN lighting standard" people reach for: it is the heavy-duty protocol (trucks, agricultural, marine), and a Transporter is passenger-car architecture running VW's own frames. J1939 would only apply if MoonLight targeted commercial vehicle equipment. - -So phase 1 is OBD-II only, which genuinely works on any car and needs no per-vehicle data. Phase 2 adds raw-frame sniffing, and there the frame IDs must be CONFIGURATION rather than compiled-in constants: a Transporter owner who discovers theirs shares a config file, and no firmware release is involved. That is what keeps a per-vehicle feature from becoming a per-vehicle maintenance burden. - -**What it needs:** - -- A `platform::twai*` seam (init, send, receive, stop). TWAI is the ESP32's CAN controller and is present on every chip MoonLight ships: 1 controller on classic and S3, 3 on the P4. -- **An external transceiver**: the ESP32 has the controller but no CAN PHY, and this is the only part that needs buying. An SN65HVD230 board (3.3V native, unlike the 5V TJA1050) plus an OBD-II pigtail to reach pins 6 (CAN-H) and 14 (CAN-L), so nothing on the vehicle is cut. Watch for a termination resistor fitted on the transceiver board: the vehicle bus is already terminated at both ends and a third resistor disturbs it. Power the device from USB while developing rather than OBD pin 16, so a crash cannot load the vehicle supply. -- A `VehicleService` module, and a desktop stub so the logic is testable without a car. - -**Phase 1 is already confirmed on the van**: an ELM327 running Torque pulls PIDs from it today, so the OBD port is live, CAN is reachable there, and the standard request/response layer answers. Nothing further needs discovering before building the seam. - -**Phase 2 is the open question, and one free test decides it.** Torque and ELM327 adapters speak the FILTERED request/response layer: you ask for a PID and the ECU replies, which says nothing about what else is on the wire. Torque itself can do the test: it has a dedicated `canbus monitor` protocol (selected in the vehicle profile, beside a hybrid mode that polls OBD2 and monitors at once) and imports DBC files in the PID editor to decode signals out of the stream. Traffic beyond one's own polled PIDs means the body frames are reachable on that port; only one's own requests and replies coming back means they sit on another segment behind a gateway, so phase 2 needs a different tap point. **The constraint is the adapter, not the app**: monitor-all (`AT MA`) is in the genuine ELM327 specification and many clones omit it outright, which makes them fine for standard PIDs and useless for reverse engineering. An ELM327 identifier app probes which AT commands a given adapter actually recognizes, so that is the thing to check first. Worth the five minutes before designing anything for tier 2. - -**Prior art, checked 2026-09-11.** The building blocks are all open source and the closest match is [can_wizard](https://github.com/okhsunrog/can_wizard) (MIT): an ESP32-C3 plus SN65HVD230 sniffer on ESP-IDF's TWAI driver, with hardware acceptance filters, live bus state, error counters and bus-off recovery, which is close to the seam described above. It uses the legacy `driver/twai.h`, deprecated in IDF 6.x, so the new `esp_twai.h` is what to write against. Also useful: [esp32-slcan](https://github.com/mintynet/esp32-slcan) exposes an ESP32 as a standard slcan device so desktop CAN tooling works against it, and [an Alfa Romeo dashboard project](https://github.com/ClaudeMarais/AlfaRomeoGiulia_DashboardInfo_ESP32-S3) runs an ESP32-S3 on two CAN buses at once in a real car. Driving LEDs from OBD2 RPM is well-trodden ground, which is another way of saying tier 1 carries little risk. - -**What is NOT available, and it shapes the design.** [awesome-automotive-can-id](https://github.com/iDoka/awesome-automotive-can-id), the main community collection, carries VW data for the MQB platform, Golf, Polo, Touran and e-Up, but **nothing for the Transporter and no lighting signals for any VW**: its coverage is drivetrain and engine management, not body control. So tier 2 starts from an empty database however much searching precedes it, which is exactly why the frame IDs belong in configuration a user discovers and shares rather than in a compiled-in table. (T6 owner forums do report the reverse signal traveling over CAN, so the body signals exist on a bus; they are simply not publicly documented.) - -**Related:** a convoy light show across several cars needs nothing from this. That is MoonSync (planned, not built) synchronizing timebase and effect state between devices, with each car rendering locally through whatever driver it has. The two compose for free and neither blocks the other. - -### Audio-reactive follow-ups - -The manual level + 16-band FFT spectrum has shipped (AudioService; what landed and why is in [lessons.md](../../work/past/lessons.md)). These are the deferred follow-ups, each its own increment: - -- **Adaptive conditioning** — auto noise-floor / auto-gain / smoothing so the display self-calibrates to a room ("sound off → dark, sound on → vivid") instead of being tuned by hand. A self-calibrating version was prototyped and removed; the manual `floor`/`gain` is the shipped baseline. Reinvent from scratch when wanted, and **tune it in a quiet room** — a noisy environment (a strong, varying low-frequency ambient) is the adversarial case that made the prototype hard to settle. (The per-band floor above is the first piece of this.) -- **Adaptive noise gate** — replace the borrowed `squelch`/`floor`-as-gate with a real noise gate: asymmetric bang-bang timing (open fast, close slow), a relative "detect silence" test (thresholds as factors of a learned floor, not absolute sample counts), keying off the RMS envelope we already compute, GEQ/FFT bands left untouched. A softhack007 concept; analyzed and judged in full (good idea, industry-standard, but tight on the <30ms budget; decompose into steps rather than overhaul) in AudioService.md § Adaptive noise gate. The recommended sequencing: the per-band floor above is step 1 (its complementary frequency-domain half), the relative-threshold-over-RMS is the cheap high-value cherry-pick as step 2, hysteresis/timing step 3, log-domain + soft-gate optional. Eventually retires the manual squelch. -- **Pin auto-scan** — detect the mic's `sdPin` with `wsPin`/`sckPin` fixed (a noise-prompt + confirm convenience); ships today with explicit pin controls. -- **Beat / onset detection** beyond the raw peak; more audio effects (2D / palette-driven frequency-reactive). -- **A second, decimated FFT for the low bands (2026-10-05).** At the default 22.05 kHz a 512-sample block gives 43 Hz bins every 23 ms, so the bands below about 130 Hz own one bin each and two bass tones a semitone apart land in the same band. A longer FFT alone buys resolution at the cost of time for every band (2048 samples: 10.8 Hz, but 93 ms). The textbook multirate answer keeps both: low-pass and decimate by 4 to 5.5 kHz, run a 512-point FFT on that for the bands under about 2.7 kHz (about 11 Hz bins), and keep the 23 ms FFT for the rest. Cost is one more 512-point FFT and a small decimation filter per block. Pinned on the desktop by two synthesized bass tones (50 and 60 Hz) landing in different bands, then measured on an S3 against the Audio module's tick time. -- **Bass, mid and treble energies on the audio frame (2026-10-05).** Which band carries the kick depends on the song, the sample rate and the band edges, so an effect keyed on band 0 misses it whenever it lands in band 1 or 2. Three sums over fixed ranges (20 to 150 Hz, 150 Hz to 2 kHz, above 2 kHz) beside the 16 bands give an effect "the bass" independent of the layout. Three sums per block, and a MoonLive system variable each. - -### GyroDriver → core Service move + AudioService-consistency pass (branched, not merged) - -A working **GyroDriver** (MPU6050 IMU over I²C) exists on an unmerged branch (commit `11f8eb7`, "Add GyroDriver (MPU6050) + generic platform I2C layer"); it is not in this branch's tree. This entry reverse-engineers that commit so the move is tracked now. **Verify against the real implementation when the branch merges, then delete this entry.** - -What the commit contains (reverse-engineered): - -- `src/light/drivers/GyroDriver.h` — reads an MPU6050 over I²C and surfaces five read-only telemetry controls (`gyroX`/`gyroY`/`gyroZ` rates in °/s, `pitch`/`roll` tilt angles). Polls the sensor in `loop20ms()` (50 Hz), formats the display strings in `loop1s()`. WHO_AM_I probe + wake on `setup()`, big-endian 14-byte burst parse, `atan2`-based tilt (no fusion filter). -- A **generic, domain-neutral platform I²C master** (`platform::i2cInit`/`i2cWriteReg`/`i2cReadRegs`, 7-bit addressing) so future sensors reuse it; ESP32 impl on the IDF v6 `i2c_master` driver in a new `platform_esp32_i2c.cpp`, plus an MPU6050-shaped desktop simulation so the UI and host tests see live values without hardware. -- `unit_GyroDriver.cpp` — WHO_AM_I probe, simulated burst parse, control formatting, time-ramp tracking. - -The move: it currently masquerades as an input-only **driver** under the Drivers container (a no-op `setSourceBuffer(Buffer*) override {}` is the tell). It belongs as a **Service** — a user-added child of the `Services` container, exactly like AudioService — both are sensor services that poll hardware and publish read-only telemetry. On the move, make it consistent with AudioService (the established sibling pattern): - -- **Relocate** `src/light/drivers/GyroDriver.h` → `src/core/` and its spec `docs/moonmodules/light/drivers/GyroDriver.md` → `docs/moonmodules/core/`; change `role()` to `Service`; delete the `setSourceBuffer` no-op; rewrite the doc's "input-only driver under the Drivers container" framing. (Rename to `GyroService` too, to match the `AudioService`/`IrService` convention — a Service names itself by its subcategory.) -- **Pin controls + rebuild path.** GyroDriver hardcodes SDA/SCL (`static constexpr` 21/22, with its own "Hardcoded until BoardModule exposes I2C pin mapping" comment). AudioService already shows the pattern: editable `uint16` pin controls + `controlChangeTriggersBuildState` + a `reinit()` on `onBuildState`. Adopting it retires the hardcoded-pins TODO and satisfies the robustness rule (reconfigure in any order). -- **Lifecycle.** GyroDriver has `setup()` only — no `teardown()`. Add teardown for symmetry with AudioService's setup/teardown/reinit (the shared I²C bus has little per-instance state to free, so this is consistency, not a leak fix). -- **Document the cadence difference.** GyroDriver polls in `loop20ms()` (50 Hz is plenty for tilt); AudioService reads in `loop()` every tick because I²S DMA must be drained promptly or it overflows. Both are correct; add a one-line "why this cadence" comment at each so the two siblings aren't "harmonised" into a bug. -- **Add it** to the deviceModel catalog under `parent_id: "Services"` (user-added per board), the same shape as AudioService — a gyro sensor is optional per board, so it's a Service, not a wired-by-code System child. - -Already done on this branch (the reverse direction): AudioService's two live read-outs were switched from `addText`+`setReadOnly` to `addReadOnly` (the display-only type, matching SystemModule and the way GyroDriver already does it correctly) — so the telemetry idiom is consistent before the gyro branch even lands. - -### Sensor input on Raspberry Pi 5 — microphone, IMU, line-in (post-1.0, multi-commit) - -Audio-reactive lighting (and motion-reactive) is core to what WLED-MM / MoonLight are known for. The Pi 5 is the right host for it: it has the CPU and RAM for real FFT-based audio analysis that the Xtensa ESP32 struggles with, and a full Linux audio + I²C stack. None of this exists today — the codebase has no sensor, audio, or IMU concept, and the Pi currently runs the **desktop** platform backend (there is no `src/platform/rpi/`), which has no hardware access. So this is a domain expansion built on a real platform-backend prerequisite, not a small add. - -**Target sensors and their Pi 5 interfaces:** - -- **Microphone** — I²S MEMS mic, or a USB audio device read via ALSA. The high-value one: FFT → frequency bands + beat detection drive audio-reactive effects. -- **Line-in** — the Pi 5 has no native analog input, so this is a USB audio interface / DAC HAT feeding the same audio pipeline as the mic; only the source differs. -- **IMU / gyro** — an I²C device (MPU-6050 / 9250-class) on the Pi's I²C bus; tilt / motion → effect parameters. - -**How it fits the architecture (the load-bearing part):** - -1. **The module category exists — `ModuleRole::Service`.** Services are user-add/deletable children of the `Services` container (a gyro `Service` lands there via the GyroDriver→core move). What's missing for audio-reactive is the *consumption* side: a sensor reads hardware and *produces* values (audio bands, IMU axes) that effects consume — the producer side of the [producer/consumer data-exchange model](../../explanation/architecture/moonmodule.md#data-exchange-between-modules) (a sensor produces an `AudioFrame` / `ImuState` the way effects produce a buffer that drivers consume). Define the producer struct domain-neutrally so it isn't audio-specific. Today's services are display-only; wiring them into effects is the new work. -2. **All hardware access stays behind the platform boundary.** New `platform::` APIs (e.g. `readAudio()` returning PCM/FFT, `readImu()` returning axes) with the ALSA / I²S / I²C implementation in a real `src/platform/rpi/` backend — which is itself the prerequisite that doesn't exist yet (the Pi uses the desktop backend today). No ALSA/I²C include or call outside `src/platform/`. -3. **Effects consume sensor data the same way they read the layer.** An audio-reactive effect reads the current `AudioFrame` (bands/level/beat) the way `PreviewDriver` reads what `Layer` produces — through a plain data structure wired in `main.cpp`, not a direct hardware call. - -**Increments (each a normal domain addition, picked up one at a time):** - -1. A real `src/platform/rpi/` hardware backend (GPIO/I²C/I²S/ALSA) — the prerequisite; until it lands, the Pi runs the desktop backend with no sensors. -2. The producer struct(s) (`AudioFrame` / `ImuState`) + the `platform::read*` APIs. (The `Service` role + Services-container add/delete already exist.) -3. The first audio service — **MicrophoneModule** (canonical, highest value: FFT bands + beat). -4. The first audio-reactive effect(s) consuming it. -5. IMU and line-in slot into the same source-module + platform-API shape afterwards. - -Study the proven audio pipeline in MoonLight / WLED-MM (FFT band layout, AGC, beat detection) to inform our own — reference the approach, don't port their code, per [history](../past/index.md) practice. Specs before code: a `MicrophoneModule.md` (and the source-category contract) get written and reviewed before implementation. - -## Effects and preview - -### DemoReel: extrude hosted lower-D effects (pending) - -DemoReel hosts one effect at a time and drives its `loop()` directly. A hosted **D1/D2** effect only writes its own slice (D1 → the x=0 column; D2 → the z=0 plane), and — unlike a normal Layer child — it does NOT get `Layer::extrude()` applied, so on a 2D/3D grid its output stays on one column/slice instead of spreading. A first fix (call `layer()->extrude(child->dimensions())` after the child's `loop()`, mirroring `Layer::loop`) **crashed the test suite with a heap/vtable smash** — `Layer::extrude` copies within `buffer_` using the Layer's `width_/height_/depth_`, and something in the reel's host path leaves those out of sync with the allocated buffer (root-cause not yet pinned). Redo it carefully: verify the Layer's dims match its buffer at the extrude call, add a bounds guard in `extrude` (or a reel-local extrude that reads the child's real dims/buffer), and pin it with a DemoReel test that hosts a D1 and a D2 effect on a 3D grid and checks the spread. Until then the reel renders D1/D2 hosts on a single slice (visible but not full-grid) — acceptable, not a crash. - -### BlurzEffect — a compounding blur has no rate carry (open) - -`Layer::fadeToBlackBy` takes a rate per reference frame and the Layer scales it once, the pattern every fade uses ([architecture § Where the machinery lives](../../explanation/architecture/index.md)). `draw::blur` does not fit it: blur COMPOUNDS, so applying it twice at half strength is not one blur at full strength, and the fractional carry that makes a fade frame-rate independent produces the wrong result. BlurzEffect is the effect this bites. The open question is what the right construct is — a per-frame blur budget, a single blur at an accumulated strength, or leaving blur explicitly frame-gated and documenting it as not-a-rate. - -### A real 2D/3D PacMan (pending) - -The migrated 1D PacMan and 1D Ant effects were removed — a chase rendered on a single strip reads as a blob of moving dots, not a game, so they weren't worth keeping. A proper **2D (or 3D) PacMan** — an actual maze, Pac-Man and ghosts navigating it, power dots, the blue-ghost flee state — would be a genuinely fun signature effect. Build it fresh as a grid game (not a strip port); the WLED/MoonLight 1D versions are reference for the state machine only, not the rendering. - -### Add real z-axis variation to 2D effects (pending) - -Only **NoiseEffect**, **PlasmaEffect** and **RipplesEffect** have z-aware math. The other honest-D2 effects use `Layer::extrude` to duplicate the z=0 plane, so every z-slice is identical on 3D layers. Candidates for genuine D3 promotion: Metaballs/GlowParticles (add z to blob coordinates), Plasma palette/Spiral (add z-driven phase term), Fire (z-drift heat grid), Rings/LavaLamp/Checkerboard/Particles (add z to each element). Prioritise after seeing real 3D installations; each promoted effect also needs its `dynamicBytes` budget for the full 3D buffer. - -### Self-describing preview frame header (mid term) - -The preview wire format is a private opcode protocol: `0x02` per-frame channels, `0x03` coordinate table, each a hand-rolled byte layout, and the color payload is **always RGB** regardless of the buffer's `channelsPerLight`. Every new data kind (RGBW display, beam direction, …) means inventing another opcode and another fixed layout by hand. The minimal fix that stops that sprawl: a small **typed header** — `[type][format][count][stride]` where `format` enumerates `{RGB, RGBW, …}` — so one message kind carries any per-light channel layout and the browser shader reads `format` to interpret the payload. Do it concrete-first, when RGBW *display* (below) is actually wanted, not speculatively. Prereq for both items below. - -### RGBW preview end-to-end (mid term) - -The light `Buffer` already holds `channelsPerLight = 4` (RGBW), and the device output drivers handle it, but the **preview only ever sends/draws RGB** — the W channel is invisible in the UI. (The full-res fast path no longer penalises a cpl≥3 buffer — see the short-term fix — but it still drops W on the wire.) Once the self-describing header lands, carry the W channel on the wire and render it in the shader (W as a warm-white tint / brightness lift on the disc). Small, but gated on the header so it isn't another bespoke opcode. - -### Fixture model — moving heads, beams (long term) - -Today a "light" is a point at a static coordinate with a color. A **moving head** is a fixture that emits a *beam* in a direction it controls live (pan + tilt), plus color, beam-width, etc. — per-light **vector** state, not just color, and a different draw (a cone/ray, not a disc). The static-positions-`0x03` + color-`0x02` split can't express "this fixture's beam now points here." The industry-standard model is **DMX/GDTF fixtures**: a fixture has a position *and* a set of typed attributes (color, pan, tilt, beam). The preview becomes a fixture renderer (disc for a pixel, cone for a beam); this is also the "make Preview a general-purpose module, not light-specific" goal. A domain-model change (the fixture/attribute model), not just transport. Plan when moving heads are actually on the bench. - -**Sub-item — per-emitter targets, and fixing the Yellow/UV RGB-synthesis placeholder.** `Correction::apply()` today synthesizes every non-RGB emitter (White, WarmWhite, Yellow, UV) from RGB via the one `whiteMode` gate. That conflates two different physics: **White/WarmWhite are broadband illumination** with a real achromatic basis (`min(R,G,B)` is a sound approximation — warm vs cold differ only in phosphor CCT a byte value can't express), but **Yellow/Amber (~590 nm) and UV (~400 nm) are saturated/out-of-gamut emitters with no honest RGB pre-image.** Yellow's `min(R,G)` stand-in reads greener than a true amber die AND fires on far too much (any red+green content — yellows, whites, skin tones — muddies the fixture); UV's blue-excess is an eyeball hack. Amber is a *real, common, targetable* emitter (RGBA / RGBAW PARs ship a dedicated ~590 nm die), so the right model is an **effect that targets amber DIRECTLY** (a typed attribute), not RGB-and-synthesize. The fixture model is where that lands. **Decision to make when built:** whether Yellow/UV should default to **off** (0, like `whiteMode::None`) until an effect drives them — more honest than always firing a wrong approximation — vs. keeping the "light it up to eyeball the wiring" placeholder. **Practical test to run then:** on a real RGBA/RGBAW+UV PAR, compare the synthesized amber against a directly-driven amber die (does `min(R,G)` look acceptably yellow, or muddy?), and confirm the White subtraction under `Accurate` reads correct with all emitters present. Rationale + the current formulas live in `Correction.h`'s emitter-field comment. - -### Mixing light types in one Layouts — open design question (undesigned) - -Today each layout child describes one light type (all LED strips, or all par lights), and the current model is one Layouts container per light type. Whether a single Layouts should hold mixed types (LED strips + par lights together), and how the per-channel layout would reconcile across them, isn't designed. Deferred until a concrete need forces it; it's adjacent to the fixture model above (a real fixture/attribute model may reframe how mixed types are expressed). (Moved from architecture.md § What we leave undesigned; a deferred design decision, not a settled 🚧 one.) - - -## LCD / DMA driver work - -### Drop the i80 WR/DC sacrificial pins — done for MoonI80, open for I80 - -**Shipped for `MoonI80LedDriver` (2026-07-14).** Owning the GPIO matrix is what bought it: the matrix is a routing fabric, so a peripheral signal that is never connected to a pad simply stays inside the peripheral. `dcPin` is **gone entirely** (DC separates command from data bytes for an LCD panel; WS2812 has no such concept, and the peripheral holds it at a constant level), and WR is routed **only** when a '595 needs it as SRCLK — in direct mode WS2812 is self-clocked, nothing reads WR, and the pin stays free for a strand. Same trick frees the *spare data lanes*: they are simply not routed, rather than parked on a "ghost" GPIO. So a direct-mode MoonI80 board spends its GPIOs on strands alone. - -**Still open for `I80LedDriver`, and it cannot be fixed there.** IDF's `esp_lcd` hard-requires both (`esp_lcd_panel_io_i80.c`: `wr_gpio_num >= 0 && dc_gpio_num >= 0`) and rejects an NC data pin, which is *why* it must park spare lanes on a real GPIO. Reclaiming the two pins on that driver means leaving `esp_lcd` — which is precisely what MoonI80 already is. So this is not a change to make to `I80LedDriver`; it is a reason to prefer MoonI80 once it is proven. Parlio (P4) never needed the pins (`clk_out_gpio_num = GPIO_NUM_NC`). - -### Parlio DMA frame buffer → PSRAM (free internal SRAM for big frames) - -For driving **lots of LEDs**, internal SRAM is the scarce resource and the parallel-driver DMA frame buffer is the biggest consumer (8 lanes × lights × outCh × 24 slot-bytes + latch pad). The **i80 driver already allocates PSRAM-first on the LCD_CAM chips** (S3/P4) — `platform_esp32_i80.cpp` tries `MALLOC_CAP_DMA | MALLOC_CAP_SPIRAM` under `#if SOC_LCDCAM_I80_LCD_SUPPORTED`, falling back to internal — which is why the SE16 reaches the full 16384-light frame (see [performance.md § Multi-pin](../../reference/performance.md#multi-pin-led-driving-all-three-peripherals-128128-grid)). The classic-ESP32 I2S i80 backend stays internal-only (its DMA can't reach PSRAM — a hardware limit, not a TODO). **Parlio still allocates internal-only** (`platform_esp32_parlio.cpp`), so a large Parlio frame can exhaust DRAM while PSRAM sits unused; the IDF confirms Parlio's GDMA can burst from PSRAM (`esp_driver_parlio/src/parlio_tx.c` sets `access_ext_mem = true // support transmit PSRAM buffer`). (RMT already does the right thing — its symbol buffer goes through `platform::alloc`, PSRAM-first with an internal fallback.) - -**The change (Parlio only):** allocate the Parlio buffer `MALLOC_CAP_DMA | MALLOC_CAP_SPIRAM` first, falling back to internal when PSRAM is absent/full, using the **external-memory alignment** the IDF requires (`gdma_get_alignment_constraints` → `ext_mem_align`, typically the cache line) and keeping the buffer cache-aligned + its size a multiple of that alignment. **Why its own increment:** it changes the proven hot DMA path, PSRAM DMA has real caveats (cache-line alignment, write-back/coherence on the encode→DMA handoff, and lower PSRAM bandwidth that the IDF guards with a CPU-MAX DFS lock during transmit), and it **must be re-proven on P4 hardware** (the loopback self-test bit-verifies it, then a real strip). It also raises the Parlio ceiling toward the [chunked-transfer](#led-drivers-deferred) goal. Measure the bandwidth headroom too: a very wide, long frame at speed may want internal SRAM regardless. - - -## LED drivers — deferred - -The LED-driver increments **shipped**: increment 1 (RMT/WS2812B single-strand on classic ESP32: [`RmtLedDriver.h`](../../moonmodules/light/drivers.md#rmtled), `RmtSymbol.h`, `platform_esp32_rmt.cpp`) and increment 2 (2a multi-pin RMT, 2b parallel LCD_CAM on the S3: [`LcdLedDriver.h`](../../../src/light/drivers/LcdLedDriver.h) via [`ParallelLedDriver.h`](../../moonmodules/light/drivers.md#parallelled), `platform_esp32_lcd.cpp`), all with host + on-board-loopback tests, hardware-proven. The locked decisions, file-by-file phases, the WiFi-flicker test-rig analysis, and the bench deviations (8-GPIO i80 bus, 2.67 MHz slot clock, SOC-macro gate, real-frame loopback) are in [lessons.md](../../work/past/lessons.md), the [driver docs](../../moonmodules/light/drivers.md#rmtled), and the [analysis docs](../../work/future/leddriver-analysis-top-down.md). What remains here is only the work that has **not** shipped and is tracked nowhere else. - -- **RMT `int_ena` read-modify-write race (classic ESP32, level-5 refill).** `RMT.int_ena` is one register written by both the render task (arming a frame) and the level-5 refill handler (disarming a finished one), through `rmt_ll_enable_interrupt`'s `|=` / `&=`. A handler firing between the task's read and its write loses the task's update, leaving a channel armed or silent. Never observed: the window is a few instructions and the two writes rarely target one channel, so the symptom would be a stuck channel after hours rather than anything the bench shows. Left unfixed deliberately, because the two obvious guards are both wrong here and each was tried on hardware: `portENTER_CRITICAL_ISR` spins on a lock the level-5 handler has just preempted (it runs above `XCHAL_EXCM_LEVEL` 3 by design) and deadlocks the core, and a compare-and-swap builds but crashes, since `S32C1I` addresses only data memory and a peripheral register raises `EXCCAUSE` 3. The remaining candidate is masking to level 5 (`XTOS_SET_INTLEVEL`) around the two-instruction update, which needs no lock and no atomic bus access; it compiles but is unproven on hardware and wants a soak before it displaces firmware that is flicker-free on two boards. - -- **sigrok/fx2lafw cross-check + MoonDeck "LED driver test" Python script** — the independent-clock proof and the run-from-MoonDeck flow ([analysis §5.3](../../work/future/leddriver-analysis-top-down.md)). The on-board RMT-RX loopback (shipped) is the cheap CI correctness gate but a *compromised witness* for WiFi-induced flicker — the RX capture runs on the same ESP32 whose WiFi causes the glitch. The real flicker test is a **sustained capture (seconds) with WiFi associated + a packet flood**, decoding every frame for a byte-slip or reset-gap deviation; it validates the SHIPPED render↔encode split's WiFi isolation (drivers tick on core 1; WiFi lives on core 0). A DSLogic Plus (100 MS/s) upgrade is reactive — only if a flicker reproduces that 24 MS/s can't resolve. -- **Chunked transfer (Step 4) — the 16K lever, and now the ONE mechanism behind three separate ceilings.** Split a frame into transactions the DMA can actually swallow, feeding them back-to-back. It was scoped as a Parlio fix; it is really a **core-path** fix, and the shift-register expander is only its third beneficiary. - - **The three ceilings it lifts, all unshifted-first:** - 1. **P4 Parlio: ~4,096 lights.** The 2026-07-12 16-lane sweep found the single-DMA ceiling at 256/lane × 16, reproduced within 0.3% on a second P4 — and the cause is **not** the 65,535-byte cap (256/lane is far under the 897/lane limit). The P4 has 33 MB free heap but the largest *contiguous internal block* is ~368 KB, and a single-shot 16-bit DMA buffer needs one contiguous block: at 512/lane init fails outright. So chunking is **the only path to the 16×1024 = 16,384 lights the 16-lane widening promised.** This is the headline win and has nothing to do with shift registers. - 2. **Classic ESP32: 2,048 lights.** Its I2S DMA cannot reach PSRAM at all, so the frame must fit internal RAM. *(This entry previously said chunking "would not lift that" — that assumed chunking a PSRAM frame the DMA still had to read. It does not hold for the **staged** form below: if the DMA only ever reads small INTERNAL chunks that the CPU fills from a PSRAM frame, the classic chip is lifted too.)* - 3. **The 74HCT595 expander.** Currently capped at ~96 lights/strand because its ×8 frame only renders correctly from internal RAM ([§ 7.5](../../work/future/shift-register-driver-analysis.md)). An add-on, and explicitly **not** the reason to build this. - - **Two distinct limits, one idea — keep them straight.** For **Parlio** the constraint is *transaction size* (contiguous block + 65,535 bytes), so chunking means smaller transactions. For **i80** there is no single-shot cap at all (it chains DMA descriptors) — there the constraint is *where the DMA reads from*, so the win comes from **staging**: keep the frame in PSRAM, but have the CPU copy it a chunk at a time into small internal-RAM buffers that the DMA reads. Same mechanism, different reason, and conflating the two is what muddled the shift-register investigation. - - **Prior art, and why staging is the shape.** hpwit's drivers never DMA from PSRAM: the pixel data may live there, but an EOF ISR transposes it into small **internal bounce buffers** that the DMA clocks out (`owner_check = false`, a fixed ring, built once). Espressif do the same thing under a different name — the RGB-LCD driver's **bounce buffers**. The one place hpwit *does* DMA straight from PSRAM is the **P4 Parlio** path, and he chunks it (≤64 KB via IDF's parlio driver) — which is consistent with our own finding that the P4 handles a PSRAM whole-frame shift transfer fine while the S3 does not. The staged form gets hpwit's underrun immunity **without leaving `esp_lcd`** (still IDF-maintained, still one code path across classic/S3/P4, no raw-register driver) and **without putting the CPU back on a per-LED ISR deadline** — a bulk sequential PSRAM→internal copy per chunk is what PSRAM is good at, unlike a real-time streaming read. - - **The two mechanisms do DIFFERENT jobs, and the 100 fps target needs both.** The WS2812 wire time is a physical constant — 30 µs per light, serial down each strand — so the *only* lever on frame rate is **lights per strand**, never the chip: - - | strands × lights | total | wire time | fps ceiling | - |---|---|---|---| - | 16 × 1024 (direct, 16 GPIOs) | 16,384 | 30.7 ms | **33 fps** | - | **48 × 256 (6 pins × '595)** | **12,288** | **7.7 ms** | **130 fps** ✅ *(hpwit + PO proved this in practice — StarLight)* | - | **64 × 256 (8 pins × '595)** | **16,384** | **7.7 ms** | **130 fps** ✅ | - - So: **chunking/staging buys LIGHTS (memory); the expander buys FRAMES PER SECOND (short strands).** Neither substitutes for the other, and 12–16K at 100+ fps needs both — the expander's own ~145 KB frame is exactly the one that does not work from PSRAM today, so it *depends* on the staging fix. (A third piece is needed too: at 130 fps the CPU encode must finish in <7.7 ms, and it measures ~24 ms at 16K — that is what the multicore render↔encode split exists for.) - - **The classic ESP32 is a target, not a write-off.** It is routinely dismissed for work like this, and the dismissal is wrong: hpwit and the PO have *run* 48 strands × 256 at ~100 fps on classic silicon (StarLight), with the same '595 expander, while WiFi was up. 240 MHz, two cores, and a 30 µs/light budget is a lot of headroom. The thing that would stop us is **our own encode cost**, which is software we control, not a property of the chip — and the ~24 ms/16K figure quoted in the multicore analysis is (a) measured on a **P4**, not a classic, and (b) **pre-dates the SWAR transpose** that shipped since. Do not carry that number into a classic-ESP32 feasibility argument; measure the real one on the real chip. What the classic genuinely needs is the **staged** form of chunking (its I2S DMA cannot reach PSRAM at all), which this item provides. - - **Build and prove chunking on the unshifted path first** (Parlio 4,096 → 16,384 is the measurable win, on proven code), then let shift mode inherit it — that is a sequencing rule about *where to de-risk the mechanism*, **not** a claim that the expander is optional. It is not: it is the only route to 100 fps at this scale without spending 48+ GPIOs. Correct WS2812 inter-chunk timing is the one hard constraint: the lines must idle LOW for < 300 µs between chunks or the strand latches mid-frame. The driver already rejects an over-limit frame with a loud status. Measured detail: [performance.md § Multi-pin](../../reference/performance.md#multi-pin-led-driving-all-three-peripherals-128128-grid). -- **`rmtWs2812Show` fuller error handling** (deferred from PR #17 / 🐇 CodeRabbit). The shipped path has a finite `rmt_tx_wait_all_done` timeout (1 s) so a wedged DMA can't hang the render tick forever, and a dropped frame self-heals (the driver re-encodes the whole frame next tick). The fuller version — `rmt_transmit` return check, `rmt_tx_stop` to cancel an in-flight transfer on timeout, `show()` returning failure so `loop()` won't reuse `symbols_` mid-transmit — belongs with the **core-1 driver-task** work, since that task owns the buffer lifetime and in-flight state the cancel logic needs. -- **Auto-derived DMA buffer count** (7 / 30 / 75 per [analysis §7.4](../../work/future/leddriver-analysis-top-down.md)), **shift-register expander stubs** ([§7.5](../../work/future/leddriver-analysis-top-down.md)). -- **16 bits at the output: what remains (2026-10-05).** Fine roles and the 16-bit curve table shipped, as [FixtureProfiles](../../moonmodules/light/supporting.md#fixtureprofiles) describes; the 16-bit Layer stays stashed, since it measured +56% frame cost. - - Verify on a WS2816 strip that the dark end of a fade is smooth, with the built-in `GRB 16-bit`. - - TM1814 and TM1914: an inverted line and a settings preamble before the pixels (8 bytes of current, 6 of mode, each followed by its inverse), as NeoPixelBus sends them; a driver protocol, not a profile. - - UCS7604: its RGB or RGBW mode is set by configuration, so it needs its datasheet before a profile. - - WS2916A-RGBW (Worldsemi): 16-bit GRBW with a 5-bit current gain per channel. Worldsemi's ESP32 example sends a 32-bit gain header once per frame, then 64 bits per pixel, MSB first. - - The header holds the G, R, B and W gains, 5 bits each, with the remaining 12 bits set to 1. - - A bit is 1.3 µs (T0H 300 ns, T1H 700 ns), and the reset is longer than 280 µs, past WS2812's. - - Needs a per-frame header in the LED drivers, the gains as controls, and a `GRBW 16-bit` profile; check our reset against 280 µs. - - WS2915 (Worldsemi): five channels, RGB plus two independent whites, 5-bit gain and 16-bit data, 5 to 48 V. Its frame layout is unpublished; the WS2916A's header with a fifth gain is the likely shape, to confirm from the datasheet. - - WS2916B: RGB at 16 bits (48-bit pixels), 3.3 to 5.5 V. - - Sources: [Worldsemi product list](https://www.world-semi.com/) and [Worldsemi's examples](https://github.com/WorldsemiLED/worldsemi-led-examples); the 4 kHz PWM reported for the WS2915 is unconfirmed. - - Temporal dithering on an 8-bit wire, fed by the same 16-bit table. - - Pan and tilt beyond 8 bits, once a layer slot carries motion wider than 8 bits; today their fine byte repeats the coarse one. -- **Stop reading `fixtureRef` after the release that follows 2026-10-05.** A driver saves its profile's name in `fixture` since then, and `DriverBase` still reads the hidden `fixtureRef` an older config saved beside a row number. With the next release out, remove the control and `DriverBase::adoptLegacyName` with its two calls, and add a `migrate.js` entry mapping `fixtureRef` onto `fixture` so an older backup still restores. -- **IR RX live-reconfigure recovery — unconfirmed, park until it recurs** (bench 2026-07-13, SE16). IR reception on the SE16 (`IrService` pin 5) went dead mid-session and only a **hard reset** brought it back; a warm/API path did not. **Ruled out:** not hardware (hard reset fixed it, receiver+switch+wiring fine), not LED-count (IR survives the full 16384-light / 8 fps load — a received code still toggled a control at max load), not a regression from the i80 commit (`platform_esp32_ir.cpp` untouched, the 1250 ns glitch-filter fix intact). **Prime suspect (unproven):** the session's live pin churn — including transiently setting the i80 `clockPin` to **5, which IS the IR pin** — left GPIO 5 routed to the wrong peripheral, and the RMT-RX channel (a pin-keyed static behind `platform::irStop`/`ensureChannel`) didn't re-acquire cleanly on the next `irRead`; only a full GPIO re-init (hard reset) cleared it. This may be pure test artifact (nothing in a *normal* user flow points two live modules at GPIO 5). **To conclude:** from a fresh hard reset (IR working), in isolation set i80 `clockPin=5` then restore `clockPin=8` and check whether IR dies and whether it self-recovers *without* a hard reset — self-recovers → no bug (test artifact); stays dead → a real live-reconfigure gap in the IR channel re-acquire worth fixing (per *No reboot to apply a configuration change*). Small robustness/repro work; do it only if IR breaks again in real use. -- **Moving-head preview = peer interpreter.** When moving heads land, the previewer must interpret channel semantics (pan/tilt/RGBW-at-arbitrary-indices) to render a moving fixture: the same fixture-profile model physical drivers use, interpreted to screen. This is *why* the increments named the abstraction "interpret the profile" rather than "apply correction / opt out": so Preview becomes a full peer here without a rename. Its own design plan when moving-head support starts. -- **Sparse fixture-profile editor.** A FixtureProfiles row currently shows one role Select per channel across the whole `channels` width: including the unmapped `, ` gaps a wide moving head has between its functions. For a fixture you usually only care about the few channels you drive (rgb, pan, tilt). The refinement: show only the *mapped* channels + an "add channel" affordance (pick a role → fills the first gap or grows the fixture), over the unchanged dense `roles[]` storage. A first attempt shipped and was reverted for edit bugs; redo it cleanly (the dense editor is the reliable interim). Prior art: GDTF / QLC+ fixture profiles (a fixture is a sparse `{channel → function}` map, not a dense per-channel array). - -- **`worley` cellular noise, when an effect needs it** (deferred 2026-08-07, PO). The one power function the plan names that stayed unbuilt on purpose. Worley (cellular / Voronoi) noise measures the distance to the nearest of a set of scattered feature points, which is what produces the look nothing else does — cracked mud, scales, stained glass, a caustic. The fields we have cannot fake it: `fbm` and `warp` are smooth by construction, so their creases never form cells. - - **Why it waits.** An industry-standardness audit (Fable, 2026-08-07) put it explicitly on the "do NOT add yet" list: a practitioner does not consider it table stakes, and every other field function in the library earned its place by having a caller. Building it now would make it the only entry whose justification is "the canon has one". The trigger to build it is an effect that wants cells — at which point it lands with that effect, the way `hashInt` landed with Dissolve and `sampleWrap` with Echo. - - **What it costs when it comes:** the standard cheap form is a 3×3 neighborhood scan around the sample's cell, hashing each neighbor's feature point and keeping the nearest distance — nine `hashInt` calls and nine distance tests per pixel, so meaningfully dearer than one noise sample. `dist16` and `hashInt` already exist, so the function itself is short; the cost question is what it does to a per-pixel budget on a large fixture. - -- **16-bit noise: tuning still open** (2026-08-08). The tier ships (`inoise16` 1/2/3D, `fbm16`), and two defects found by review are fixed: `lerp16`'s product is 64-bit (the 32-bit form was signed overflow, undefined behavior, on roughly a quarter of samples), and `fbm16` sums its octaves at full width instead of shifting each down by 8 — which had made the output 8-bit wearing a 16-bit type (195 distinct values over 20,000 samples; now 15,118). - - **What is still open.** It is VALUE noise, like the 8-bit tier: cell corners are hashed and interpolated. Gradient (Perlin) noise hashes a gradient per corner and takes a dot product, which removes the faint axis-aligned grid value noise leaves — visible on a large smooth field, which is the case this tier exists for. Also unbuilt: a 3D `fbm16`, and `warp16` / `turbulence16` to match the 8-bit compositions. The trigger is an effect that wants a large smooth field; it lands with that effect and its measurements, the way the 8-bit compositions landed with theirs. - -- **Clear the grid on an effect's FIRST frame** (2026-08-08). Fourteen effects still show the previous effect's picture on the frame right after a switch: BouncingBalls, Fireworks, FreqMatrix, FreqSaws, GEQ, GEQ3D, GameOfLife, Lissajous, NoiseMeter, PaintBrush, Random and three more. Most predate the power-function branch. - - **What the user sees.** Switching to one of these leaves the old frame underneath for a moment — an audio effect with no signal yet, or a simulation still seeding, paints nothing and inherits whatever was there. `Layer::tick` deliberately does not clear (the documented break: an effect may fade its own last frame for trails), so owning the background is each effect's job. - - **Why it waits.** It is fourteen effects' worth of change across audio-reactive and simulation families, each needing its own judgement about whether to clear, fade, or seed differently — not a mechanical sweep. `unit_Effects_gridsweep.cpp` already measures it (`afterFirst`) and asserts only the settled frame, so the number is visible without blocking. - -- **A scripted modifier that animates every frame** (2026-10-11): `MoonLiveModifier` compiles a `modifyLive()` entry, so a script moves lights per frame with `t` (scroll, wobble, sweep) as the compiled Rotate does. Geometry only: dimming is a blend mode or an effect's `mulRGB`, per [the layer compositing plan](../present/Plan-20261011%20-%20Layer%20compositing%2C%20masks%20and%20transitions%20(2%20of%203).md). Built once a script needs it; the cost to measure is one compiled-script call per light per frame, on an S3 at its largest grid. -- **A scripted modifier needs a way to drop a light** (2026-08-10). A coordinate slot is a byte, so a script that computes past 255 wraps: `shift.mlv` with a large `amount` lands lights back at the left edge instead of walking them off it. The Layer already drops an out-of-bounds position, but a script has no way to SAY out-of-bounds — every value it can write is a valid coordinate. Needs a sentinel the binding recognizes (or a wider coordinate slot), at which point the "walks off the edge" behavior a scroll modifier wants becomes expressible. - -- **A scripted layout loses its control values across a reboot** (2026-08-11). Persistence SAVES them correctly (`/.config/Layouts.json` holds `"0.width":64`), but on boot they are loaded into a control set that does not exist yet: a script's controls are created by `defineControls`, which can only publish what the ENGINE declared, and the engine does not compile until `prepare()` — Scheduler phase 4, after the phase-2b re-bind. So the loaded value has nowhere to land and the script's declared default wins; a saved 64×64 grid comes back 16×16. Verified on the P4 bench. - - Compiling inside `defineControls` is NOT the fix (tried): it makes the default script's controls exist before `setSource` runs, and swapping the source then re-seeds every control from its new declared default — the same value-loss, moved. The real fix is ordering: the engine must compile once the persisted `source` is in place but before controls are published, which is a Scheduler-phase question (the same parent-before-child ordering the `const_cast` in `MoonLiveLayout::compile` already works around). Affects all three MoonLive bindings, not just the layout. - -- **A scripted modifier that reshapes the grid** (2026-08-10). `ModifierBase::modifyLogicalSize` lets a modifier change the logical `width`/`height`/`depth` — a Multiply kaleidoscope grows the grid, a crop shrinks it — and a compiled modifier uses it. A SCRIPTED one cannot: system variables are read-only, so `MoonLiveModifier` writes the box in and never reads it back. Needs a writable system variable — the binding reads the slots after the script returns and reports the result through `modifyLogicalSize` — which is a new `SysVarKind` (or a mutable flag on `SysVar`) plus the read-back, not a new builtin. Until then a scripted modifier can fold coordinates but not resize the grid they live in. - -- **The compile-failure latch is not provable on the host** (2026-08-18). `MoonLiveScript::sync` - refuses to re-attempt a script that failed until its (name, content) changes. The latch exists for - a device-only reason: each attempt is two LittleFS reads (~5 ms on an S3), a layout is asked from - `lightCount()`/`placeLights()` as well as `prepare()`, and the pipeline asks repeatedly while - sizing a fixture, so the retries starve the render task until the watchdog resets the device. - - On the host a re-read costs microseconds and nothing observable differs. Four test shapes were - tried and each still passed with the latch REMOVED ENTIRELY, so none was kept: what survives pins - only that a script fixed in place compiles without a rename, which is control-checked. Closing - this needs either a counting seam (a compile counter the test can read) or a platform fake whose - reads are observable. Until then the latch is protected by its comment and by hardware, not by a - test. - -- **Catch device-backend operand defects on the host** (2026-08-18). Two array-codegen bugs shipped - to an S3 while all 1313 host tests stayed green, and both were control-checked: reintroducing - either one leaves the suite fully passing. `IrInst::c`/`d` are VREG fields the spill pass - renumbers, so a width parked there becomes a register number; and `sourcesOf` writes its sources - back POSITIONALLY, so reporting `kArg4` first shifts every real operand one place along. arm64's - register map absorbs both, which is exactly why the suite cannot see them. - - Three test shapes were tried and deleted for failing their control run: emitted-bytes difference - tests (wrong bytes still differ from other wrong bytes) and a register-liveness walk (the index - satisfies it whatever happens to the value). What DOES work is asserting an assembler primitive - directly, as `unit_moonlive_codegen_xtensa.cpp` now does for `addImm`. The general form is - probably an IR-level invariant check rather than a bytes-level one: assert that no op reports a - fixed ABI vreg among its positional sources, and that non-register operands never occupy c/d. - That is a property of the IR the host CAN evaluate, unlike the emitted code. - -- **Size the MoonLive control arena to the script** (2026-08-18). `kCtrlBytes` is a fixed 64 bytes - per engine (three engines per pipeline), so a script declaring one byte pays for 64 and one - wanting a 128-light array is refused. The arena is already `platform::alloc`'d, so the constant is - habit rather than necessity, and the member byte count is known at compile time. - - What blocks it: the system variables sit ABOVE the script region at compile-time constant offsets - (`kSysWidth = kCtrlBytes + 0`), baked into emitted code as `LoadCtrl` immediates and cached as - slot POINTERS by the bindings. A script-sized region moves every one of them. Closing it means - putting the system variables BELOW the script region so their addresses stop depending on it, - which touches the sysvar table, the bindings and every emitted immediate. The hard ceiling stays - 255 either way, since an arena offset is a `uint8_t` in the record, in `controlSlot` and in the - instruction. Until then, raising the constant is one edit and the failure is a clear compile - error naming the arena, so hitting it is visible rather than silent. - -- **Drain MoonLive's `print()` through a queue** (2026-08-09). `print(v)` writes to serial directly, and an EFFECT script runs on the render tick — so a print inside one blocks the frame for as long as the UART takes. The burst cap bounds it (a handful of writes per compile, then a compare and a return), but bounded is not free, and `tick()` is annotated `MM_NONBLOCKING`. - - **What it costs when it comes:** a small preallocated record queue the built-in writes into, drained from a housekeeping path through the existing platform output seam. The budget and the burst-spent message stay as they are; only where the bytes are written moves. Worth doing when a script is left with a print in it on a real fixture, which is the case the cap exists for. - -- **A bus pin that belongs to another peripheral is accepted, and takes the board off the - network** (2026-09-06). ParallelLedDriver's classic-ESP32 WR/DC defaults are 18/23, which are - IDF's Ethernet MDIO/MDC defaults on the same chip, so an Olimex ESP32-Gateway that added the - driver lost its Ethernet link within seconds and looked crashed (the firmware kept ticking on - serial; it stayed reachable only through its WiFi fallback). A DC pin of 5, the Gateway's PHY - reset line, did the same. The driver's own comment claims 18/23 are "unused by the catalog's - boards", which is false for every RMII board. Two fixes, both wanted: (1) classic defaults that - collide with nothing common (the RMII set 0/16-19/21-23/25-27, flash 6-11, straps 0/2/12/15, - and the Dig-Next-2's relays 5/20-22 and I2C 14/15 all excluded); (2) the pin registry already - detects a double claim (`PinsModule::flagConflicts`) but only colors the edge, so a control - write that lands on a GPIO another control owns should be refused with a status naming the - owner, the way a reserved flash pin already is. A collision with a network pin is worse than - most: it ends the session that could have fixed it, and the SMI pins stay re-muxed until a - reboot even after the driver releases them. - -- **Bench-verified classic parallel setups, not yet in the catalog** (2026-09-06). Every classic - board in `deviceModels.json` ships `RmtLedDriver`, so none carries a `ParallelLedDriver` entry, - and the driver's defaults (`clockPin` unset, `dcPin` 33) work on a classic board without one. - Two setups are proven on hardware and worth recording before they are lost: the **Olimex - ESP32-Gateway** drives 64 lights on GPIO 16 with `clockPin` 32 / `dcPin` 4, and its free pins are - scarce enough that a catalog entry should pin them explicitly rather than inherit a default (its - Ethernet PHY holds 18/23 as MDIO/MDC and 5 as reset, all of which the old defaults collided - with); the **QuinLED Dig-Next-2** drives 256 lights on GPIO 2 with `clockPin` unset / `dcPin` 33. - Add them when a classic board actually ships parallel output as its default, rather than - speculatively across the other 16 classic boards, none of which has been tested this way. - -- **Classic-ESP32 I2S instance split: LEDs on 1, audio on 0** (2026-09-06, SHIPPED, kept as the - rationale). The classic ESP32's parallel LED bus IS an I2S peripheral, so it contends with the - audio input, and the 2026-09-03 "hangs in `esp_lcd_new_i80_bus`" entry is closed: that was a pin - fault (the ESP32-PICO-V3-02 has no GPIO 18/23, which were the WR/DC defaults), not contention. - The split is fixed in silicon rather than chosen: instance 0 alone carries the PDM converters - (`I2S_LL_PDM2PCM_SUPPORTED_PORT_MASK` is `1U << 0`), and NOTHING on this chip requires instance - 1, so the LED bus is the one consumer that can always yield. It therefore takes 1 unconditionally - and audio takes 0, which removes the boot race entirely, leaves 0 for every audio source (PDM, - standard I2S, line-in ADC, codec), and costs nothing. `esp_lcd` picks the first FREE instance - rather than taking one by number, so 1 is claimed by holding 0 across bus creation. For contrast, - hpwit's I2SClocklessLedDriver hard-codes `I2S_DEVICE 0` and would take the PDM instance instead. - Both sides also retry once a second while they want a busy instance, so the loser of any - contention recovers without the user touching a control. - -- **Bench-verified classic parallel setups, not yet in the catalog** (2026-09-06). Every classic - board in `deviceModels.json` ships `RmtLedDriver`, so none carries a `ParallelLedDriver` entry, - and the driver's defaults (`clockPin` unset, `dcPin` 33) work on a classic board without one. - Two setups are proven on hardware and worth recording before they are lost: the **Olimex - ESP32-Gateway** drives 64 lights on GPIO 16 with `clockPin` 32 / `dcPin` 4, and its free pins are - scarce enough that a catalog entry should pin them explicitly rather than inherit a default (its - Ethernet PHY holds 18/23 as MDIO/MDC and 5 as reset, all of which the old defaults collided - with); the **QuinLED Dig-Next-2** drives 256 lights on GPIO 2 with `clockPin` unset / `dcPin` 33. - Add them when a classic board actually ships parallel output as its default, rather than - speculatively across the other 16 classic boards, none of which has been tested this way. - -- **Classic-ESP32 parallel LEDs and a PDM microphone are exclusive** (2026-09-06). The - 2026-09-03 "hangs in `esp_lcd_new_i80_bus`" entry is closed: the QuinLED Dig-Next-2 carries an - ESP32-PICO-V3-02, whose package has no GPIO 18/23 (its pads serve the in-package flash and PSRAM), - and the classic WR/DC defaults were exactly 18/23. The driver now refuses a pin the package lacks - and defaults both lines to unset (sunk onto input-only pads). What remains: IDF's LCD mode exists - on I2S0 only, and `esp_lcd` falls through to I2S1 when I2S0 is taken, which wedges the chip the - same silent way. A PDM microphone is also I2S0-only in hardware, so the platform refuses the bus - with a named status when I2S0 is held. Two follow-ups: report the I2S1 fallthrough upstream (it - should return an error), and note that the raw-I2S classic driver above (the ring, on I2S1 as - hpwit's driver runs) is what lets the two coexist, on top of lifting the 2048-light cap. A - standard I2S microphone (INMP441) is content on I2S1 and coexists today when the LED bus claims - I2S0 first. - -## FixedPoint fades outside the Layer's aggregation (2026-09-07) - -`FixedPointEffect` calls `draw::fade` directly where every other fading effect calls -`layer()->fadeToBlackBy`, so it skips what the Layer adds: the per-frame aggregation (N effects on -one layer cost one buffer pass, gentlest rate wins), the framerate scaling, the sub-unit carry, and -the buffer-generation bump. Invisible until a second effect shares the layer. - -The swap is one line and was tried: it makes the effect **3.1x brighter at high framerate than at -low**, against the 1.35x band `unit_Effects_framerate` enforces. The two calls mean different -things. `draw::fade(cv, n)` applies n every frame; `fadeToBlackBy(n)` is a RATE per reference frame -that Layer scales by elapsed time. The `fade` control (default 70, range 0..255) was tuned against -the first meaning, so moving it needs the default re-tuned and the trail looked at on a device, not -a silent swap. - -Worth doing, as its own change: it is the last effect outside the shared fade path, and while it -stays outside, "every fade goes through the Layer" is not true. - -## Downloading a scripted palette can repoint the active one (2026-09-06) - -Found while testing the palette-download fixes. A `.mlp` that sorts BEFORE an already-installed one -silently changes what the current selection points at: with `fire.mlp` active at index 60, -downloading `beat-flash.mlp` re-sorts the scripted tail and index 60 becomes `beat-flash.mlp`. The -stored value never moved; what it means did. - -[Palette.h](../../src/light/util/Palette.h) already reasons about exactly this and solves half of it. A -palette selection is an index: it persists, it rides `seg[0].pal` over the WLED API, and Home -Assistant renders `paletteNames` positionally, so scripted palettes sort AFTER the built-ins and the -sixty built-in indices are fixed forever. What is not solved is the scripted tail among itself, and -that tail now grows at runtime, which is what the download path made reachable. - -Nobody has reported it, and the blast radius is small: a rig with one scripted palette cannot hit it, -and the value re-reads correctly the moment the user picks again. It matters most where a preset or -an MQTT/HA automation stores the index and replays it later, which is the case where nobody is -watching the LEDs when it changes. - -**What it would take:** persist the scripted palette by NAME alongside the index and re-resolve the -index on load, so a stored selection survives a re-sort. `paletteScript` already holds the resolved -file name for the editor, so the value exists; what is missing is using it as the authority when the -list changes. The alternative, appending new scripts rather than sorting them, keeps indices stable -but makes the picker unreadable as the list grows, which is the trade the sort was chosen over. - -## Classic-board memory: the wins are system-level, not per-module (2026-09-09) - -The Dig-Octa on a clean boot with RmtLed has **88,284 bytes free internal, 86,016 largest block**. -Under load and after driver swaps that largest block fell to 24,576, so fragmentation matters as -much as the total. - -What the modules hold is NOT where the RAM went. The whole tree reports **9,342 bytes** of -`dynamicBytes` (Layer 3,586, Preview 2,560, RmtLed 1,536, Drivers 1,536). So per-module trimming has -almost nothing left to win, and the remaining ~230 KB of internal RAM in use is WiFi, lwIP, FreeRTOS -task stacks and the binary's static data. - -Where to look, in order of likely return: - -- **Task stack sizes.** Every task allocates its stack from internal RAM at creation, and the - defaults are generous. The Tasks module already reports them, so the measurement exists. -- **lwIP pool sizes** (`CONFIG_LWIP_*`): TCP PCBs, pbuf counts, and the send/receive windows are all - sdkconfig knobs, sized for a general-purpose device rather than a controller with a handful of - connections. -- **WiFi buffer counts** (`CONFIG_ESP32_WIFI_*_BUFFER_NUM`): the static RX/TX buffer pools are the - single largest tunable block on a classic board. -- **Fragmentation, not just totals.** A 24 KB largest block with 50 KB free is a placement problem; - allocating the long-lived buffers early and together is what fixes that. - -Each is an sdkconfig change measurable on the bench in minutes (`freeInternal` and `maxBlock` on a -clean boot), and each risks a different failure: too few WiFi buffers drops packets under load, too -small a stack overflows under a rare path. So measure one at a time on a board that is actually -serving the UI, not idle. The payoff is real: at 88 KB free a classic board is one large allocation -away from trouble, which is what drove both driver decisions on 2026-09-09. - - -## HUB75, native (2026-09-16) - -The [Hub75Driver](../../moonmodules/light/drivers.md#hub75) drives a panel from the board's own pins, as against [PanelCardDriver](../../moonmodules/light/drivers.md#panelcard) which sends ColorLight frames to a receiving card. It has run on one panel: a 64x64 1/32-scan panel on a MoonHub75, LCD_CAM at 1,508 Hz at 6-bit, on 2026-10-06 (the bench section of the HUB75 plan). WLED-MM does not implement HUB75 itself; it wraps [mrcodetastic/ESP32-HUB75-MatrixPanel-DMA](https://github.com/mrcodetastic/ESP32-HUB75-MatrixPanel-DMA) v3.0.12 in about 600 lines (`wled00/bus_manager.cpp:677`), so the items here are measured against a library with years of hardware debugging behind it. - -### The shift-register driver init - -A HUB75 "panel" is a family, and the differences are not discoverable from its dimensions. FM6124 and ICN2038S panels need a different init sequence; WLED-MM picks FM6124 for "outdoor" panels, and its comment says the symptom of the wrong choice is a dark panel or pastel colors. Build trigger: a panel showing that. The clock edge is already a control (`clockEdge`), and latch blanking was tried on the bench and changed nothing. - -### Repaint only what changed - -`tick()` corrects and encodes the entire frame every time, whatever moved. The library tracks a dirty bit per pixel in `setPixelColor` and repaints only those in `show()`. For a full-frame effect the two are comparable; for a sparse overlay, a clock, or a mostly-static scene, the difference is the whole encode. - -Worth measuring before building: our encode is a tight loop over a packed buffer, and a dirty-bit test per pixel is not free either. The win is real only where the frame is mostly unchanged. - -### Depth that degrades instead of failing - -`bitDepth` is a user control with a hard ceiling, so asking for more than the wall can hold fails init with a memory error. WLED-MM reduces depth automatically by pixel count (8 bits under `MAX_PIXELS_8BIT`, then 6, 4, 3), so a large wall gets a dimmer picture rather than no picture. - -That fits the allocate-and-degrade rule every other output seam here follows. It interacts with the weighting item below, since the library's `lsbMsbTransitionBit` is the same trade expressed as refresh rather than as memory, so build them together rather than adding two overlapping automatic reductions. - -### Four-scan panels whose physical layout is not their logical one - -Some panels scan in a pattern where the physical row order does not match the logical image, and driving them without remapping produces a scrambled picture that looks like a wiring fault. The library handles this with `VirtualMatrixPanel` and `setPhysicalPanelScanRate` (FOUR_SCAN_32PX_HIGH, FOUR_SCAN_64PX_HIGH), and WLED-MM exposes them as bus types 105 to 108. - -Our `Hub75Geometry::rowsPerScan()` handles a panel driving more than two rows per address step, which is the common multi-pair case, but not an arbitrary remap. Build trigger: someone owns such a panel. The remap belongs in the Layout rather than the driver, since it describes where lights physically are, which is what a Layout is for. - -### Repeated planes: full top brightness and 8-bit depth - -The planes are weighted by their lit window (since 2026-10-06): plane p lights 2^p as long as plane 0 inside one pass, and brightness shortens every window. That costs top brightness, since the lower planes are dark for most of their pass: full white is about 47% of the row time at 4-bit and 33% at 6-bit. It also caps depth at 6, where plane 0 of a 64-column row still lights 2 words. The reference library repeats the upper planes instead, and windows only the lowest (`lsbMsbTransitionBit`, chosen automatically for a refresh floor): about 95% top brightness and 8-bit depth, at roughly 290 Hz for one 64x64 panel. - -**The approach: repeat DMA descriptors, not data.** Plane `p` is stored once and the descriptor chain points at it 2^p times, so the weighting costs descriptors rather than frame memory. mrcodetastic's ESP32-HUB75-MatrixPanel-DMA does exactly this (`configureDMA`, the `for (int k = 0; k < (1 << (i - lsbMsbTransitionBit - 1)); k++)` loop), and it carries a refinement worth taking with it: `lsbMsbTransitionBit` is a dial, not a constant. Planes below it show once each; planes above get full weighting. The library raises it until a configured minimum refresh is met, trading exactness at the low end for rate. That makes depth and refresh one control instead of two fighting ones. - -**What it costs here: a new LCD_CAM backend.** The blocker is not the weighting loop, it is that `esp_lcd_panel_io_tx_color` owns its descriptors and gives no way to aim several at one buffer. The library programs the silicon directly instead (`gdma_new_channel`, `gdma_connect(GDMA_TRIG_PERIPH_LCD)`, hand-linked `dma_descriptor_t.next`). So this is the same step this project already took for WS2812, from `platform_esp32_i80.cpp` (675 lines, esp_lcd) to `platform_esp32_moon_i80.cpp` (1,952 lines, own GDMA), with the same hazards that took bench bisects to find there: re-mounting the chain per frame, terminator placement, producer/consumer headroom. - -**Parlio cannot express it.** Its only repeat primitive is `loop_transmission`, which repeats the whole buffer forever with no per-plane control. Weighted depth would be an LCD_CAM capability, and the `peripheral` select has to say so rather than offering a choice that silently changes the picture. - -Build trigger: a wall that reads too dim at 6-bit, or a user who needs 8-bit gradients. It stays automatic, chosen from `bitDepth` and a refresh floor, with no new setting. - diff --git a/docs/work/future/backlog-mixed.md b/docs/work/future/backlog-mixed.md deleted file mode 100644 index 7f3acb12e..000000000 --- a/docs/work/future/backlog-mixed.md +++ /dev/null @@ -1,80 +0,0 @@ -# Backlog: mixed (core + light) - -Forward-looking items whose work genuinely spans **both** the core and light domains: a core mechanism interacting with a light driver/effect/modifier, where assigning it to one side would misrepresent it. Core-only items are in [backlog-core.md](backlog-core.md), light-only in [backlog-light.md](backlog-light.md). Index + overview: [README.md](index.md). - -## Cross-domain - -### Switching a MoonLive script from the card fails intermittently (2026-09-29) - -Picking a second script for a `MoonLiveEffect` leaves the module running the first one, with nothing reported: the card shows the new name, the effect keeps rendering the old one. -Measured over six consecutive picks on the desktop: two switched, four did not, and the fourth failure recovered on its own. - -**What is established.** The file is on the device and complete (`noise.mle`, 560 bytes, `/.moonlive`). -The first pick after the card opens usually works, and later ones fail in runs rather than randomly. -The picker dialog opens on a failing attempt, so the failure is inside row selection rather than in reaching the widget. -No alert fires, which is why the UI looks like it simply did nothing. - -**The strongest lead.** An editor pane is present after the first pick, and the change handler does `await editor.save()` and refuses to switch while the pane is still dirty. -That path is unproven: the next step is to instrument the handler and read `editor.isDirty()` on a failing pick. - -**Ruled out.** The duplicated download logic in the dropdown, which called `mlDownloadScript` directly instead of the shared `mlEnsureLocal`. -That was a real defect and is fixed, pinned by `test/js/ui-script-picker-download.test.mjs`, and it did not change this symptom. - -**Worth deciding alongside the fix**: whether the script control should be editable at all, or whether a scripted module's script is chosen once through the module picker. -The module picker path works reliably, so a read-only control would remove the failing surface rather than repair it. - - -### LightsControl: not building it (decided 2026-09-01) - -The idea was one module owning global light state (master on/off, brightness, palette, bpm) and -acting as the integration point for external controllers, modelled on MoonLight's -`ModuleLightsControl`. **It is not being built**, and the two reasons are worth keeping: - -**The generic ControlModule is already the hub.** It carries the surface (switches, encoders, -faders, pads) that every transport writes: OSC, MQTT, the WLED bridge, the web UI, and now the -physical inputs. A second hub beside it would be the split brain the OSC plan forbids. - -**A global light param belongs to the module that uses it.** `palette` lives on `Drivers` because -that is where it is consumed (effects read it through `Palettes::active()`), and the same holds for -`brightness` and `on`. Moving them into a hub separates a value from its consumer and buys nothing: -encapsulation, not centralisation. - -So the surface reaches INTO those modules rather than owning their state, which is already how it -works: `fader1` targets `Drivers.brightness`, `switch1` targets `Drivers.on`, and `encoder1` targets -`Drivers.palette`. Those bindings are hardcoded today and become user-assignable as a UI plus -persistence job (see [power-functions-analysis-top-down.md](power-functions-analysis-top-down.md), -per-control assignment). - -**What an encoder bound to a select needs:** the option NAME under the knob, not the index, so -turning through palettes reads "Rainbow" rather than "37". One rule in the generic UI (an encoder -bound to a select renders its target's option label) rather than per-module code, which is how the -rest of the UI already works. A `uint8_t` encoder covers 256 options, comfortably past the palette -count, and whether it wraps or clamps at the ends follows the bound control's type: a palette ring -wraps, a brightness does not. - -### Light presets: save / load / loop a whole effect configuration - -MoonLight's preset system (part of `ModuleLightsControl`): **64 named slots**, each capturing the *complete* effect-tree configuration (all module control values), with save / load / delete, **preset looping** on a timer (rotate between a first/last slot), and Home-Assistant registration for external recall. The product owner has flagged this as **definitely needed**. - -**Partly shipped since this was written.** `ControlModule` owns the preset system: a pad grid of slots, save and apply, and `capture` controls that choose which subtrees a save includes. So the home question is settled (the surface, not a hub), and what remains from the description above is **preset looping** on a timer, rotating between a first and last slot, plus Home Assistant registration for external recall. Persistence already round-trips control values, so the mechanism is there; the loop timer is the missing piece. Complements the [presets UI note](backlog-core.md) (control-value bundles). - -### MultiplyModifier mapping-LUT memory at large grids (investigation, re-verify on classic) - -`scenario_perf_full` on the S3 (2026-06-17) measured the MultiplyModifier's cost across grid sizes. The finding, stated correctly: the modifier **reduces compute** (with the default 2×2 kaleidoscope the effect renders only the ¼-size logical quadrant: Noise+Multiply at 16K is 29,647µs vs 50,555µs for Noise alone), and its real cost is **memory**: the mapping LUT's destinations array. Measured modifier heap cost on the S3: 16²→1.7KB, 32²→10.8KB, 64²→23.5KB, **128²(16K)→93KB** (`nrOfLightsType` is `uint32_t` on a PSRAM board). On the S3's 8MB PSRAM this is trivial. Under the physical→logical fold build each physical light contributes ≤1 destination, so the destinations array is bounded by the real light count regardless of chain depth, so there is no build-time fan-out. - -**This is NOT a no-PSRAM blocker:** 16K Noise + Multiply has run on a classic ESP32 (no PSRAM, 320KB internal) before at **10–20 FPS** (WiFi vs Ethernet), sending frames out over **ArtNet to a display, not physical LED drivers**. It works there because classic's `nrOfLightsType` is `uint16_t` (half the LUT size) and the modifier shrinks the logical render grid. So the action is **re-verify the working classic setup when a classic board is connected** (find the config, its grid, mirror and ArtNet target, that reproduces the historical 10–20 FPS), not "fix an impossibility." Worth investigating only if that re-verification shows the LUT memory has regressed since: the destinations array is the obvious lever (it stores a `nrOfLightsType` per physical destination; a 2× kaleidoscope is 1:1 in *count* so the LUT need not store fan-out > the physical count: confirm it isn't over-allocating to `maxMultiplier()` when the effective fan-out is 1). Capture the classic numbers into performance.md's multi-board table first. - -### NoiseEffect simplex cost on ESP32 (investigation) - -With mirror XY at 128×128, NoiseEffect renders the 64×64 logical quadrant in **~11 ms/tick** on the Olimex (measured), as the simplex math dominates, since the Xtensa LX6 has no FPU and float math is software-emulated. (RainbowEffect on the same pipeline is much cheaper.) This is correct, non-degraded behavior; it's only worth revisiting if a deployment needs Noise faster than ~11 ms at this grid. - -Worth investigating if so: - -- **Q16 fixed-point simplex** instead of float (kills the software-float emulation cost). -- **Lower-precision hash:** current simplex uses a 256-entry permutation lookup; a smaller / SIMD-friendly hash may be faster on Xtensa. -- **Strided sampling + interpolation:** render at 32×32, bilinear up to 64×64. Visual quality cost; needs A/B comparison. -- **Inline / unroll the inner per-pixel loop** to keep the simplex state in registers. - -None of these are obviously free, and a fixed-point port may shift the visual signature. Defer until there's a real use case: on the no-PSRAM Olimex at large grids the tick is dominated by the synchronous ArtNet send (~35 ms), not Noise, so the effect is rarely the bottleneck there. - -**S3 render-only data point (2026-06-17, `scenario_perf_full`):** on the PSRAM S3 with **no output driver**, Noise is the dominant cost at every grid and there's no ArtNet floor to hide it: 16²→738µs, 32²→2,831µs, 64²→11,235µs, **128²(16K)→50,555µs (~20 FPS)**, clean ~linear-in-pixels (67×), so no fragmentation/realloc pathology, just raw simplex compute. The light effect (Checkerboard) on the same sweep is 6–11× faster (16K→7,949µs, ~128 FPS). So on a PSRAM board the heavy effect IS the 16K bottleneck (where on the Olimex the network send was). This is the strongest case for the fixed-point/strided-sampling ideas above, since a PSRAM board can run 16K grids that the network-bound Olimex never reaches. The S3 has a real FPU (LX7), so the win is less about software-float emulation and more about per-pixel simplex work; profile before committing. diff --git a/docs/work/future/backlog-modifiers.md b/docs/work/future/backlog-modifiers.md new file mode 100644 index 000000000..2012b2c68 --- /dev/null +++ b/docs/work/future/backlog-modifiers.md @@ -0,0 +1,24 @@ +# Backlog: Modifiers + +To-build items for Modifiers, grouped by module, each with its fields on one line. Other areas: [the future index](index.md). + +## MultiplyModifier + +### MultiplyModifier mapping-LUT memory at large grids + +Kind: Investigation · Platform: classic · Trigger: a classic board is connected + +The modifier reduces compute and costs mapping-LUT memory, measured per grid in [performance.md](../../reference/performance.md#multiplymodifier-compute-down-memory-up-noise-effect). Under the physical→logical fold build each physical light contributes ≤1 destination, so the destinations array is bounded by the real light count regardless of chain depth, so there is no build-time fan-out. + +**This is NOT a no-PSRAM blocker:** 16K Noise + Multiply has run on a classic ESP32 (no PSRAM, 320KB internal) before at **10–20 FPS** (WiFi vs Ethernet), sending frames out over **ArtNet to a display, not physical LED drivers**. It works there because classic's `nrOfLightsType` is `uint16_t` (half the LUT size) and the modifier shrinks the logical render grid. So the action is **re-verify the working classic setup when a classic board is connected** (find the config, its grid, mirror and ArtNet target, that reproduces the historical 10–20 FPS), not "fix an impossibility." Worth investigating only if that re-verification shows the LUT memory has regressed since: the destinations array is the obvious lever (it stores a `nrOfLightsType` per physical destination; a 2× kaleidoscope is 1:1 in *count* so the LUT need not store fan-out > the physical count: confirm it isn't over-allocating to `maxMultiplier()` when the effective fan-out is 1). Capture the classic numbers into performance.md's multi-board table first. + +## Across modules + +### Retro looks on a picture: scanlines, chroma bleed, tracking glitches (robwomp, 2026-10-21) + +Kind: Feature + +Asked for on Discord by robwomp, who loves the glitchy analog and digital look of the 90s and 00s and has Rockchip boards with HDMI and CSI inputs. +A captured picture becomes worth watching on lights once it can be degraded on purpose: scanlines, a color channel bleeding sideways, a frame that tears and rolls like a VHS tracking error. +Each is a modifier on what the Picture effect draws, so it works on any source, a capture, a GIF or a browser tab. +Ask robwomp which looks matter most before building, and have them tested on robwomp's capture hardware. diff --git a/docs/work/future/backlog-moonlive.md b/docs/work/future/backlog-moonlive.md index b74cd8fa0..07a582bc6 100644 --- a/docs/work/future/backlog-moonlive.md +++ b/docs/work/future/backlog-moonlive.md @@ -1,566 +1,267 @@ # Backlog: MoonLive -> **Forward-looking backlog document, an exception to the CLAUDE.md present-tense rule.** Third in a -> family: [livescripts-analysis-top-down.md](livescripts-analysis-top-down.md) designs the ENGINE -> (grammar, IR, codegen), [power-functions-analysis-top-down.md](power-functions-analysis-top-down.md) -> designs the BUILTIN SURFACE the engine calls into. This one is the ordered plan for closing the -> gap between them: which language features to add next, and why that order. Every limit quoted -> below was measured against the shipped compiler, not read off the source. - -## What this is for - -MoonLive compiles a C-subset to native code on the device. The subset is deliberately small: it -started at "fill a buffer with a color" and grew a feature at a time. This document is the ordered -list of what to grow next, and why that order. - -**The bar is a corpus, not one effect.** The predecessor MoonLight publishes a body of live scripts at -[ewowi/MoonLight/livescripts](https://github.com/ewowi/MoonLight/tree/main/livescripts), -and the goal is that MoonLive can **run them**, not that we migrate them into this repo. They are -written against a language with floats, structs and a wide builtin surface, so they are an honest -external measure of how far the subset still has to grow: each one that compiles unchanged is a -feature below that landed, and each one that does not names the next gap. - -The measure throughout is **what a real effect needs**. A simulation effect (particles, physics, -anything where this frame depends on the last) is the honest test, because it exercises state, -arithmetic and control flow together rather than one at a time. `moonlive/effects/particles/balls.mle` is the -worked example: bouncing balls, written against the language as it stands today, and every -compromise it had to make is a line item below. - -## The ceilings, measured - -Five hard limits, all found by hitting them: - -| limit | value | where | -|---|---|---| -| script state | **64 bytes** shared by all members | `kCtrlBytes`, `MoonLiveBuiltins.h` | -| distinct members | **16** ✅ | `kMaxCtrls`, same file | -| branch labels | **96** in the IR, **255** labels and **255** fixups in the assembler, counted across the whole script (an `if` or `for` takes up to 2, and each `||`, `!` and `!=` one more) | `kIrLabels`, `kAsmLabels`, `kAsmFixups`, `MoonLiveIr.h` | -| frame slots | **32**, shared by live variables, loop counters and staged call arguments ✅ | `kMaxLocals`, `MoonLiveIr.h` | -| native code | **16 KB** per script, the same on every chip; the RISC-V backend emits the most, and the largest script is at 13.7 KB there | `kCodeCap`, `MoonLiveBuiltins.h`; see § The code cap, per platform | -| ~~numeric types~~ | ~~`uint8_t`, `uint16_t`, `int16_t`~~ → **`int`, `byte`, `bool`, `fixed`, `string`** ✅ | still no float: `fixed` is Q16.16 | -| ~~builtin table~~ | ~~16, and 16 used~~ → **96** ✅ | `BuiltinTable::kMax`, raised, with an overflow assert | - -The branch budget was binary-searched with generated scripts: **6 `if`/`else` + 2 `for` compiles, -7 does not.** The state budget is what caps the balls effect at 4 balls rather than 25: six fields -per ball needs ~150 bytes and six members. - -The frame-slot budget was hit writing `aurora.mle` (2026-09-04), the first two-layer shader: it -compiles only after folding intermediates back into the expressions that use them, which is the -readability the language exists to give. Factoring the loop body into a helper would relieve it, -once helpers can take arguments and return values (§ 6). See § 8b. - -### The code cap, per platform (backlog, 2026-10-16) +To-build items for the MoonLive language and engine, each with its fields on one line. The ordered roadmap and its reasoning: [the language roadmap](analysis-moonlive.md). + +## Across modules + +### The next language steps, in order + +Kind: Feature + +The [language roadmap](analysis-moonlive.md#the-order-and-why) orders them by what removing each buys: [a bigger arena and more members](analysis-moonlive.md#3-a-bigger-arena-and-more-members-check-the-handle-route-first-storage-rules-now-in-the-type-system-design-above), [what remains of the predefined structs](analysis-moonlive.md#4b-predefined-structs-what-remains-after-coord3d-and-rgb), [function arguments and return values](analysis-moonlive.md#6-function-arguments-and-return-values-moderate-removes-a-real-footgun), [user structs and arrays of them](analysis-moonlive.md#10-user-structs-and-arrays-of-them-readability-once-the-arena-is-bigger), [what the StadBeest legs and eyes hit](analysis-moonlive.md#11-what-the-stadbeest-legs-and-eyes-hit-2026-10-07) and [a one-to-one port of hpwit's scripts](analysis-moonlive.md#12-a-one-to-one-port-of-hpwits-scripts-for-a-like-for-like-comparison-2026-10-19). A step lands when it deletes a row from the roadmap's compromise table, and the predecessor's livescripts corpus compiling unchanged is the outside measure. + +### The code cap, per platform (2026-10-16) + +Kind: Feature Measured across all 53 shipped scripts with `moondeck/moonlive/disasm.py` (asked on Discord by hpwit, author of ESPLiveScript): compiled bytes per source byte run Thumb-2 1.4x, Xtensa 1.7x, arm64 2.2x, RISC-V 2.8x. The largest script, `stadbeest-sauron.mle` (135 lines, 5.7 KB of source), compiles to 7.8 KB on Xtensa and 13.7 KB on RISC-V, so a script about 20% bigger is refused on a C3 or a P4. A script's code meets three ceilings and the lowest wins: `kCodeCap`, the staging the compiler reserves per token (`codeCapFor`, capped at `kCodeCap`), and the executable memory `allocExec` finds. Only the last is the hardware: IRAM on a classic ESP32 holds tens of KB, while a P4's 768 KB of internal memory is executable and a desktop's is unbounded. The fix is the code cap as a per-platform value from the platform layer: 16 KB on a classic ESP32 and a C3, 32 KB on an S3, 64 KB on a P4 and the desktop. Pinned by a test that a script past 16 KB compiles on a platform whose cap allows it and is refused with a clear message where it does not. The members, frame slots, entry points and labels are separate budgets, costing memory per script on every chip, so they stay until a script needs more. -## What a simulation effect gives up today - -Each row is a compromise the balls effect makes, and the language feature that would remove it: - -| forced to | because | wants | -|---|---|---| -| 4 objects, not 25 | 64-byte arena | a bigger arena, or a pool handle (shipped for particles) | -| whole-pixel motion | no fractional type | fixed-point or float | -| one flat color | no `hsv()` builtin | `hsv()` | -| one array per field | no structs | structs | -| the helper reads a member for its index | functions take no arguments | arguments | -| guards folded into `mod()` | 16 branch labels | a bigger label budget | - -## The library is already built; the engine is what is missing - -This is the part worth stating plainly: **the power-functions library exists and is largely -shipped** (particle kernels, the typed math surface, twelve showcase effects). It was built to make -MoonLive rich, and today almost none of it is reachable from a script. - -Its top-down spec already records what it needs from the engine, in -[§ 4 MoonLive requirements](power-functions-analysis-top-down.md), deferred at the time so the -library could finish compiled-side first. That list is the real roadmap, and it is more specific -than "add a language feature": - -1. **A builtin table of ≥ 64 entries.** ✅ *shipped*. `BuiltinTable::kMax` was **16, and the light - domain registered exactly 16**: the table was FULL, `add()` returned false past the limit, and - nothing checked the result, so the seventeenth builtin was dropped silently and the script - failed later with "unknown function". `kMax` is now 96, and an overflow is loud rather than - silent (`MM_ASSERT_NO_BUILTIN_OVERFLOW`). -2. **A larger branch budget (`kIrLabels`, today 16).** Every `if` costs up to two labels and every - `for` costs two, counted across the WHOLE script including its helper functions, and labels are - never reused once a scope closes. A script hits "too many branches in one script" well before it - feels complex. **Concrete case (2026-08-20):** giving `balls.mle` the wall bounces its own name - promises (four `if`s, one per wall) exceeded the budget and would not compile, even reduced to - one branch per wall by writing the direction bit arithmetically instead of toggling it. The - effect is 4 balls on a 64-byte arena. It ships bouncing only because the motion was rewritten as - a `beatsin()` per axis, where the reversal is inherent in the sine and costs no branch at all. - That worked out better here, but it was a workaround found under the ceiling rather than the - obvious way to write the effect, and the next script will not always have one. - **The cost is stack**, already measured and documented at `kIrAsmLabels`: ~4 bytes per label in - `lowerWith` (a local in the assembler), plus `Loop loops[kIrLabels]` and `int32_t - labelAt[kIrLabels]` in the spill pass. Raising 16 → 32 is a one-constant change; the number to - watch is `lowerWith`'s frame on the classic ESP32, which the existing note records going 480 → - 1120 bytes when the assembler tables were last raised. Reusing a label once its scope closes - would lift the ceiling without the stack, but that is a real allocator change rather than a - constant. -3. **Typed multi-argument host calls** (≤ 6 args, optional return). Today a builtin takes one - `uint32_t` and returns one, which is why `line()` had to be given a bespoke seven-argument - staging path and why most of the library is inexpressible. -4. ✅ **Script symbols**: SHIPPED as `t`, `width`, `height`, `depth`, `xPos`, `yPos`, `zPos`. -5. **Two entry shapes:** `frame()` for composing kernels (the scalable path) and `pixel(x,y,z)` - for per-pixel ergonomics (honest ceiling around 32×32). -6. **Stateful handles** (a `Pool`, a `BeatPhase`): script-declared, arena-allocated at compile - time, passed as an opaque first argument. No script-side memory management. - -Item 5 is worth noting against the arena work below: a particle pool as a HANDLE means the script -does not spend its own 64 bytes on particle state at all. That is a better answer than widening -the arena, and it is already designed. - -## The type system: ✅ *shipped* - -Shipped as designed, with three things worth carrying forward that the design did not anticipate: - -- **`fixed` needed three new assembler primitives**, not the one the design implied. A Q16.16 - multiply is `mulhi` + `mul` + two shifts, and the JIT had no shift instruction and no - multiply-high at all: `mulReg` is a plain 32-bit multiply. `shlImm`, `sarImm`, `shrImm` and - `mulhi` went into all four backends, every encoding checked byte-for-byte against the real - assembler. Worth it: the alternative made every fixed multiply a host call inside a per-pixel - loop, and made `int`↔`fixed` conversion (one shift) a function call. -- **An integer literal ADOPTS fixed at a meet point.** The design said any mix is an error; that - made `v * 2` and `if (v < 0)` unwritable. A bare literal now converts at compile time by - patching its own `Const` (free at run time), while a *variable* still names its conversion, - because a literal's meaning is visible at the site and a variable's scaling is not. -- **`movImm` truncated above 16 bits on arm64 and Xtensa.** A latent bug the whole time, masked - `& 0xffff` under a comment warning about exactly that failure mode: invisible while literals - capped at 65535, and fatal the moment a Q16.16 literal (2.0 is 131072) rode a `Const`. Both - backends now materialize the full 32 bits. - -`bool` truncates rather than normalizing (`flag = 256` reads false), because normalizing needs a -compare-and-select the IR has no op for: there is no `Sub` and no bitwise op. Waiting for a script -that writes a non-boolean expression into a bool. - -The design as agreed follows, unchanged. - -## The type system: the settled design (2026-08-23) - -Decided with the product owner after the signed-values work, whose four bugs were all -storage-width plumbing at the scalar boundary (a wrapped uint16 member, a sentinel read through a -16-bit window, a one-byte store into a two-byte member, a sign-blind array load). The design -removes that boundary rather than patching it again. This section supersedes the per-width -thinking in items 3, 5 and 10 below; those entries stay for their history and their measurements. - -**Five types, each usable as scalar or array; one storage rule each way:** - -> `int`, `byte`, `bool`, `fixed`, `string`. Every SCALAR occupies one uniform 4-byte slot, -> whatever its type. ARRAYS pack by element type. Strings are literals. - -A type is a SEMANTIC, not a storage width, for scalars: a `byte` scalar is an int the compiler -masks to 8 bits on assignment (one AND), a `bool` normalizes to 0/1 (one compare), `fixed` is an -int with scaled literals and a shifted multiply, `int` is bare. That single move is what buys -orthogonality without resurrecting the width machinery: one scalar load, one scalar store, no -sign windows, no width-matched bindings, no alignment rules. The width question survives only in -arrays, where it pays for itself in memory, and the array access path already carries an element -width (`idxPack`), so packed arrays are existing machinery rather than new. - -| Type | Min | Max | Step | The range is enough because | -|---|---|---|---|---| -| `int` | -2,147,483,648 | 2,147,483,647 | 1 | The domain's biggest integers: the ms clock (2^31 ms = 24.8 days, the known wrap), 12,288 lights, squared on-grid uv distances (<= 2^30). Anything wider lives in 64-bit inside a builtin, as escape() already does. | -| `byte` | 0 | 255 | 1 | Exactly a hardware channel, a palette index, a heat cell. The LED's range, not a chosen one. | -| `bool` | 0 | 1 | - | Normalized on store. | -| `fixed` (Q16.16) | -32,768.0 | +32,767.99998 | 1/65,536 | Coordinates top out near 256 px and uv at +-4.0, orders of magnitude inside the range. The step gives 16 fraction bits against the 8 a channel displays, so two chained factors still land at the display floor. One turn as 1.0 steps at exactly angle16's resolution, the precision sin16 already consumes. Q16.16 is libfixmath's fix16_t, the de-facto embedded standard. | -| `string` | - | one of the script's literals | - | An index into the compile-time pool. | - -**Cost on the hot path: none where it matters.** Scalar loads/stores are plain 32-bit forms -(on Xtensa `l32i.n`, 2 bytes against `l8ui`'s 3, so smaller than today). A `byte[]` element's -zero-extend and truncation happen INSIDE the load and store instructions; the only added -instructions are one mask on a `byte` scalar store and one normalize on a `bool` store. A `fixed` -multiply costs the mul-high + shift pair, which is the inherent price of fixed-point math the -builtins already pay inside their Q13/Q8.8 conventions; it just becomes visible and uniform. - -**`int` <-> `fixed` conversion is EXPLICIT, never implicit.** The conversion itself is one shift -(`<<16` / `>>16`), so the cost is trivial; the rule exists so a script never silently mixes a -pixel count into a Q16.16 expression and gets a number 65,536 times off. A mixed expression is a -compile error naming the conversion to write. - -**The two edges of `fixed`, stated so they are conventions rather than traps:** -- **Time never goes into `fixed` raw**: 32,768 seconds is 9.1 hours. `t` stays `int` milliseconds - and rhythm reaches a script through `beat()`/`beatsin()`, which is already the idiom. -- **Deep fractal zoom bottoms out at ~16 bits of magnification**, where the step becomes a pixel. - The same class of limit Q13 has at 13 bits, and better than float32 past that point (its 23-bit - mantissa is shared with the integer part). Extreme zoom is a 64-bit-builtin problem, not a - scalar-type problem. - -**`float` stays out, with a clean conscience.** The whole compiled library, the particle kernel -and escape() are integer/fixed already; math16.h supplies sin/atan/dist/sqrt; the hot path makes -float promotion a fatal warning. The FPU story splits the boards (LX6/LX7/P4 have one, the RISC-V -line does not), so JIT float either runs wildly differently per board or softfloats, against the -one-language-four-ISAs-identical rule. Fixed-point is bit-identical everywhere, which is also what -makes an effect reproducible. If the predecessor's corpus (written with floats) ever forces the issue, -the answer is compile-time float-literal-to-fixed translation, not runtime float. - -**Per-type notes:** -- `bool[]` ships byte-backed first (elements are 0/1 either way), and the 8-per-byte packing lands - later as a pure memory optimization when an automaton effect pays for its read-modify-write - codegen. Same rule as every constant here: against a script that needs it, not on speculation. -- `string` is a literal reference: assignable from literals, comparable, passable to a builtin, - one int of storage. NO concatenation, ever: runtime string building means allocation inside the - render loop. -- Where arrays LIVE: small arrays stay in the arena (internal RAM); grid-sized state goes through - ScratchBuffer handles (item 9b's model), which prefer PSRAM and fall back to internal heap on - the classic ESP32, whose fixtures are small by construction. `byte[]` is what keeps a classic - heat map at 1x rather than 4x. - -**Migration**: 26 shipped scripts spell C widths (`uint8_t`, `uint16_t`, `int16_t`). MoonLive is -not launched, so this is a clean mechanical break now and a compatibility program forever after. -That timing is a large part of why the decision was taken when it was. - -## The order, and why - - -Ordered by **what removing it buys**, not by implementation cost. - -### 1. A bigger builtin table: ✅ *shipped* - -`BuiltinTable::kMax` was 16 and the light domain registered exactly 16: **the table was full**, -and it failed SILENTLY: `add()` returned false, no caller checked it, and the next builtin would -have surfaced as "unknown function" in a script with nothing pointing at the cause. - -Now 96 (what the power-functions spec asks for), with `MM_ASSERT_NO_BUILTIN_OVERFLOW` so a dropped -registration is loud at startup rather than silent. This gated every other builtin; the palette -work below went in immediately behind it. - -### 2. Typed multi-argument host calls: ✅ SHIPPED - -SHIPPED: a builtin takes `const uintptr_t* args` with arities up to seven, plus typed flags -(`fixedArgs`, `fixedReturn`, `byRef`, `byStr`), and `emit` uses all seven. The paragraph below is the -problem as it stood before that. - -A builtin took one `uint32_t` and returned one. `line()` needed a bespoke seven-argument staging -path to exist at all, and most of the power-functions surface cannot be expressed without this. -The spec asks for ≤ 6 arguments plus an optional return. - -Doing #1 and #2 together is what actually opens the library; either alone leaves it stranded. - -### 3. A bigger arena and more members: *check the handle route first; storage rules now in the type-system design above* - -64 bytes of member data is why an effect holds four objects rather than twenty-five. - -**More members: ✅ shipped (2026-10-07).** `kMaxCtrls` is 16, measured at +280 bytes per scripted module on the S3 (class size 1312 → 1592 B). The StadBeest legs need 11 members, 6 controls and 5 values kept between frames, and before the raise packed four of those values into one `int` by bit ranges. The arena stays at 64 bytes, and the rest of this section is about the arena. - -**The two limits bind at very different points, and it is the COUNT that bites first.** -`fractal.mle` wanted 4 controls plus 4 scratch members plus a loop counter: 9 members costing -**12 of the 64 arena bytes**. It compiled once the counter was dropped (a `for` counter does not -have to be a member), so the script lost nothing, but the ceiling it hit was `kMaxCtrls` with 81% -of the arena still free. Any script with a handful of controls and a handful of intermediates -meets the same wall. If only one of the two moves, the count is the one worth moving: the four -tables it sizes were `DeclaredControl[8]` at 24 B each; the raise to 16 measured +280 B per -scripted module on the S3. - -**But check the handle route first.** The power-functions spec's item 5, a particle pool as an -arena-allocated HANDLE, means a simulation effect stops storing its own particle state entirely, -which removes the pressure without touching these constants. Widen the arena for the scripts that -genuinely hold their own state; do not widen it as a substitute for handles. - -Not purely a constants bump, and the blockers are known: - -- `kCtrlBytes` / `kMaxCtrls` are both `uint8_t`, so the arena caps at 255 bytes before any type - change. 150 bytes of particle state fits under that; much more does not. -- `static_assert(kCtrlBytes <= 64, "seeded_ is a 64-bit mask, one bit per script arena byte")` - (`MoonLive.h:355`) is the real gate. Past 64 bytes the seeded-member mask needs re-indexing, - and there is a worked example, because it was widened 16 → 64 once already. The assert exists - because the earlier `uint32_t` version silently aliased members mod 32. -- Watch `sizeof(MoonLive)`. It is held BY VALUE in every scripted module. `registerType`'s probe - builds one on the heap, since a probe on the boot stack is what boot-looped the P4 at 1440 bytes. - Growing the arena grows every scripted module. - -### 4. `setPaletteColor()`: ✅ *shipped* - -A script now writes one call where it used to write three: +### A for loop whose step never advances holds the render task (2026-10-29) + +Kind: Bug · Platform: all + +`parseFor` in `MoonLiveCompiler.cpp` requires the step to assign the loop variable but not to change it, so `for (int i = 0; i < 1; i = i) {}` compiles, and the emitted loop compares a counter that never moves against its limit with no iteration cap. On a device the render task never returns and the task watchdog resets the board, where the rule is degrade, never crash. The cheap tier is an iteration budget per entry, checked at the loop's back-edge, that fails the script with a status and leaves the layer dark, as a parse error does. Traced in the code, not run: a test of it would hang the suite until the budget exists, so the test lands with the fix. + +### A scripted driver + +Kind: Feature + +[MoonLive](../../explanation/architecture/moonlive.md) names a driver as a role still to come. A `MoonLiveDriver` is a `DriverBase` whose `tick()` runs compiled code over the output buffer, the way `MoonLiveLayout` answers `placeLights()`. The builtin it needs is a read of the corrected frame; the binding stays as thin as the effect's. + +### A scripted control's name can disagree with the file that declares it (2026-09-04) + +Kind: Bug · Platform: all + +Reproduced with `Nebula.mle` on the desktop: the file declares `rate`, the UI renders `rate1`, a name the file held earlier in the session. Ruled out: the builtin table, `kMaxCtrls`, the arena, declaration order and the control name. Start at `rebuildControls()` after a recompile and whether a control that vanished from the script is cleared from `ControlList`; a test that compiles, renames one control, recompiles and asserts the old name is gone pins it. Beside it: `/moonlive/Nebula.mle` and `/.moonlive/nebula.mle` are two files on a device and one on a case-insensitive desktop. + +### Desktop backend: a control-arena write is not seen by a second `run()` of an already-compiled program (2026-08-20) + +Kind: Bug · Platform: desktop + +The live-edit path is "move a slider, the next frame reads the new arena +byte, no recompile". On the host JIT the first render is correct, the write lands (the arena +reads back `232,3` = 1000 at the control's own `offset`), and a second `run()` of the same +compiled program still renders the OLD value. + +**Ruled out — do not re-investigate these:** +- Not `LoadCtrl16` missing: it is implemented, lowers to `a.load16`, and `load16` exists in all + three assemblers including `platform/desktop/moonlive_asm_host.cpp` (`ldrh`, immediate scaled). +- Not uint16-specific: a `uint8_t` member behaves identically. +- Not `defineControls()` or the control sink: a class with an unrelated second function fails the + same way, and one with no members at all (a literal in `tick`) does too. +- Not the arena address or offset: `controlSlot(dc[0].offset)` is the byte the write reaches. + +**A large part of the original symptom was a TEST BUG, not an engine bug.** `run()` without an +entry name starts at the BLOCK START — the first function compiled — which in a class with +`defineControls()` is `defineControls`, not `tick`. So the script rendered nothing and looked +like a broken control read. Naming `kEntryTick` makes the initial render correct in every +variant tried (with/without `defineControls`, with an unrelated second function, `tick` first or +second, uint8 and uint16). Only the re-read after a slot write is still wrong. + +**Reproduce** (in `unit_moonlive_fill.cpp`, which has `kCtrlTable`/`kSys` to hand): compile +`class T { int big = 5; defineControls() { addControl("big", big, 0, 1000); } tick() { +setRGB(0, big, 0, 0); } }`, `run(..., kEntryTick)` → 5, write the slot to 7, run again → still 5. + +**Impact: desktop only, nothing ships broken.** A wide control is hardware-verified on both ISAs +(S3 Xtensa and S31 RISC-V drive ember's `cycle` to 2000 and back). What is missing is DESKTOP +coverage of the live-edit loop — the path users touch most — so no host test can pin it and the +next regression there would surface only on a board. Add the runtime assertion together with the +fix, not before: asserting the current behavior would encode the bug. + +### A MoonLive script's branch tables are fixed and on the stack (2026-10-08) + +Kind: Bug + +**Found** on the StadBeest eyes: one more `if` in a helper turned a working script into `codegen failed: assembler overflow`. +Each function takes three labels (entry, depth guard, exit) before its own `if`s, loops and divisions by a non-power-of-two. +The assembler's label table holds 96 two-byte code positions in the stack the 48 four-byte ones took (2026-10-10), which let Sauron blink. +The nearer limits are now the compiler's own `kIrLabels` at 40, which reports "too many branches", and the 96 fixups. + +**The fix to build:** the label and fixup tables of all three assemblers move from members to the heap, as the code buffer already did, then `kAsmFixups` and `kIrLabels` rise (to 255 and 96); `Label` stays a byte. +The spill pass and the lowering hold `kIrLabels`-sized arrays on the stack too, so they move with it. +Verified by compiling the shipped scripts on a classic ESP32, an S3 and a P4, whose compile stacks are the ones at risk, and pinned by a host test with a script past 40 branches on each backend. + +### A MoonLive control past a limit vanishes without a word (2026-10-08) + +Kind: Bug + +**Found** in review: an `addSelect` with a 17th option, or one whose names overflow the 96-character option store, adds no control at all, and nothing tells the script's author why. +The other limits of `addDeclaredControl` behave the same way: past `kMaxCtrls` controls, a member already bound, a slot running past the arena, or names past the engine's 128-character name store (`nameText_`, about sixteen controls of seven characters). + +**The fix to build:** each refusal names itself on the effect's status line, as a compile error does, such as `control "shape": more than 16 options`, so the author sees which declaration lost and why. +Pinned by a test per limit that reads the status. + +### RISC-V emits 1.7 times the code Xtensa does (2026-10-08) + +Kind: Investigation · Platform: P4 + +**Found** on the StadBeest eyes: the script is 7 KB on an S3 and 12.4 KB on RISC-V, where one `smoothstep` call added to `tick` cost 432 bytes on its own. +A call inside a function with many live values saves and restores each of them, so every addition to a drawing loop costs RISC-V 400 to 500 bytes, and large scripts reach the 16 KB code buffer first on the P4 and the S31. + +**The fix to build:** measure where the RISC-V lowering spends its bytes on the shipped scripts (`disasm.py --isa riscv`), starting with the save and restore around host calls, and keep values that live across a call in callee-saved registers rather than saving them per call. +Pinned by a size test per backend on the largest shipped script. + +### A scripted module's DIMENSION chip still comes from its type, not its script + +Kind: Bug + +`writeModuleJson` emits an instance's `tags()` (HttpServerModule.cpp), so a MoonLive module shows +the emoji its loaded script declares. Its DIMENSION does not follow the same path: `/api/types` +carries `dim` per TYPE, captured at boot from a probe with no script loaded, and the module state +carries no `dim` at all. So a script declaring `int dimensions() { return 3; }` renders as 🟦 on the +card while behaving as D3 through `Layer::extrude`: the behavior is right and the chip lies. + +The fix is not a line in the serializer. `MoonModule` deliberately has no `dimensions()`: +`ModuleFactory::registerType` detects one with `if constexpr (requires ...)` on the CONCRETE type +precisely so the light-domain `Dim` enum stays out of core (ModuleFactory.h). Core holds a +`MoonModule*` when it writes state, so emitting a per-instance dim means giving `MoonModule` a +virtual that returns a byte, which puts a light-domain concept on the domain-neutral base for the +sake of a chip. + +Options, cheapest first: a `uint8_t dimByte()` on MoonModule defaulting to 0 (the enum stays in the +light domain, only the number crosses, mirroring what the probe already does); or leave it and +accept that a scripted module's dimension chip reflects its type. Worth doing when someone is +annoyed by the wrong chip, not before. + +### MoonLive core/platform layering + JIT sdkconfig scoping (CodeRabbit #29, 3 findings left) + +Kind: Restructure + +Four 🟠 Major boundary findings from the PR #29 review are real but each is its own scoped change, not a tail on the ring branch. The Critical sibling (a `cpl<3` overflow guard in the MoonLive effect's `tick`) landed with the branch it was found on. + +**One of the four is CLOSED (2026-09-07):** the scenario now uses `PreviewDriver`, the in-process +sink, instead of `NetworkSendDriver`. Its `tick_us` half was reviewed and DISMISSED rather than +fixed: a `measure` step asserts nothing, it records, and that recording is what feeds repo-health's +per-commit performance trend. A reviewer reading the file could not see that; deleting the +baselines would have blinded the trend to fix nothing. Recorded here so it is not re-raised. + +The three that remain: + +- **Core includes platform, compiled core in `mm_core`.** `src/core/moonlive/MoonLive.cpp` `#include`s `platform/platform.h` and calls the exec-memory API directly, and the root `CMakeLists.txt` compiles `MoonLive.cpp`/`MoonLiveCompiler.cpp` into `mm_core` and links `mm_core → mm_platform` — violating the header-only-core / no-platform-includes contract both files declare. The runtime exec-memory placement layer wants a core-neutral injected interface (or to move out of `src/core`), so the compiled/platform-dependent surface sits behind `mm_platform` and `mm_core` stays INTERFACE-only. These two are one change (same boundary). +- **W^X disabled in the board default.** `esp32/sdkconfig.defaults.esp32s3-n16r8` turns off `CONFIG_ESP_SYSTEM_MEMPROT_FEATURE` and enables `CONFIG_HEAP_HAS_EXEC_HEAP` for *every* build on that board, even with no MoonLive effect installed. The JIT genuinely needs a writable-then-executable heap, but that belongs in a dedicated MoonLive/JIT opt-in overlay or an explicit build profile, not the board default — so a stock build keeps memory protection on. + +### The compile-failure latch is not provable on the host + +Kind: Feature + +- **The compile-failure latch is not provable on the host** (2026-08-18). `MoonLiveScript::sync` + refuses to re-attempt a script that failed until its (name, content) changes. The latch exists for + a device-only reason: each attempt is two LittleFS reads (~5 ms on an S3), a layout is asked from + `lightCount()`/`placeLights()` as well as `prepare()`, and the pipeline asks repeatedly while + sizing a fixture, so the retries starve the render task until the watchdog resets the device. + + On the host a re-read costs microseconds and nothing observable differs. Four test shapes were + tried and each still passed with the latch REMOVED ENTIRELY, so none was kept: what survives pins + only that a script fixed in place compiles without a rename, which is control-checked. Closing + this needs either a counting seam (a compile counter the test can read) or a platform fake whose + reads are observable. Until then the latch is protected by its comment and by hardware, not by a + test. + +### Catch device-backend operand defects on the host + +Kind: Feature + +- **Catch device-backend operand defects on the host** (2026-08-18). Two array-codegen bugs shipped + to an S3 while all 1313 host tests stayed green, and both were control-checked: reintroducing + either one leaves the suite fully passing. `IrInst::c`/`d` are VREG fields the spill pass + renumbers, so a width parked there becomes a register number; and `sourcesOf` writes its sources + back POSITIONALLY, so reporting `kArg4` first shifts every real operand one place along. arm64's + register map absorbs both, which is exactly why the suite cannot see them. + + Three test shapes were tried and deleted for failing their control run: emitted-bytes difference + tests (wrong bytes still differ from other wrong bytes) and a register-liveness walk (the index + satisfies it whatever happens to the value). What DOES work is asserting an assembler primitive + directly, as `unit_moonlive_codegen_xtensa.cpp` now does for `addImm`. The general form is + probably an IR-level invariant check rather than a bytes-level one: assert that no op reports a + fixed ABI vreg among its positional sources, and that non-register operands never occupy c/d. + That is a property of the IR the host CAN evaluate, unlike the emitted code. + +### Size the MoonLive control arena to the script + +Kind: Feature + +- **Size the MoonLive control arena to the script** (2026-08-18). `kCtrlBytes` is a fixed 64 bytes + per engine (three engines per pipeline), so a script declaring one byte pays for 64 and one + wanting a 128-light array is refused. The arena is already `platform::alloc`'d, so the constant is + habit rather than necessity, and the member byte count is known at compile time. + + What blocks it: the system variables sit ABOVE the script region at compile-time constant offsets + (`kSysWidth = kCtrlBytes + 0`), baked into emitted code as `LoadCtrl` immediates and cached as + slot POINTERS by the bindings. A script-sized region moves every one of them. Closing it means + putting the system variables BELOW the script region so their addresses stop depending on it, + which touches the sysvar table, the bindings and every emitted immediate. The hard ceiling stays + 255 either way, since an arena offset is a `uint8_t` in the record, in `controlSlot` and in the + instruction. Until then, raising the constant is one edit and the failure is a clear compile + error naming the arena, so hitting it is visible rather than silent. + +### Drain MoonLive's print() through a queue + +Kind: Feature + +- **Drain MoonLive's `print()` through a queue** (2026-08-09). `print(v)` writes to serial directly, and an EFFECT script runs on the render tick — so a print inside one blocks the frame for as long as the UART takes. The burst cap bounds it (a handful of writes per compile, then a compare and a return), but bounded is not free, and `tick()` is annotated `MM_NONBLOCKING`. + + **What it costs when it comes:** a small preallocated record queue the built-in writes into, drained from a housekeeping path through the existing platform output seam. The budget and the burst-spent message stay as they are; only where the bytes are written moves. Worth doing when a script is left with a print in it on a real fixture, which is the case the cap exists for. + +### Downloading a scripted palette can repoint the active one (2026-09-06) + +Kind: Bug + +Found while testing the palette-download fixes. A `.mlp` that sorts BEFORE an already-installed one +silently changes what the current selection points at: with `fire.mlp` active at index 60, +downloading `beat-flash.mlp` re-sorts the scripted tail and index 60 becomes `beat-flash.mlp`. The +stored value never moved; what it means did. + +[Palette.h](../../../src/light/util/Palette.h) sorts scripted palettes AFTER the built-ins, whose indices are fixed, and solves half of it. What is not solved is the scripted tail among itself, and +that tail grows at runtime, which is what the download path made reachable. -```c -setPaletteColor(x, y, index, brightness); -``` +Nobody has reported it, and the blast radius is small: a rig with one scripted palette cannot hit it, +and the value re-reads correctly the moment the user picks again. It matters most where a preset or +an MQTT/HA automation stores the index and replays it later, which is the case where nobody is +watching the LEDs when it changes. -Three per-channel palette calls shipped first and worked, but the shape was wrong: three host calls per -pixel, and, because the compiler evaluates each argument independently, three evaluations of -whatever expression produced the brightness. A per-pixel radial falloff made that visible. +**What it would take:** persist the scripted palette by NAME alongside the index and re-resolve the +index on load, so a stored selection survives a re-sort. `paletteScript` already holds the resolved +file name for the editor, so the value exists; what is missing is using it as the authority when the +list changes. The alternative, appending new scripts rather than sorting them, keeps indices stable +but makes the picker unreadable as the list grows, which is the trade the sort was chosen over. -**Measured on an S3, 64x64 grid: 2331 us → 1838 us, 21% off the effect's own cost**, and the call -site went from three copies of a six-term expression to one. It also takes x/y rather than a flat -index, so the buffer layout (`mod(x, width) + mod(y, height) * width`) stops being open-coded in -every script. +### Changing a live MoonLiveEffect's script does not rebind it (2026-09-29) -No engine change was needed: `line()` already proved a Call builtin can write pixels through the -per-run draw canvas, so this rode the existing seam. `paletteRGB(i, bri)` answers the color as an `RGB` for a script that wants the value rather than a pixel (#4b). +Kind: Bug -### 4c. Insights from porting a per-pixel effect: *the cost is CALLS, not math* +**Being worked on the macOS bench; this entry is measurement for whoever has it.** Rewriting the `script` control on an existing `MoonLiveEffect` leaves the module largely rendering what the previous script rendered. The card reports a clean status, the byte count updates, frames keep animating, so nothing in `/api/state` says anything is wrong. Only the pixels do. -A second port (`moonlive/effects/waves/octopus.mle`, a polar spiral) measured very differently from the -first, and the difference is the useful part. +Measured on Windows over the 33 shipped `.mle` effects, same scripts and same instrument, changing only the route. Each script ran 3 s while its preview frames were sampled, then scripts were compared on mean level and on frame-to-frame motion; a pair matching closely on both is a pair that cannot be telling the pixels apart: -| effect | shape | S3, 64x64 | -|---|---|---| -| balls | four small discs, ~500 lit pixels | **1278 us** | -| octopus | every pixel, every frame | **21762 us** | +- Reusing one module and rewriting its `script` control: **59 indistinct pairs**, in groups. Nine scripts (noise, octopus, plasma, rain, ripples, sparkle, spectrum, stadbeest-frog-eye, stadbeest-walk) all sat at mean 27.2 and motion 0.16, and six more (fountain, fractal, gradient, lines, metal, mh-ambient) at mean 42.9 and motion 0.33. +- A fresh module per script, which is what the UI's picker does: **1 indistinct pair**, and the values separate as you would expect, with fractal at 108.2, noise at 127.2, mh-aim at 255.0 and stadbeest-frog-eye at 151.5. -A third port (`metal.mle`, an SDF shader) put a number on the most expensive builtin, measured on -shiffy's 80x48: +The picker works because it does two things rather than one: `replacePickedType` in `src/ui/app.js` calls `replaceModule` to put a NEW module in the slot and only then `setCardScript`. A user who switches scripts from the dropdown on an existing card gets the broken path; a user who picks from the module picker does not, which is why the same library looks fine one way and stale the other. -| variant | tick | vs plasma | -|---|---|---| -| plasma (9 calls/px, no sqrt) | **16031 us** | baseline | -| metal, 2 `polarR`, no `uv` | **32843 us** | 2.0x | -| metal, 2 blobs + `uv` | **46221 us** | 2.9x | -| metal, 3 blobs + `uv` | **59600 us** | 3.7x | +Two scripts render nothing that changes even by the working route, and may simply be correct: `gradient.mle` produces one distinct frame at mean 104.8 with zero motion, and `mh-aim.mle` one distinct frame with every sampled channel at 255. `mh-aim` aims a moving head rather than painting a grid, so full white may be the honest answer. Worth an eye rather than an assumption. -**`polarR` costs ~13400 us per call site per frame at 3840 pixels: about 3.5 us per pixel, one -builtin.** It wraps `dist16`, a real square root, and `draw.h` already measures a sqrt-based SDF at -~108 cycles/px against ~14 for the squared form. A squared-distance builtin (`polarRSq`, or letting -a script compare against `r * r` as `ripples.mle` does) is the cheap fix, and it is the same trick -the compiled effects already use. +The check itself is worth keeping. Frame distinctness plus cross-script comparison catches exactly what compiling cannot, and it is what the existing test suite has no view of: `unit_MoonLiveScripts.cpp` proves all 55 scripts COMPILE, and every one of them passed while the pixels were telling this story. -Also measured and **disproved**: hoisting the four loop-invariant `beatsin` calls out of the inner -loop into members moved 59600 to 58459 us. Call overhead per se is NOT the cost here: the square -roots are. Worth recording because it contradicts the natural first guess. +**From the card (2026-09-29, desktop).** Six consecutive picks from the script dropdown: two switched, four did not, and the fourth failure recovered on its own. The file is on the device and complete, the card shows the new name, and no alert fires. The dirty-editor path is not the cause: it alerts "Not switching: this script still has unsaved changes" (`src/ui/app.js`). The duplicated download logic in the dropdown was a real defect and is fixed, pinned by `test/js/ui-script-picker-download.test.mjs`, and it did not change the symptom. -That is ~5.3 us per pixel, and it is not the arithmetic; it is **~32,000 host calls per frame**. -Each `polarA`/`polarR`/`sin`/`scale`/`beat` is a real call through the builtin ABI, and a -whole-canvas effect makes eight or so per pixel. +**Worth deciding alongside the fix**: whether the script control should be editable at all, or whether a scripted module's script is chosen once through the module picker. The module picker path works reliably, so a read-only control would remove the failing surface rather than repair it. -Three things follow, and they sharpen the priorities above: +### A scripted modifier that animates every frame -1. **No common-subexpression elimination.** `polarR(x - cx, y - cy)` appears twice in one - expression and is CALLED twice. The compiler has no way to name an intermediate, so a script - cannot hoist it either, the same limitation that made a palette pixel cost three brightness - evaluations before #4. A single-assignment local (part of #4b/#6's territory) would remove a - third of this effect's calls on its own. -2. **Per-pixel builtins want to be inline ops, not calls.** `setPaletteColor` is a Call and that is - fine at ~500 pixels; at 4096 the call overhead dominates. The `Inline` kind already exists - (`setRGB` uses it); the polar pair and the palette write are the candidates. -3. **A `frame()` entry shape would sidestep it entirely.** The power-functions spec's item 4, - compose kernels over a whole frame rather than script per pixel, is exactly the answer for - this class of effect, and octopus is the concrete case that argues for it. +Kind: Feature -Worth stating plainly: 35 fps for a full-canvas 64x64 effect is usable, and the port needed no -new language features beyond two builtins. The ceiling here is call overhead, not expressiveness. +- **A scripted modifier that animates every frame** (2026-10-11): `MoonLiveModifier` compiles a `modifyLive()` entry, so a script moves lights per frame with `t` (scroll, wobble, sweep) as the compiled Rotate does. Geometry only: dimming is a blend mode or an effect's `mulRGB`, per [the layer compositing plan](../present/Plan-20261011%20-%20Layer%20compositing%2C%20masks%20and%20transitions%20(2%20of%203).md). Built once a script needs it; the cost to measure is one compiled-script call per light per frame, on an S3 at its largest grid. -**A naming lesson, learned by breaking two scripts:** the polar builtins were first called -`angle`/`radius`, which silently broke `ring.mll` and `rose.mll`: both declare a `radius` member, -and a builtin shadows a member name. Builtins share one namespace with every script's own -variables, so a new builtin should take a name a script would not: `polarA`/`polarR` rather than -the obvious ones. The compile-every-script test caught it immediately, which is the argument for -keeping that test cheap to run. - -### 4b. Predefined structs: `Coord3D` and `RGB`, together: ✅ *shipped* - -A struct is a named fixed-size array the compiler expands into scalar operations: a member, a local, a parameter and a return value, with field access, whole-struct assignment and a constructor. A struct argument fills one position per field, so every three-channel builtin takes an `RGB`. `getRGB` and `paletteRGB` answer one. The language reference holds the rest: [RGB and Coord3D](../../moonmodules/light/moonlive.md#rgb-and-coord3d). +### A scripted modifier needs a way to drop a light -**No `HSV` struct.** A hue wheel is how an effect picks color while ignoring the user's palette, so `HSV` stays in `setPalEntryHSV`, which authors a palette rather than bypassing one. - -What remains: `setColorFromPalette(pos, index, brightness)` taking a `Coord3D`, and arrays of structs (§ 10). +Kind: Feature -### 5. Fractional arithmetic: *superseded by `fixed` in the type-system design above* +- **A scripted modifier needs a way to drop a light** (2026-08-10). A coordinate slot is a byte, so a script that computes past 255 wraps: `shift.mlv` with a large `amount` lands lights back at the left edge instead of walking them off it. The Layer already drops an out-of-bounds position, but a script has no way to SAY out-of-bounds — every value it can write is a valid coordinate. Needs a sentinel the binding recognizes (or a wider coordinate slot), at which point the "walks off the edge" behavior a scroll modifier wants becomes expressible. -Every remaining visual compromise traces back to this: smooth motion, real velocities, a bounce -that conserves speed, and any falloff term that makes a shape look round rather than flat. Without -it each is a separate workaround. - -Two routes, and the choice matters more than the schedule: - -- **Fixed-point** (a `q8_8` over the existing `uint16_t`): no new backend work: it lowers to the - integer ops all three ISAs already have. Enough for positions, velocities and most ramps. -- **True `float`**: closer to a precompiled effect's source, which is the near-verbatim porting - goal the top-down analysis sets. Costs an FPU path per backend; the P4 and S3 have one, the - classic ESP32 does not, so it would soft-float or refuse. +### A scripted modifier that reshapes the grid -Fixed-point first is the pragmatic order: most of the quality at a fraction of the cost, and it -does not foreclose float. - -**Settled** by the type-system design above: `fixed` is Q16.16 on a uniform int slot, float -stays out, and the range analysis lives there. - -### 6. Function arguments and return values: *moderate, removes a real footgun* +Kind: Feature -`draw(i)` instead of setting a member the helper reads. The current shape is not just verbose: -caller and callee agree by convention and nothing checks it, so a helper called from two places -with different state silently does the wrong thing. It is also what makes helpers composable. - -Script functions DO exist and ship: `balls.mle` calls `drawBall()`, `crosshair.mle` calls three -helpers, and every layout and modifier is one. Recursion works. Three limits sit on top of them, and -`aurora.mle` hit all three at once writing a `bright(v)` helper to apply one contrast window to two -layers (2026-09-04). **The return value shipped the same day, and eight parameter words with structs on 2026-10-12**, so one remains: - -| limit | what the compiler says | -|---|---| -| no forward calls: a helper must be declared ABOVE its caller | `unknown function`, with the column but not the name | - -A function may now RETURN a value: `int f() { return ...; }` and the call is an expression. Each -backend's `callLabel` preserves the whole vreg pool and delivers the result into the destination -register, so a value survives the calls that follow it in the same expression. - -The forward call is the cheapest to build and the most confusing to meet, since the message is the -one an actual typo produces: a second pass over the class body resolves it, which the code comment -names as the reason it is refused rather than half-supported. - -Parameters and returns also relieve § 8b: a helper's variables are live only inside it, so factoring -a loop body into a function is how a script stays under the frame budget. - -### 7. Signed values: ✅ *shipped* - -`int16_t` members, signed comparison, signed `/` and `%`, and `uvX`/`uvY` returning a signed -coordinate with no bias to subtract. - -**The framing this item had was wrong about the cause, which is worth recording.** It described the -problem as `a - b` wrapping, and prescribed signed comparison. Writing a Mandelbrot effect produced -four bugs in one session and **not one of them was a comparison bug**: no `<` or `>` ever produced a -wrong picture. Three were the *biased-unsigned* convention, where a builtin returned a value centered -on 32768 and the author had to subtract that bias, which is exactly the subtraction unsigned -arithmetic breaks. The fourth was a byte argument truncating instead of saturating. Every one -presented as "the effect renders nothing", never as an error. - -So what shipped is smaller than "make the language signed" and removes more than it adds: - -- `signedArg`'s undocumented **16-bit window** is gone. It was the inverse of `uint16_t` member - truncation, written down in neither place, and it is what made `d = 60000` read as -5536. -- The **bias on `uvX`/`uvY`** is gone: a coordinate has an origin, so the center is 0. -- `sin`/`cos` **keep** their bias, deliberately. A wave has no origin, and `scale(sin(a), n)` - sweeping a full axis is the idiom 14 shipped call sites use. A script wanting a signed wave - writes `sin(a) - 32768`, which works now. -- **Comparison** is a separate `BranchGeS` op, not a change to `BranchGe`: the array-index clamp - and the loop guards need unsigned, and a negative index arriving as a huge value is what lets one - branch catch both ends of a range. -- **`int8_t` is deliberately absent.** Xtensa has no signed byte load, so it would need a - sign-extend sequence the other three ISAs do not, for a width no script has asked for. - -`escape()` stays a builtin regardless: its Q13 squaring needs 64-bit intermediates, which a 32-bit -script value cannot express however signed it is. - -### 8. More branch labels: ✅ *raised to 96 IR labels and 255 assembler labels and fixups, the tables on the compile-time heap* - -16 was tight enough that a straightforward nested draw did not fit. At 40 IR labels and 48 assembler labels with 96 fixups two scripts still hit them on 2026-10-07: the StadBeest legs with a bass envelope, and the StadBeest eye with darts, blinks and a ripple, each at roughly 20 to 25 `if`s across all its functions. Both fit only after branches were rewritten as arithmetic (§ 11). Raising `kIrLabels` costs -compile-time table space and nothing at run time. Measure what a realistic effect needs before -picking a number: the balls port wanted ~12 and had to be folded down. - -### 8b. More frame slots: *the encoding is NOT the limit; measure the stack* - -**32 live variables**, shared by a script's named variables, its loop counters, and the arguments it -stages for a call. The budget is what is live AT ONCE rather than a total: a call hands its staging -slots back, and an `if`, `else` or `for` block hands its locals back at the closing brace -([MoonLive](../../moonmodules/light/moonlive.md) documents both). A script that exceeds -it fails with "too many variables in this function", "too many arguments to hold" or "too many loop -variables". - -Sixteen is enough for the shipped corpus and was not enough for the first two-layer shader: -`aurora.mle` wants an oscillator per layer, the grid centre, a polar angle and radius, and a field -sample per layer, all live in the same scope inside two nested loops, with the loop counters and -each call's staged arguments drawn from the same 16. - -`kMaxLocals`'s own comment says raising it "means widening the frame on all three backends together, -the slot index is an instruction field". **Measured on the shipped assemblers, that is not so:** - -| backend | how a slot is addressed | slots the encoding allows | -|---|---|---| -| Xtensa | `s32i/l32i`, an 8-bit offset counting 4-byte words | 256 | -| RISC-V | `sw/lw`, a 12-bit signed offset from the frame pointer | 2048 | -| host (arm64, x86-64) | an offset from a parked frame pointer | not encoding-bound | - -So the real cost is **stack**, not encoding: each slot is 4 bytes of frame on the render task, and -`kAsmLabels`/`kAsmFixups` next door carry the warning that this project has already bootlooped a P4 -on an oversized stack frame. 16 slots is 64 bytes; 32 would be 128. That is a cheap change on its -face, and the measurement settled it: **`kMaxLocals` is 32** (2026-09-04), 128 bytes of frame, and -the encoding was never the limit on any backend. - -### 9. Division: ✅ *shipped* - -`/` and `%` are operators, at multiplication's precedence. Both lower to a host call the way `mod` -already did, so the operator costs nothing the capability did not already cost: the divide itself -is the expense, and it is a host call wherever it appears: fine on a cold path, deliberate -per-pixel. The parser resolves both through the builtin table (`div`, `mod`) rather than knowing -either by name, so core stays domain-neutral and a domain that registers neither simply has no -operator. `mod(a, b)` stays registered: it is the name the cyclic case reads best under. - -### 9b. A ScratchBuffer pool handle, ✅ *shipped for particles* - -A script sizes its own particle pool with `pool(n)` from `defineControls()`, and the buffers live in -`MoonLiveParticles` (six `ScratchBuffer`s the binding owns) rather than in the 64-byte arena, which -would have held about five particles. Sizing is reachable ONLY from that one moment: the sizing sink -is installed around the `defineControls` run, so `pool()` from `tick()` is a no-op reporting the live -count and no allocation ever reaches the render path. - -Nine builtins, all whole-pool passes: `pool`, `emit`, `gravity`, `drag`, `step`, `age`, `render`, -`bounce` and `collide`. The last two shipped after measurement: collide is an N-body check (3.2 us -at 48 particles against 0.1 us without, 53.6 us at 200), so the quadratic is real but the absolute -cost at ball-pit sizes is not, and the numbers ride the builtin so an author knows what a big pool -would cost. -The cost model is the point. `fountain.mle` measures **9 us** on a 128x96 desktop grid against -`metal.mle`'s **1557 us** on the same grid: the first script vocabulary whose cost scales with the -OBJECTS rather than with the grid. - -**Structs were NOT needed, and that is a finding rather than a deferral.** Every pool operation is -whole-pool or takes plain scalars, so a script never names a particle field. #10 below is about -`ball[i].x` INSTEAD of parallel arrays, which a pool removes the need for; #4b is `Coord3D`/`RGB` -for per-pixel shader signatures, whose real prerequisite is #2. - -Not exposed, each with a reason: `spray` -(`emit` with a wide cone is one), `spawn` (per-particle in a whole-pool API), `force`/`forceSmall` -(needs the `acc` buffer for wind nothing needs yet), `attract`, `wrap`, `liveCount`, `clear`. - -### 10. User structs, and arrays of them: *readability, once the arena is bigger* - -`ball[i].x` instead of parallel arrays. Genuinely nicer and closer to how a precompiled effect -reads, but parallel arrays work the moment the arena is big enough. Last because #1 removes most -of the pain, not because it does not matter. - -Arrays of the five scalar types already ship (`byte heat[16]` in `ember.mle`), so what is missing -here is arrays OF STRUCTS, and it splits in two. An array of a PREDEFINED struct (§ 4b) is the -smaller half and the one an effect reaches for first: a script holding `Coord3D pos[8]` is exactly -the parallel-array flattening this item exists to remove, and the element width the array path -already carries (`idxPack`) is the machinery it needs. An array of a USER struct needs the general -declaration machinery above it. Worth building in that order if this is picked up. - -### 11. What the StadBeest legs and eyes hit (2026-10-07) - -Two effects written in one session on real hardware: `stadbeest-walk.mle` (a walk cycle on 10 tubes, dancing to music) and `stadbeest-frog-eye.mle` (a frog's eye on a 241-light ring disc). Besides the member and label limits above, each of these cost a workaround: - -| limit | what it cost | wants | -|---|---|---| -| "codegen failed: assembler overflow (branch range, slot, or immediate)" | names neither the limit nor the line; finding it took a desktop bisect with `disasm.py` | the message names the table that ran out and the function it was filling | -| "too many arguments to hold" | fires when the eye's `tick()` held too many live locals, so the wording sends the author to the calls | a message about live variables when that is the cause | -| a failed recompile leaves the effect dark | on a live installation every edit that does not compile switches the light off until a fixed one arrives; it happened three times in the session | keep running the last program that compiled, and show the error beside it | -| `setPaletteColor` clamps the palette index | a palette is circular, but `hue + offset` past 255 sticks at the last color unless the script writes `mod(..., 256)` | wrap the index, as the palette itself does | -| an effect visits every grid pixel | the eye computes 1600 pixels to light 241, and the legs' grid is mostly empty between 10 tubes | a way to iterate only the layout's lights, for sparse layouts | -| a `bool` control takes a min and a max | `addControl("pulse", pulse, 0, 1)` | `addControl(name, flag)` for a bool | - -### 12. A one-to-one port of hpwit's scripts, for a like-for-like comparison (2026-10-19) - -hpwit's compiler (`hpwit/new-parser`, `asmparser2`) runs whole C-like programs. His `multi_effect_controller.sc` (800 lines) and the five `ProceduralEffects` were ported to MoonLive on 2026-10-18 (`moonlive/projects/hpwit/`), but as the same LOOK rather than the same program, so code size and frame time do not compare like for like. A one-to-one port needs the language to accept his programs as written. Each construct was compiled on the desktop on 2026-10-19: - -| his scripts use | MoonLive today | what it takes | work | -|---|---|---|---| -| ~10 KB of state: struct arrays of 160 to 220 objects, a 40×80 trail, 12 KB heat and angle tables | 64 bytes of members, 16 members | an arena sized from the declarations, on the heap or PSRAM, past the `uint8_t` and 64-bit seeded-mask limits; item 3 has the blockers | L, 3 to 5 days | -| ~30 functions: struct methods and free functions | 8 per class (`kMaxEntryPoints`) | a function table sized per script; free functions outside the class | M, 1 to 2 days | -| `struct` with fields, methods and a constructor, arrays of them | refused | item 10, user structs, then their methods and arrays | XL, 1 to 2 weeks | -| `float`, `uint8_t`, `uint32_t`, casts like `(int)(x)` | refused | `float` (every target but the RP2040 has an FPU) or float mapped onto `fixed`; the sized integer names as aliases; casts | S for the names and casts, 1 day; M to map `float` onto `fixed`, 2 days; XL for a real `float` on five backends, 1 to 2 weeks | -| `grid[y][x]` (his `[y, x]`) | refused | 2D arrays over the existing element-width path | M, 1 to 2 days | -| `while`, `break`, `continue`, `i++`, `+=`, `?:`, `switch` | all refused | the statements and operators, each a parser and IR addition | S each for `while`, `++` and `+=` (rewritten into what exists), 1 day together; M each for `break`/`continue`, `?:` and `switch`, 3 to 5 days together | -| `#define` constants | refused | a constant declaration (`const int`) rather than a preprocessor | S, half a day | -| `main()` with `while (true)`, `show()`, `delay()` | `tick()` per frame | a shim: the loop body becomes `tick()`, `show()` and `delay()` become no-ops, `millis()` reads `t` | M, 1 to 2 days | -| `hsv()`, `rand(mod)`, `atan2`, `hypot`, `sin` on radians, `printfln`, `pinInterrupt` | palette, `random16`, `polarA`/`polarR`, `sin` on 0..65535, `print`, none | host bindings with his signatures, under his names, in a compatibility table | M, 2 days, once `float` is decided | -| code past 16 KB | `kCodeCap` 16 KB | the cap per platform (§ The code cap, per platform) | S to M, 1 day | - -Work is in days of agent implementation with the product owner's review, for the parser, the IR and all five backends, each with its tests; S is under a day, M one to two, L a week, XL two. In all about five weeks with `float` mapped onto `fixed`, about seven with a real `float`, the structs and the arena being most of it. - -Most rows are general language growth the MoonLive scripts want anyway; the `main()` shim and his host bindings exist only for the comparison, and stay behind a compatibility table rather than entering the standard vocabulary. **Decide first** whether the comparison is worth the compatibility layer, or whether comparing on the ported looks, now in `moonlive/projects/hpwit/`, is enough. - -## How to know a step landed - -Two measures, one local and one external. - -**Local:** re-port a simulation effect after each step and delete the row it removes from the -compromise table above. A port with no rows left (dozens of shaded, colored objects with -fractional velocities on a full-size grid, at a frame rate the bench can measure) means the -language carries a real simulation. - -**External, and the harder bar:** take the -[predecessor's livescripts corpus](https://github.com/ewowi/MoonLight/tree/main/livescripts) and -count how many compile and run unchanged. That number is the honest progress metric, because those -scripts were written without regard for our subset. Track it per step; a feature that moves it is -worth more than one that does not, whatever this document guesses. +- **A scripted modifier that reshapes the grid** (2026-08-10). `ModifierBase::modifyLogicalSize` lets a modifier change the logical `width`/`height`/`depth` — a Multiply kaleidoscope grows the grid, a crop shrinks it — and a compiled modifier uses it. A SCRIPTED one cannot: system variables are read-only, so `MoonLiveModifier` writes the box in and never reads it back. Needs a writable system variable — the binding reads the slots after the script returns and reports the result through `modifyLogicalSize` — which is a new `SysVarKind` (or a mutable flag on `SysVar`) plus the read-back, not a new builtin. Until then a scripted modifier can fold coordinates but not resize the grid they live in. diff --git a/docs/work/future/backlog-platform.md b/docs/work/future/backlog-platform.md new file mode 100644 index 000000000..9a2dcb824 --- /dev/null +++ b/docs/work/future/backlog-platform.md @@ -0,0 +1,667 @@ +# Backlog: Platform + +To-build items for the platform layer and boards, grouped by module, each with its fields on one line. Other areas: [the future index](index.md). + +## Across modules + +### SIMD inner loops for noise and the bilinear lerp on the S3 and P4 + +Kind: Feature · Platform: S3 + +The noise inner loop and the bilinear resampler are the per-pixel cost of every field and transport effect. PIE variants behind `src/platform/`, bit-identical to the scalar forms by test, and the FPU `curl16` where `hasFpu`, measured per target through the KPI gate; the P4 gets the same. The P4's PPA fits `upscale16`, a scaled blit, and nothing in advection, which displaces every pixel by its own velocity. + +### Network Receive on a P4 past 3,000 packets a second (2026-10-26) + +Kind: Feature · Platform: P4 + +Below about 3,000 packets a second a P4 receives every frame; past it packets go, more for Art-Net than for DDP, as [the table and where a packet's time goes](../../reference/performance.md#network-receive) show. +Network Receive's task spends tens of microseconds per socket call, a `select` hundreds. +Both network tasks may run on either core, yet stay on core 1 with core 0 idle. +Two levers, each its own change: the network tasks on core 0, where pinning them by `ETH_MAC_FLAG_PIN_TO_CORE` and lwIP's task affinity kept Ethernet from coming up, so that needs its own look first; and lwIP's raw `udp_recv` callback writing into staging from lwIP's task, with no socket, no `select` and no receive task. + +### The `.deb` ships the systemd unit, raw Ethernet included (2026-10-22, from robwomp on Disc + +Kind: Feature · Platform: Pi · Stage: Install + +The package holds only the binary, so a Pi or NanoPi user writes `/etc/systemd/system/moonlight.service` by hand from MoonWalk's "Run at boot" step. That unit grants no `CAP_NET_RAW`, so the panel card driver's `AF_PACKET` socket fails unless MoonLight runs as root, and the `setcap` alternative is lost whenever apt replaces `/usr/bin/MoonLight`. `CAP_NET_RAW` is all MoonLight needs, since it sets no interface flags. The fix: `package_desktop.py` installs the unit with `AmbientCapabilities=CAP_NET_RAW` and a dedicated `moonlight` user, so installing is `apt install` plus `systemctl enable --now moonlight`. MoonWalk's unit step shrinks to that enable, and the `setcap` step stays for a binary run by hand. + +### An HTTPS request on a board without PSRAM (2026-10-24, from the JSON plan) + +Kind: Investigation · Platform: classic + +A board without PSRAM never fetches over HTTPS: `tlsRoom` asks for the 100 KB internal floor plus a 45 KB TLS session, and a classic ESP32 has 55 KB free at idle. `CONFIG_MBEDTLS_DYNAMIC_BUFFER` allocates mbedTLS's buffers as a connection needs them rather than the worst case up front, which may fit one request on an idle board. To measure on `MM-Olimex` (.207): the peak a session takes with it, what a smaller floor costs WiFi and the HTTP server, and whether a feed then fetches with lights running. Until then such a board gets its data pushed from one with PSRAM. + +### A StadBeest eye reset once by the task watchdog (2026-10-24) + +Kind: Investigation · Platform: S3 + +On 2026-10-23 eye2 (`MM-Eye2`, an ESP32-S3-Zero) reset once by the task watchdog, shortly after its OSC service was added, with no USB attached to read the panic. It has not recurred: both eyes ran the crossfading autopilot without a reset afterwards, and on 2026-10-24 they report `POWERON` after the beast was switched on. Recorded so a recurrence is recognized: `bootReason` on the System card is the one reading the network gives, since the board keeps no crash record; the panic itself needs the eye on USB with `monitor_esp32.py`. + +### What the WiFi buffer copy costs (2026-10-24) + +Kind: Investigation + +`CONFIG_LWIP_L2_TO_L3_COPY` copies every received frame so a socket the blocked loop stops draining cannot hold the 32 WiFi receive buffers; without it, every firmware upload stalled at 150 to 860 KB. Its cost is unmeasured: the frame rate on a busy DDP stream with and without it, same scene. If it costs much, firmware uploads move onto the transfer slots so nothing blocks the loop, and the copy goes. + +### An update's connection that would not open until a reset (2026-10-22, from the internal RA + +Kind: Investigation + +On 3.0.0 a board could not open the update's connection (`ESP_ERR_HTTP_CONNECT`) until it was reset, with 80 KB of internal RAM free. TLS has lived in PSRAM since then (`CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC`), which may be what cured it; it has not recurred. Recorded so a recurrence is recognized: the free internal RAM and largest block at that moment are the first readings to take. + +### Move more devices to MoonLight over WiFi (2026-10-09) + +Kind: Feature · Stage: Install + +The migration image (`moonbase/migrate/`) moves a board running WLED to MoonLight through WLED's own update page, with no cable: verified on WLED 0.15.0 on an ESP32-C3, on a bench board with a power cut at every step and on an Athom bulb. +An image is built for one chip and carries one variant's partition table, so each further reach is its own piece: +- **Every MoonBase variant:** `migrate: True` on `esp32`, `esp32-16mb`, `esp32-wrover`, `esp32-pico` and `esp32s3-zero`, each rehearsed on a bench board of that chip and shipped as a release asset. The variants without MoonBase (the larger S3s, the P4) need another design, the image writing the app itself. +- **Other WLED versions:** 0.13 and 0.14 keep the same files and layout, untested. 0.16 checks an upload's release name, so the user ticks WLED's option to skip it, untested. A WLED build with one app slot cannot move the image aside. +- **Tasmota:** its layout already has a recovery app in the factory slot and one app slot, so the image arrives running where MoonBase goes with nowhere to move aside; it would write over the recovery app instead. Its WiFi lives in a binary settings block in NVS, which needs its own reader. +- **ESPHome:** its update accepts the image, but its WiFi is compiled into the firmware, so MoonBase opens its access point. +- **A generic WiFi source:** ESP-IDF's WiFi driver can keep the last network in NVS under a namespace every framework shares; tried when no WLED files are found, it may carry the network from the others. Bench first, since whether it is there depends on how each firmware starts WiFi. + +### Back to WLED over WiFi (2026-10-09) + +Kind: Feature · Stage: Install + +The migration image moves a WLED device to MoonLight without a cable, but the way back needs one: MoonBase installs MoonLight images only, so a bulb going back to WLED is opened for USB. +Owners and makers of these devices care most about this: a firmware change that cannot be undone without a cable is a bigger step than one that can. +It is ours to build rather than WLED's, since the layout it undoes is MoonLight's, about one to two sessions: +- **A small image that downloads WLED itself** from WLED's GitHub releases while it runs, so MoonLight never redistributes WLED's binary and the image stays far from what it writes. +- **WLED goes into WLED's second app slot**, past the image's own position, so nothing moves aside; then WLED's table, and the boot pointed at that slot. WLED starts, formats its filesystem and opens its own access point. +- **MoonBase installs it** only once the image is stamped as one of MoonLight's, a deliberate change to the one rule that refuses other images (`src/core/util/FirmwareImage.h`). +- **A "Back to WLED" button** in the Firmware card, a release asset per chip, and the bench rehearsal with a power cut at every step that the way in had. +- The WiFi stays behind: WLED opens its access point, where the user sets it. + +### The desktop build advertises no mDNS (2026-09-06) + +Kind: Feature · Platform: desktop + +`mdnsInit` on the desktop platform is a stub returning false, so a desktop instance is invisible to +discovery: it neither announces itself nor answers `.local`. On a board mDNS is what makes a +device findable without knowing its address. + +Nice-to-have rather than blocking, because instances address each other by IP and that is what the +network drivers take (`NetworkSendDriver`'s `ips` list). What it costs today is convenience: a +container on a NAS has to be looked up rather than named, and a fleet of them cannot be enumerated +the way a fleet of boards can. + +**What it costs:** a platform implementation per OS, which is the awkward part. macOS has Bonjour +built in, Linux would want Avahi (a dependency the container deliberately does not have), and +Windows has its own. A small self-contained responder is the alternative, since the announce side is +a handful of multicast records and we do not need the resolver half. + +Worth revisiting if instance-to-instance sync grows: discovery is the difference between configuring +a fleet by address and having it assemble itself. + +### The arm64 `.deb` is untested on Raspberry Pi OS bookworm (2026-09-13) + +Kind: Investigation · Platform: Pi · Stage: Test + +One piece of the arm64 verification was never done, because the bench has no such board. The job runs on +`ubuntu-22.04-arm` **deliberately**, for glibc 2.35: Raspberry Pi OS bookworm ships 2.36, and a +build on `ubuntu-24.04-arm` (glibc 2.39) would install there and then die at startup with +`GLIBC_2.38 not found`, the same failure the Dockerfile records hitting on debian12. The floor is +what makes bookworm work, and **bookworm is the one target nobody has run it on**. + +Two halves are untested together: that the binary starts (the symbol check says the highest +requirement is `GLIBC_2.35`, which is the proxy), and that `Depends: libcurl4 | libcurl4t64` +resolves there. Trixie provides only `libcurl4t64`, bookworm only `libcurl4`, and only the first +half has been exercised. A wrong alternation fails visibly at `apt install` with an unsatisfiable +dependency rather than installing something broken, which is the good failure mode. + +**A user with a Pi settles it in one command.** Closing this needs a report, not a code change. + +### A flashable SD image with MoonLight already on it (robwomp, 2026-09-08) + +Kind: Feature · Platform: Pi · Stage: Install + +Suggested on Discord while bringing up a NanoPi R28S: most Pi users want to write an `.img` to a +card and be running, not to install a toolchain. Armbian's own build tooling supports exactly this +(`armbian/os` carries per-application "extensions", OMV being a small worked example), so the image +is a spin of a maintained distribution plus our package rather than a distribution to maintain. + +Wants the arm64 `.deb` above first: with it the extension is roughly "install this package, enable +this service", which is the shape those extensions already have. Without it, the image would have to +carry a source build, which is the thing it exists to avoid. + +### MoonBase follow-ups + +Kind: Feature + +MoonBase v1 ([MoonBase](../../explanation/architecture/moonbase.md)) +ships exactly one action: install firmware (upload + URL). The name is deliberately broader than +"recovery", these are the candidate next actions, each solving something only a separate boot +image can solve, and each must earn its bytes in the slot's headroom (a few KB of code is fine; a +new IDF component is the expensive kind). + +- **Boot with config disabled**: one-shot flag the app reads at startup to skip loading + `/.config`: diagnose "is it my config or the firmware?" without erasing anything. +- **WiFi re-provisioning**: edit the stored credentials from MoonBase's page (today it only + *reads* them; the AP fallback plus the app's provisioning already covers most of this). +- **Firmware downgrade guard**: MoonBase installs whatever image it is given; a version display + (read from the incoming image's app descriptor) before flashing would make an accidental + downgrade visible. +- **Hardware diagnostics**: chip/flash/PSRAM identification and a minimal pin tester, for + triaging a board that misbehaves under the full app. +- **MoonBase Ethernet beyond classic RMII**: the P4's IP101/managed-component PHY and the S3's SPI W5500, + which matter only if MoonBase ever goes beyond the 4 MB classics. +- **MoonBase as the only update mechanism, all boards** (PO, 2026-08-26): would delete the app's + whole in-place OTA path (a real subtraction) and grow every app slot, with the stronger + power-fail story everywhere. Three deciding factors first: MoonBase Ethernet beyond classic RMII (the P4 has no + WiFi of its own and uses a different PHY; the S3 uses SPI W5500), a migration that does not lose config ([config backup and restore](backlog-core.md#config-backup-and-restore-identity-a-recipe-and-tiers-2-and-3)), and accepting + ~45 s of visible downtime per update where dual-OTA installs in the background. Trigger: + MoonBase proven in the field on the 4 MB boards. + +### LEDs on the Teensy flash white, then the board resets (2026-10-13) + +Kind: Bug · Platform: Teensy + +Five times in one session, the 64-light strip on the Teensy 4.1 bench board went full white, and the board restarted with reset reason `UNKNOWN`, no fault mark and no watchdog: a supply dip, which a full-white strip on USB power explains. +Each white flash came while the device built a large state document: switching SdCard's board with the old 46 KB full state, and subscribing to Drivers or the whole tree with the new one; none came with the LEDs disconnected. +A WS2812 strip reads a data line held high as all ones, so suspect the GPIO-DMA output stalling high, or its buffer moving under the DMA when the Layer reduces its buffer under memory pressure ("buffer reduced" was showing). +Reproduce with a logic analyser on the data pin while subscribing to Drivers, before changing code. + +### A Teensy sometimes drops a message the page sends (2026-10-13) + +Kind: Bug · Platform: Teensy + +Choosing Custom on SdCardService's board select reached the Teensy once in three tries in one session, and every time in later ones; the same clicks always reach the Dig-Octa. +A lost write leaves the page showing a choice the device never made, and nothing tells the user. +Suspects, in order: the `/wsp` preview stream filling the send side while a frame from the page waits, a long render tick delaying the poll that reads the socket, and power dips while the LEDs show full white. +Reproduce with the preview open and closed and the LEDs dark and lit, logging each frame the device receives, before changing any code. + +### SD card writes over SPI stop the card on the Dig-Octa (2026-10-12) + +Kind: Bug · Platform: S3 + +On the QuinLED Dig-Octa's SPI slot (SCK 14, MOSI 15, MISO 36, CS 16), a write past about 50 to 200 KB fails mid-transfer, and the card then answers no command until its power is cut. +Reads hold: a 5 MB file reads back byte-exact at 184 KB/s. +The same no-brand 32 GB card writes 5 MB cleanly on the native SD bus of the P4 and of the Olimex ESP32-Gateway. +Ruled out: the clock (20, 10 and 4 MHz fail alike), 30 ms between writes, and the wiring, which Quindor's schematic shows with 10 kOhm pull-ups, its own LDO and decoupling. Turning the LED driver off moved the limit a little without removing it. +Next: the same 5 MB test with an A-brand card, since SPI mode is the part of the spec newer cards treat as secondary. ESPixelStick, the one other firmware driving this slot, throttles its writes into 7 KB blocks with a 30 ms gap and pauses LED output during an upload (forkineye/ESPixelStick issue 802 and `src/FileMgr.cpp`). +Until it is solved, a card mounted over SPI shows a warning status. + +### ESP32 UDP receive is bounded by PACKET COUNT, not bytes + +Kind: Investigation + +Reported from other projects working the same ground: the ESP32 family's incoming UDP limit behaves +as a **mailbox of packets** rather than a memory budget, with a hard numerical ceiling, and the P4 +is no better. The observed shape is perfect reception up to that count and then progressively worse +loss as universes climb, rather than a clean cliff. Raising it is tunable but bounded, since the raw +packet buffers are full-frame sized whatever the payload. + +Two consequences worth having in mind: + +- **Pixels-per-packet is the lever, not bandwidth.** Art-Net is DMX512 on the wire, so it is capped + at 512 values per packet whatever the frame size; DDP fills close to a whole MTU. For the same + pixel count DDP therefore needs far fewer packets, which is the resource that runs out first. + This matches our own measurement that ArtNet is where the WiFi limit bites. +- **A P4-specific escape exists.** Have the packet handler DMA the payload to PSRAM and release the + hardware buffer immediately, then parse from another task, so the mailbox drains at memory speed + rather than at parse speed. That is real work and speculative, but it is the shape of a fix rather + than a tuning knob. + +Unverified on our own bench: this is other people's measurement, recorded so the next receive-path +investigation starts from it rather than rediscovering it. Confirm before acting on it. + +### WiFi Art-Net performance + +Kind: Investigation + +128×128 WiFi ArtNet measurements exist (see [performance.md](../../reference/performance.md) "ArtNet over WiFi" and "Build-variant WiFi comparison"). Remaining matrix: + +- WiFi STA 64×64 (4K LEDs, 24 universes) +- WiFi STA 32×32 (1K LEDs, 6 universes) + +This determines the practical LED limit for WiFi-only boards. Until the `sdkconfig.defaults` TX-buffer fix lands (identified in the build-variant table), **prefer wired Ethernet for any ArtNet workload on classic ESP32** — the default `esp32` build carries both stacks, so Ethernet is available even when the original measurements were taken on the old `esp32-eth-wifi` variant. + +### Memory ceiling on non-PSRAM ESP32 with WiFi and Ethernet + +Kind: Investigation · Platform: classic + +On the `esp32` firmware, which carries both stacks, default 128×128 grid, measured when the two were a separate variant, free heap at boot is ~28 KB — not enough for `esp_wifi_init` (needs ~16 KB RX buffers) after the light pipeline allocates ~210 KB. The device stays running but WiFi init fails silently. + +Fix options in increasing scope: +- **Cap the default grid** — drop to 64×64 on a classic board without PSRAM (Layer ~32 KB + LUT ~16 KB = 48 KB, comfortably under). Simplest. +- **PSRAM for Layer buffer + LUT** — ESP32-Gateway has 4 MB PSRAM unused on non-S3 builds. Moving the 49 KB pixel buffer + 64 KB LUT out of DRAM frees ~110 KB for radios. Cost: ~25% FPS hit (PSRAM bandwidth ~12 MB/s vs DRAM ~80 MB/s); needs measurement. See [lessons.md](../../work/past/lessons.md) "Adaptive memory allocation design" for the allocation rules. +- **Lazy WiFi init** — skip `esp_wifi_init` when `ssid_` is empty and no AP-fallback is pending. Helps only when credentials exist but the network is unreachable — niche. + +### Boot-time buffer degradation on non-PSRAM at 128×128 + +Kind: Investigation · Platform: classic + +On the Olimex (no-PSRAM) at 128×128 with a modifier, the Layer sometimes comes up **degraded** at boot — status `"buffer reduced — not enough memory"`, with a visibly wrong render (the reduced render buffer overflows what the LUT/extrude expects). **Toggling any layout control (forcing a fresh `onBuildState`) fixes it** — the rebuild allocates the full buffer and the display is correct. So this is a boot-time allocation *race*, not a code bug: the same rebuild path that fails at boot succeeds moments later. + +Measured: the full pipeline needs two ~49 KB contiguous buffers (Layer render buffer + Drivers output buffer, both 128×128×3), plus the LUT. At boot the largest contiguous block is only ~14–20 KB while the network stack / mDNS / HTTP-server buffers are still settling into the heap — so the Layer can't claim a contiguous 49 KB and degrades (halves its dimensions). A rebuild after the heap settles wins the contiguous block and allocates full. The device is at the fragmentation edge either way (~42 KB free / ~14 KB largest block at 128×128 with the full pipeline up). + +The annoyance is purely that the device boots degraded and needs a poke to recover — it should come up working. Fix options in increasing scope: +- **Allocation order** — claim the big Layer/Drivers buffers *before* the network/mDNS/HTTP buffers fragment DRAM (i.e. wire the light pipeline's `onBuildState` ahead of network bring-up in `main.cpp`). Cheapest if the ordering is safe. +- **Boot retry** — if `onBuildState` degrades, schedule one more `buildState()` after boot settles (a one-shot, e.g. after the first `loop1s` or once the network reports up). Self-healing without reordering init. +- **Cap the default grid** on no-PSRAM to a size whose two buffers fit the post-boot largest block (same lever as the memory ceiling above). +- **PSRAM for the buffers** on PSRAM-equipped variants: sidesteps DRAM fragmentation entirely (related: the [Memory ceiling](#memory-ceiling-on-non-psram-esp32-with-wifi-and-ethernet) PSRAM notes). + +Related: this is the render/output-buffer face of the same non-PSRAM fragmentation cliff the paged `MappingLUT` already addressed for the *LUT*. The buffers themselves still allocate as single contiguous blocks. + +### WiFi runtime disable + +Kind: Investigation + +WiFi initializes only when a station or the access point starts, and `wifiRadioDown` frees it when both stop, so a board that Ethernet carries holds no WiFi memory. Turning WiFi off entirely, the access point fallback included, is undesigned: whether the gate keys off detected hardware presence, an explicit control, or a deviceModel-catalog field is not decided until a concrete case asks for it. + +### Trim the TLS certificate bundle to the common roots + +Kind: Feature · Trigger: flash runs tight on a board + +`CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN` keeps the common roots only, an estimated 40 KB less flash; `_NONE` saves about 50 KB but breaks TLS. + +### Platform API: `std::span` migration + +Kind: Restructure + +Several `platform.h` APIs still use `(buf, len)` pairs where `std::span` would catch length/pointer mismatches at compile time. Concrete sites: `http_fetch_to_ota`, `improvProvisioningInit`, and friends. ~2 h including ripple updates to callers. Do alongside the next platform-API expansion (Windows socket port or POST /api/firmware streaming). + +### Ethernet on the QuinLED Dig-Next-2 (W5500 over SPI) + +Kind: Investigation · Platform: S3 + +The DN2 is in the catalog with no Ethernet block, so it comes up WiFi-only. Adding it is a `deviceModels.json` entry and nothing else: the preset work landed the seam, and W5500 already applies live. + +**RMII is impossible on this board, so the W5500 is the only route.** The DN2 carries an ESP32-PICO-V3-02, whose package leaves GPIO 16, 17, 18 and 23 unbonded, and those are the RMII management and clock pins. This is the same package fact behind the flash-cache wedge recorded in [lessons](../past/lessons.md). QuinLED's own LAN8720 board stacks onto the QuinLED-ESP32 only, not onto a Dig board. + +**The wiring, from [Quindor's firmware source](https://github.com/intermittech/WLED-Dig-Next-2_W5500)**, using the QEXT header for the bus and the Stemma QT header for the two control lines. Fitting Ethernet therefore costs the I2C port: + +| W5500 | connector | GPIO | +|---|---|---| +| SCLK | QEXT green | 25 | +| MOSI | QEXT blue | 32 | +| MISO | QEXT yellow | 0 | +| CS | QEXT white | 33 | +| INT | Stemma QT SDA | 15 | +| RST | Stemma QT SCL | 14 | + +MISO on GPIO 0 is deliberate and sound: GPIO 0 is a strapping pin, and a W5500 leaves MISO high-Z while CS is inactive, which is what Espressif's rule about not driving strapping pins at reset asks for. + +**It stays `Custom`, and that is the finding.** Across the three W5500 boards now known, LightCrafter 16, SE 16 V1 and this one, no two share a single pin on any of the six lines. SPI Ethernet is a module wired to whatever a board left free, so there is no W5500 pattern to make a preset from, and inventing one would be a preset that fits nothing. + +**What is unverified.** The pin table is trustworthy as what that firmware does, because it was read out of the code rather than a description. What rests on one source is that it works: a single-author repository created 2026-09-19, self-labelled experimental, tested on one unit, with LED output, buttons, relays and mic explicitly not exercised. No independent build was reported when this was written. So the open question is not the pins but whether SPI Ethernet and the LED output coexist on this board under load, which is a bench answer. + +### Link-time optimization on the ESP32 builds + +Kind: Investigation + +Splitting `HttpServerModule` into three files cost 7,360 bytes on the classic ESP32 (2,129,216 to 2,136,576), the compiler no longer inlining across the files. Link-time optimisation (`-flto`) optimises the whole program at the link and wins that back, and likely more across the firmware. ESP-IDF's current docs offer it as `CONFIG_COMPILER_LTO_LINKTIME`; v6.1, which MoonLight builds with, has no such option. Try it once the ESP-IDF release MoonLight moves to offers it, per board: the image size, the IRAM placement in the map file (an interrupt handler must not inline code from flash), a run on the RMT and parallel drivers, and the build time, which LTO makes two to three times longer. + +### Adopting the v6.x ecosystem changes + +Kind: Investigation · Trigger: each v6 item's own trigger fires + +The as-is state of each item is the table in [build-system.md § The v6 ecosystem](../../reference/build-system.md#the-v6-ecosystem); this entry carries the plan and the triggers. + +**Per item, how and when:** + +- **EIM** — adopt any time. It shipped *in* v6.0 so it clears the v6.0-floor rule, and it has a headless CI mode. Add EIM as the **preferred** path in `setup_esp_idf.py` (`eim install`), keep `install.sh` as a documented fallback for one release. Keep the exact-commit pin: EIM's multi-version management helps reproducibility, it does not replace the pin. +- **PSA Crypto** — nothing to migrate while we stay on high-level components. Watch only: if a feature needs hashing or signing directly (signed-OTA verification, a device identity), write it against the **PSA API** from the start, not legacy mbedTLS. Trigger: first direct crypto use. +- **`network_provisioning`** — adopt to close the phone-app + SoftAP gap. It is in v6.0 (clears the floor) and is the IDF-native standard. Add it as a **sibling provisioning module** beside ImprovProvisioning (both wired-by-code System modules; a device offers whichever transports its chip supports), reusing the same WiFi-credential plumbing. Not a replacement for Improv: they cover different front-ends (browser vs phone-app) and a product can want either. Weigh the BLE-stack cost per chip. Spec before code. +- **CMake Build System v2** — watch until GA, not while it is a preview: adopting a preview build system is the opposite of common-patterns-first. Trigger: v2 ships as the default. Then dry-run a build under v2, fix any `idf_component_register()` / Kconfig fallout, switch. Low risk given how little custom CMake we have. +- **Built-in MCP server** — evaluate, do not default to it. The risk is a *second control path* bypassing the script policy, and it is ESP32-only (no desktop), so it cannot be the uniform path. If adopted, wrap it behind a script (`moondeck/run/idf_mcp.py`) so the policy layer still applies. Trigger: a concrete debug workflow the scripts cannot cover. + +**Sequence.** Close the v6.0 gaps one at a time as normal feature commits: + +1. **EIM installer** — rework `setup_esp_idf.py` to prefer `eim install`, keeping `install.sh` as a one-release fallback. Smallest and lowest-risk (build-path only, no firmware change, no hardware re-test), and EIM's multi-version management is what cleanly supports the v6.0-floor / v6.1-fallback juggling, so it sequences first as an enabler. +2. **`network_provisioning`** — the headline capability: the phone-app + SoftAP onboarding flow. Its own plan. +3. The rest (PSA-native crypto, CMake v2, MCP) as their triggers fire. + +**The pin's drift, shown in MoonDeck.** The pin and its policy live in [build-system.md § ESP-IDF version](../../reference/build-system.md#esp-idf-version); its drift warning shows only while `setup_esp_idf.py` runs, so a MoonDeck banner or status dot surfacing the same drift is still to do. + +**Guardrails.** Platform-generic stays intact: these are ESP32-specific gains and none may regress Teensy or the desktop paths, which do not use ESP-IDF at all. An IDF feature is adopted *inside* the ESP32 platform layer / build tooling, never by leaking an IDF assumption into shared `src/` or the desktop build. And the v6.0 floor holds: adopt only what is in v6.0, so the v6.0 fallback keeps working. + +### ESP32-P4 WiFi through the C6 co-processor + +Kind: Bug · Platform: P4 + +Rounds 1 (board and Ethernet-only) and 2 (Parlio LED driver) have landed; WiFi through the C6 is what remains. + +Issue #146 (a P4 `eth-wifi` boot loop) was fixed without anyone seeing WiFi work: the bench P4-NANO's C6 never answers on SDIO (`ESP-Hosted link not yet up`, no `Card init success`), with or without the fix. So every P4 WiFi question waits on a reporter's board. The textbook cause of a silent C6 is a host/slave mismatch: our host side is ESP-Hosted 2.12.13, built into the firmware, while the C6 runs whatever slave firmware the board maker flashed. **Make our own P4-NANO's link come up, then keep it as the WiFi reference.** + +- **The slave image**: `network_adapter.bin` built from esp-hosted-mcu's `slave` example at the host's version (2.12.13), for the C6. +- **Flashing it, with the link down** (our case): directly over the C6's own UART with its boot pin held, through an ESP-Prog or a USB-serial adapter. The P4-NANO's schematic says where the C6's UART and boot pads are; ESPHome's board page names only the SDIO pins (`clk` 18, `cmd` 19, `d0`-`d3` 14-17) and the C6 reset (`GPIO54`). +- **Flashing it, with the link up**: ESP-Hosted's transport OTA, its `host_performs_slave_ota` example, which ESPHome wraps as the `esp32_hosted` update platform. This is the path for users once one board works, and a candidate for MoonLight itself: carry the slave image and offer the update on the WiFi card, as ESPHome does. +- **Say which failure `wifiCoproc` shows**: `no version reply` covers two cases, a link that never came up and a C6 too old to answer the version query (or answering 0.0.0), and only the first stops WiFi. `coprocessorWifi()` in `platform_esp32.cpp` checks the transport state first and reports `link down` or `C6 too old to report`, so a reporter's System card answers what today needs a serial log. +- **Then**: the round-3 regression (associates on rc1, not on the v6.1 pin) gets measured on a board with a known-good link, and the two defects of the cascade below get a repro. + +What #146 taught, for any P4 change: before the scheduler starts, the P4 has 148 KB of internal RAM (retention, RTC and SPM; the 384 KB L2 region still holds the startup stacks), and every global constructor allocates from it. ESP-Hosted's took 123 KB until `CONFIG_ESP_HOSTED_MEMPOOL_PREFER_SPIRAM` moved its buffers to PSRAM. A boot that asserts in `vApplicationGetIdleTaskMemory` is that budget exhausted; a `heap_caps_get_free_size(MALLOC_CAP_INTERNAL)` report from a constructor of priority 101 and an `ESP_SYSTEM_INIT_FN` at stage 999 brackets it. + +#### Round 3: association and the slow link + +- **Round 3 regression: WiFi associates intermittently on IDF v6.1 (2026-10-12; the PO sees it associate on some boots and not on others, 2026-10-16).** The variant keeps shipping so users can try it. It associated and served traffic on IDF v6.1-rc1 (below). The bench P4-NANO (v1.3, port `usbmodem5ABA0767291`, registry .139) flashed with `esp32p4rev1-eth-wifi` on the v6.1 pin, Ethernet unplugged: Improv delivered the bench SSID over USB serial, the board answered "unable to connect", and it never appeared on the network. Its log does not reach `monitor_esp32` on that USB-JTAG port, so the cause is unread. First step: read the P4's console (UART pins with an adapter) for the esp_hosted / C6 lines, then compare against the rc1 build that worked: the IDF move from rc1 to v6.1 and the esp_hosted version it pulls are the two candidates. +- **Round 3, WiFi via the C6 co-processor: the link is slow (2026-08-19).** Boots and associates on IDF v6.1-rc1; the remaining defect is throughput, bench-bisected below. How the variant is built: [lessons](../past/lessons.md#esp32-p4-round-3-wifi-via-the-c6-the-abstraction-the-earlier-round-feared-wasnt-needed). + + **Open issues before this is done:** + + 0. **The esp_hosted build is slower on HTTP — BISECTED to the hosted link, not to us (2026-08-19), and much improved but NOT gone (re-measured 2026-08-21).** + + **Original (2026-08-19):** Same board, same commit, same application code, measured over **Ethernet on both builds** so the radio is not in the path: + + | build | per-request (`/api/system`) | throughput (73 KB `app.js`) | + |---|---|---| + | `esp32p4rev1-eth-wifi` | alternating 0.434 / 0.788 / 0.446 / 0.813 s | 42 KB/s | + | `esp32p4rev1-eth` | 13 ms flat, 12 consecutive fetches | 708 KB/s | + + Render is healthy in both (121-134 fps), so this is not frame-loop contention. Because the two images share every line of application code, the penalty is **esp_hosted being compiled in and its SDIO link serviced**, degrading traffic on an interface it is not even carrying. The cost is **per-request, not per-byte** (a 1 KB fetch cost nearly as much as a 36 KB fetch), which points at a periodic blocker a request must wait out rather than a slow pipe. + + Already excluded on the bench: SDIO width/clock (already 4-bit/40 MHz, the maximum), WiFi buffer counts (match the S3's), ICMP latency (6.6 ms). An earlier `SystemModule` fix (`coprocessorWifi()` retried an unanswered version query every tick: 1,012,344 us → 257 us) was real and is kept, but it was **not** this: the stutter outlived it and vanished only when the C6 path left the image. + + **Per-module tick timing, both builds on the SAME Ethernet interface (2026-08-19).** The measurement that says what this is NOT: + + | module | eth-only | eth-wifi | factor | + |---|---|---|---| + | HttpServer | 113 us | 3441 us | **30x** | + | File Manager | 279 us | 4686 us | 17x | + | Services | 242 us | 1388 us | 5.7x | + | Audio | 239 us | 1376 us | 5.7x | + | Effects | 172 us | 397 us | 2.3x | + | **Network** | 10 us | **92 us** | (negligible in absolute terms) | + | Drivers | 6601 us | 4215 us | **0.64x (FASTER)** | + | ParallelLed | 7186 us | 6569 us | 0.91x (faster) | + + **Two theories are refused by this table.** It is not a blocking WiFi call in `NetworkModule` (`Network` costs 92 us, and `wifiStaRssi`/`wifiTxPower` are state-gated off entirely in `ConnectedEth`); and it is not periodic WiFi scanning, since no module carries anything like a 0.37 s cost. Instead **modules that touch no network at all** (Audio, Noise, Effects, Layer) slow by the same kind of factor as the HTTP path: a broad, roughly proportional slowdown of ordinary code. + + **Working theory: L2 cache contention.** Both builds are byte-identical in every memory setting (`CACHE_L2_CACHE_128KB`, `SPIRAM_SPEED_200M`, `FLASHFREQ_40M`, `SPIRAM_MODE_HEX` — a full sdkconfig diff shows no cache/PSRAM/flash/CPU-freq difference), and code executes from flash through that shared 128 KB L2 in both. Adding esp_hosted's tasks, ISRs and DMA buffers evicts application code that was previously resident, so ordinary work starts missing to flash/PSRAM. This also explains the otherwise odd inversion: the DMA-bandwidth-bound `Drivers`/`ParallelLed` got *faster*, which is what happens when the CPU competes less for the same bus. **Consistent with the data, not yet proven** — separating cache contention from plain CPU stealing needs a cache-hit-rate counter or a per-tick histogram, neither of which we expose today. + + **The cache theory does NOT explain the visible LED hiccup, and there are TWO effects here (PO observation, 2026-08-19).** Uniform cache contention predicts a smooth frame-rate drop (133 → 96 → ~73 fps), which is what the averages show. It does not predict a *stall*, yet a once-per-second hiccup is plainly visible on the fixture. So the throughput loss and the hiccup are separate problems and must be chased separately. + + **Our instrumentation structurally cannot see the hiccup.** `MoonModule::tickTimeUs_` is a MEAN (`accumUs_ / frameCount`, [MoonModule.h:656](../../src/core/module/MoonModule.h)) and `Scheduler::fps()` is derived from it, so a single 300 ms frame among 70 good ones shifts the average ~4 ms and vanishes. Every per-module number in the table above is an average and none of them can confirm or refute a stall. External evidence of the stall is therefore weak but non-zero: sampling `fps` once per second for 30 s gives a steady 73 with periodic dips to 69-70 (~55 ms lost in those seconds), and the dips are NOT on a clean 1 s period. Ruled out along the way: the degradation is not caused by our own HTTP polling (fps is identical at 76 after 25 s of zero traffic and under continuous polling), and the early 96 → 73 decay is warm-up, not load. + + **Per-TASK CPU, both builds, `--task-cpu-stats` (2026-08-19) — this is the decisive measurement.** Same profiling overhead on both, so the comparison is clean: + + | task | prio | eth-only | eth-wifi | | + |---|---|---|---|---| + | `main` (render) | 1 | **35.4%** | **93.3%** | the same work costs 2.6x the CPU | + | `IDLE0` | 0 | 63.3% | 5.1% | core 0 headroom is gone | + | `ipc1` | 24 | 7.8% | 12.7% | present in BOTH, so not the cause | + | `mmEncode` | 5 | 3.4% | 6.0% | | + | `sdio_read` / `sdio_write` / `sdio_process_rx` / `rpc_rx` / `rpc_tx` | 23 | absent | **0.0-0.1%** | the hosted tasks are IDLE | + + **What this refutes.** Not a busy WiFi task: every SDIO/RPC task sits at 0.0-0.1%. Not periodic WiFi scanning, and not a blocking call in our render path either, since `main` is not *waiting* — it is *running*, and burning 2.6x the cycles for identical work. Not `ipc1` (priority 24, preempts everything), which is present on the eth-only build too. There is no WiFi *activity* to stop, so a "compile it in but don't run it" variant would likely change nothing; the cost is already there with the link idle. + + **What this supports.** The same instruction stream executing 2.6x slower with no extra runnable work is the signature of **memory contention**, consistent with the L2-cache theory above: esp_hosted's footprint evicts application code from the shared 128 KB L2, so ordinary code stalls on flash/PSRAM fetches. Cycles are spent *inside* `main`, which is why every module slowed proportionally and why the DMA-bound drivers (bandwidth-bound, not cache-resident) got faster. + + **Three more theories died on the bench (2026-09-29, IDF v6.1 final).** Each was tested against 30 consecutive `/api/system` fetches over Ethernet, so the radio carried nothing in any of them. + The FreeRTOS tick warning esp_hosted prints at boot (`CONFIG_FREERTOS_HZ is 100, ESP-Hosted recommended 1000`) is an unconditional nag rather than a measurement, and the arithmetic runs the wrong way: IDF's `usleep` busy-waits sub-tick intervals exactly, so the first eight TX retries cost 14.4 ms at 100 Hz against 17.2 ms at 1000 Hz. + Priority inversion is refuted by its own null result: raising our task from 1 to 10 changed nothing measurable, and IDF's own `esp_http_server` runs at priority 5, which would be equally starved if six tasks at 23 were the mechanism. + Nagle with delayed ACK looked compelling, since responses are written as a header then a body with no `TCP_NODELAY` and the median penalty is close to a 40 ms delayed-ACK timer, but setting it moved neither build: eth-wifi stayed at ~55 ms and eth-only was already 8-44 ms. + `CONFIG_LWIP_TCPIP_TASK_PRIO=23` is kept, on the grounds that level-with rather than below is defensible, NOT because it is proven: one run lost its 230-270 ms tail spikes and a later run saw them return. + + **What the penalty actually costs a user, measured per interface (2026-09-29, both fixes in, 30 fetches of `/api/system` each).** + + | build and link | median | spikes | + |---|---|---| + | eth-only, Ethernet | **14 ms** | none | + | eth-wifi, Ethernet | 51 ms | 3 of 30 at ~200 ms | + | eth-wifi, WiFi (RSSI -44) | 57 ms | 4 of 30 at 280-870 ms | + + Over Ethernet the eth-wifi build is DEGRADED rather than unstable, and an earlier note here calling it unstable on every interface was wrong. + A click lands in 51 ms where a person notices around 100, so the interface feels fine and the 3.6x is invisible for single actions. + It surfaces in two places: the ~200 ms outlier roughly once in ten requests, which a page load of eight parallel requests will usually hit, and any client that issues hundreds of requests, which is why the live scenario sweep failed 4 of 6 on this build over Ethernet while a human clicking around noticed nothing. + Over WiFi the median barely moves but the tail doubles and reaches 870 ms, and under scenario load it reaches multi-second timeouts. + So the honest split is: usable on a wired link, unreliable on a wireless one, and the eth-only build is the one to run when a P4 has to be dependable. + + **The interface is noticeably quicker than it was (PO observation, 2026-09-29).** Changing an effect over WiFi went from a visible hang to something that responds, which a fetch loop cannot measure: that gesture is a POST plus a tree rebuild plus a state resync, so it pays the tail several times over where a single GET pays it once. + THREE things changed together and no single one is credited: `TCP_NODELAY` on accepted connections, `CONFIG_LWIP_TCPIP_TASK_PRIO=23`, and a board no longer carrying a failed scenario's leftover driver. + That last one also taints the earlier "20-second timeouts on WiFi" figure recorded here, which was measured on a board in that state; the 4x penalty over Ethernet is clean and reproducible, the WiFi instability was not measured as carefully. + + **Why the co-processor board and not the others.** A native-radio chip carries its WiFi stack in the image the cache was sized around; the P4 has no radio, so WiFi arrives as an additional resident subsystem (six tasks, an SDIO driver, RPC machinery, DMA buffers) on a chip whose 128 KB L2 is shared by everything. The difference is not silicon quality but whether WiFi is extra code competing for that cache. + + **Still unexplained: the visible ~1 s LED hiccup.** Cache contention predicts the steady 2.6x, not a stall. Per-task CPU is cumulative-since-boot and cannot show a spike either. So the hiccup remains unmeasured, and the worst-case instrumentation below is still the next step for it specifically. + + **Instrumentation gap to close first — worst-case tick tracking.** Add a per-module and per-scheduler *max* (and ideally a coarse histogram or a "frames over 2x mean" counter) alongside the existing mean, and expose it in `/api/system`. Cheap (one comparison per tick), and it converts "the PO can see a hiccup that no number shows" into a measurable quantity. Without it, every theory below is unfalsifiable from the host side. Reading the P4 console needs `CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y` or a UART adapter on GPIO 37/38 (see the dev-loop note in round 4), so serial is not the fast path. + + Then, cheapest first: (a) read the new max/histogram to see whether the cost is uniform or spiky (uniform favors cache contention, spiky favors a blocking call, and the two coexisting is now the leading reading); (b) the P4 cache performance counters for a direct miss-rate read; (c) moving the hot render path to IRAM/internal RAM and re-measuring, which is also a candidate fix rather than only a diagnostic. + 1. **Runtime SDIO re-init of the C6 fails: CONFIRMED a C6 slave-firmware problem (not a guess).** SystemModule now exposes a `wifiCoproc` read-only control (via `platform::coprocessorWifi()` → `esp_hosted_get_coprocessor_fwversion()`), and on the bench it read **`not detected`** at the time (the control now reports **`no version reply`**, and only after a bounded number of attempts): the C6 returns no valid firmware version. **What that means was overstated here.** It was read as the signature of absent / incompatible slave firmware, but the same board later associated and served traffic over that very link while this RPC still went unanswered, so an unanswered version query says the QUERY failed, not that the C6 is absent. The round-3 conclusion below rests on the `sdmmc_card_init failed` re-init evidence, not on this control. Likely a version mismatch on top of that: The host pulled esp_hosted **2.12.9**; Espressif's P4-Function-EV-Board ships its C6 pre-flashed with esp_hosted slave **v0.0.6**, and the **Waveshare NANO is a different board that may carry a different / absent C6 slave image**. The symptom fits: boot inits the host SDIO master fine, but resetting the C6 (GPIO 54) and re-enumerating it as a slave fails (`sdmmc_card_init failed`) because the C6 has no compatible slave firmware responding. **Primary next step: build + flash the version-matched esp_hosted slave firmware onto the NANO's C6.** The slave project is already vendored at `esp32/managed_components/espressif__esp_hosted/slave/` (`sdkconfig.defaults.esp32c6`, `partitions.esp32c6.csv`); `idf.py create-project-from-example "espressif/esp_hosted:slave"` → `set-target esp32c6` → flash. **Caveat / needs PO + bench hardware:** flashing the C6 on the EV board uses an **ESP-Prog wired to the `PROG_C6` header** with the P4 held in bootloader mode (esp_hosted `docs/esp32_p4_function_ev_board.md` §5.2): the NANO's C6-flash path must be confirmed (separate USB? equivalent header? ESP-Prog?), and an ESP-Prog may be needed. An OTA slave-update path exists but needs a *working* link first (chicken-and-egg here). This is a hardware-provisioning task, not application code. Secondary fallbacks if firmware-match doesn't fix it: an esp_hosted option to skip the reconfigure/slave-reset when the transport is already up at boot; a slower SDIO freq or 1-bit mode; verify GPIO 54 reset polarity/timing for the NANO. **(Note: EIM, the build-system v6 ecosystem item, does NOT help here; it's a host-machine installer, unrelated to device-side C6 firmware.)** + + **User lead (2026-07-09): avoid the WiFi teardown/re-init on hosted targets entirely; it may sidestep the slave-reset failure.** A user hit the mirror symptom on a *different* codebase (ESP32-Sveltekit / WLED-MM, not MoonLight — its `lib/framework/WiFiSettingsService.cpp` doesn't exist here): after a clean C6-flash the P4-NANO WiFi worked, but their app's boot-time `WiFi.disconnect(true)` fully tore down the WiFi stack, and the later AP bring-up then failed with `esp_hosted_transport_config: Transport already initialized` / `esp_hosted_init failed!` / `AP enable failed!`. Their working fix: on `CONFIG_ESP_WIFI_REMOTE_ENABLED` (= hosted) targets, **don't do a full stack reset** — keep STA enabled, reconnect *without* a full teardown/re-init, and never call `WiFi.disconnect(true)` in the disconnect callback. **Why this is relevant to us even though the file differs:** our own comment at `platform_esp32.cpp:796` already documents that the esp_hosted transport is set up **once at boot** and is fragile to re-init (`connect_to_slave` = a transport reconfigure that resets the slave + re-inits SDIO and fails on a live link). Our disconnect *callback* is already safe (`wifiEventHandler` on `STA_DISCONNECTED` only sets a flag — no teardown), BUT our **failover path is not**: `wifiStaStop()` (`platform_esp32.cpp:915`) calls `esp_wifi_deinit()`, and the STA-retry / AP-fallback then re-runs `ensureWifiInit()` → `esp_wifi_init()` — the exact deinit→reinit cycle that on a hosted target triggers the GPIO-54 slave reset (round-3 open issue 1's `sdmmc_card_init failed`). So the round-3 failure ("runtime SDIO re-init of the C6 fails") and this user's report may be **the same root cause**: the re-init shouldn't happen at all on a hosted target. **Concrete next step to try on the bench:** guard the teardown/re-init on `platform::hasWifiCoprocessor` (already defined = `isEsp32P4 && hasWiFi`) — on hosted targets, do NOT `esp_wifi_deinit()` in `wifiStaStop()` and do NOT re-`esp_wifi_init()` in `ensureWifiInit()` once the boot-time init is up; instead just `esp_wifi_disconnect()` + `esp_wifi_set_config()` + `esp_wifi_connect()` (STA retry) or `esp_wifi_set_mode(APSTA)` for the fallback, reusing the live transport. This is cheaper than the C6 reflash and independent of #18759, so it's worth trying first once the board boots. If it works, it also removes the slave-reset from the normal failover, not just the AP case. Blocked behind the #18759 boot crash like everything else P4-WiFi, but this is the first thing to try when the board boots again. + +#### The cascade into a dead co-processor link aborts the board (2026-09-08) + +Bench, MHC-WLED ESP32-P4 shield on `esp32p4rev1-eth-wifi`, no Ethernet cable: the NetworkModule +cascades to WiFi, and the board reboots every ~22 s with `task_wdt: main (CPU 0)` while `IDLE0` +runs. The main task is not spinning, it is BLOCKED: on the P4 every `esp_wifi_*` call is forwarded +over SDIO to the ESP32-C6 by `esp_wifi_remote`, and this shield's C6 never completes the ESP-Hosted +handshake (`E H_API: ESP-Hosted link not yet up` at boot), so `esp_wifi_init` and the calls after it +each wait out their timeout, on the render thread, past the 5 s watchdog. The wait is +esp_hosted's `transport_drv.c` slave-ready loop: 200 ms polls, a slave reset every 50 of them (the +`Reset slave using GPIO[54]` lines at 10 s and 21 s), up to MAX_RETRY_TRANSPORT_ACTIVE = 100 +polls, so worst case ~20.0 s in the caller's task, which is ours. Not the HWLOOP/FFT +erratum: audio was ruled out by the watchdog text itself (a blocked main task with idle running, +not a spinning core). Control experiment, same image on the bench P4 (.139, same shield model, +esp_hosted host 2.12.13): its SDIO card init succeeds (`Card init success, TRANSPORT_RX_ACTIVE`), +it never associates either, but the main task keeps ticking, it falls back to its own AP, and it +rejoins Ethernet when the cable returns. The new shield never prints a card-init success: its C6 +does not answer the bus (no slave firmware, or one that does not match the 2.12.x host). + +Two defects are ours, whatever the C6 carries: + +1. **The cascade does not consult the link.** `coprocessorWifi()` in `platform_esp32.cpp` already + asks the C6 for its firmware version, bounded to two attempts, exactly to detect an absent or + incompatible slave. The WiFi cascade never asks; it walks straight into `esp_wifi_init`. Gate the + cascade on that answer (or on `esp_hosted` reporting the link up) and degrade to "no network, + C6 not answering" as a Network status. +2. **A failing cascade must not abort.** Robustness says degrade visibly, never crash. Even with the + gate, a link that dies later would hit the same watchdog: the forwarded calls need to run off the + render thread, or with a timeout shorter than the watchdog, so the worst case is a status line. + +Practical today: the shield runs the eth-only image without WiFi, or its C6 gets the ESP-Hosted slave +firmware flashed (the `ships: false` note on the variant in `build_esp32.py` says why that is not +yet reproducible). The Improv script's eth-only rule and the catalog's two firmwares for the shield +are already in place. + +**Everything tried on 2026-09-08 was reverted; both P4s now run `esp32p4rev1-eth` and are stable.** +The tree is back to its pre-attempt state except for the catalog, which now lists BOTH P4 firmwares +for the MHC-WLED shield so the installer offers the WiFi variant at all (it listed only the eth one, +which is why the WiFi image could not be picked). What the day established, so the next attempt does +not repeat it: + +- **The C6 link is INTERMITTENT on this shield, not simply dead.** After the vendor C6-update tool + ran (it never completed an OTA: it looped on "Not able to connect with ESP-Hosted slave device"), + the link came up and the board reached a DHCP lease on WiFi, first attempt, no retries. A power + cycle later it was back to `sdmmc_send_cmd returned 0x107` and a reboot loop. Any future fix has + to survive a cold boot, not one lucky session. +- **A patient STA retry (MoonLight's 5 s cadence, ~2 min budget) was implemented and reverted.** It + never fired on either board: both connected on the first attempt when the link worked, and when it + did not the board died before the render loop. Keeping untested robustness was not worth the + surface. The reasoning still holds and is worth redoing WITH a repro: MoonLight retries forever + and never tears the radio down, and its own comment warns that toggling WiFi on a co-processor + board forces costly esp_hosted reinit cycles, which is exactly what our AP fallback does. +- **Espressif does not endorse retrying into readiness.** Their esp-hosted troubleshooting puts + "not able to connect with slave" and SDIO 0x107 down to wiring, pull-ups, signal integrity, or a + host/slave VERSION MISMATCH: "use the same version for master and slave". Our host is 2.12.13; the + shield's C6 is on the factory build the vendor tool calls 0.0.6 (target 2.0.17). Updating the C6 + needs Method 2 (direct USB/UART to the C6), since Method 1 needs the very link that is broken. + +**A fail-fast gate was tried on the bench (2026-09-08) and REVERTED.** The idea was right and both +signals were wrong. `platform::wifiHardwareReady()` gated the STA and AP init, and it STOPPED the +reboot loop dead: the shield ran 80 s with 0 reboots and 59 ticks, logging "WiFi co-processor link +not up" instead of aborting. But on the bench P4 (.139), whose link demonstrably works, the same +predicate read FALSE at its 26 s cascade and refused WiFi on a healthy board. Two signals were +tried, neither is usable as a readiness test: + +- **`ESP_HOSTED_EVENT_TRANSPORT_UP`**, subscribed on the default event loop (both lazily and from + `ensureNetifInit`, i.e. before esp_hosted's task posts it). Never observed arriving. esp_hosted + posts through its own `g_h.funcs->_h_event_post` indirection; where that lands was not chased. +- **`esp_hosted_get_coprocessor_fwversion` returning a non-zero version.** Also false on .139, which + matches what `coprocessorWifi()` already records: on a live link this RPC times out rather than + answering, which is why that function is bounded to two attempts. + +**Not a regression: the RELEASED v4.0.0 image fails the same way on this shield** (bench, same day). +Flashed from the web installer after an erase, it logs `App version: v4.0.0` and then: + +``` +W (13438) H_SDIO_DRV: Reset slave using GPIO[54] +W (13438) gpio: conflict found for GPIO[54] +E (14988) sdmmc_io: sdmmc_io_rw_extended: sdmmc_send_cmd returned 0x107 (ESP_ERR_TIMEOUT) +E (14988) H_SDIO_DRV: failed to read registers +``` + +and reboots without ever reaching the render loop. The same v4.0.0 image serves WiFi on the bench +P4 (.139) with no SDIO error and no GPIO 54 conflict, and no MoonLight config on any P4 entry +references GPIO 54 (esp_hosted drives it as the slave reset). The two boards differ in silicon +revision: this shield is chip rev **v1.0**, .139 is **v1.3**. So the C6 side of this shield does not +come up, which is a board/slave-firmware matter rather than anything in our WiFi path, and the +firmware's job is only to degrade rather than reboot. + +**The product owner reports this same shield ran WiFi under MoonLight on IDF 5.5**, which makes a +dead C6 unlikely and points at host-side SDIO configuration on 6.1. What was checked (2026-09-08): + +- The SDIO data pins are NOT board-preset dependent: they come from the SLOT choice + (`ESP_HOSTED_SDIO_SLOT_1`, fixed silicon pins), so swapping `ESP_HOSTED_P4_DEV_BOARD_*` presets + would not move them. The preset mostly moves SPI pins, which we do not use. +- `ESP_HOSTED_SDIO_GPIO_RESET_SLAVE` defaults to **54 on any P4** regardless of preset, so the + `gpio: conflict found for GPIO[54]` line is esp_hosted resetting the slave twice, not a wrong pin + from our config. No MoonLight P4 entry references 54. + +**Our SDIO configuration is not the difference.** Diffed against a known-good local reference +(`ewowi/FlowFields/sdkconfig.esp32-p4`, an IDF 5.5-era P4 build), every hosted setting is IDENTICAL: +pins (CLK 18, CMD 19, D0-D3 14-17), `GPIO_RESET_SLAVE` 54, `RESET_ACTIVE_HIGH=y`, `CLOCK_FREQ_KHZ` +40000, `SLOT_1`, `4_BIT_BUS`, `RESET_DELAY_MS` 1500, `RX_STREAMING_MODE`. So reset polarity, reset +pin and SDIO clock are all ruled out as differences, and the identical released v4.0.0 image works +on .139 and not on this shield. Same firmware, same config, two boards, two outcomes: what remains +is on the board side (C6 slave firmware, power/strapping, or the SDIO traces on this revision), and +the only MoonLight work left is the degrade-instead-of-reboot fix above. + +What esp_hosted's private `is_transport_tx_ready()` reports is the signal the slave-ready loop +itself polls, but it lives in a PRIVATE include dir (`host/drivers/transport`, not in the +component's `pub_include`), so reaching it means either adding that dir to our include path or +asking upstream to export a readiness getter. That is the next thing to try, and it needs the +.139-still-associates control run alongside the shield-stops-rebooting one: the fix is only right +when BOTH hold. + +#### Reading the P4's runtime log over USB + +The P4-NANO's primary console is **UART on GPIO 37/38** (`CONFIG_ESP_CONSOLE_UART_DEFAULT`), not the USB port, so `ESP_LOGI` / `mm_net` lines are *not* visible over `/dev/cu.usbmodem*` by default — only the ROM boot banner and `std::printf`-to-stdout (which routes to the **secondary** USB-Serial-JTAG console) come through. Two workarounds when you need the runtime log over USB: (a) temporarily set `CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y` (note the JTAG endpoint re-enumerates when the app starts, so a reader must reconnect across the drop — `idf.py monitor` handles it; a plain fixed `pyserial` handle dies); or (b) hang a USB-UART adapter on GPIO 37/38. This cost real time during the P4 no-DHCP hunt; the fastest signal there turned out to be a `printf` of the runtime struct (stdout → secondary JTAG console) plus a `git worktree` bisect (build an old commit, flash, check LAN reachability) to prove code-vs-hardware without needing the log at all. + +### ESP32-P4 Parlio: a real long WS2812 strip + +Kind: Investigation · Platform: P4 + +A real *long* WS2812 strip, not just the bench panel, is the remaining hardware proof of the Parlio driver. The loopback self-test caps its frame to `ParallelLedDriver::kLoopbackTestLights`, which its declaration explains. + +### ESP32-P4 DHCP hostname not shown by the router (recheck later) + +Kind: Investigation · Platform: P4 + +the device sets its DHCP hostname (option 12 = `deviceName`, default `MM-XXXX`) in the `ETHERNET_EVENT_CONNECTED` handler, verified working on two boards: the S3 over WiFi (router shows `MM-70BC`) and the Olimex over RMII Ethernet (`MM-BD3C`) — the *same* `ethEventHandler` code path the P4 uses. Yet the bench P4 (Waveshare P4-NANO, RMII) still shows as blank/"Unknown" in the GL.iNet client list, while serial confirms `set_hostname` succeeds with no error. Two unconfirmed suspects, neither our logic: (1) the router holds a **sticky lease** for the P4's MAC and won't relearn the hostname until it fully expires (the per-client "forget" isn't exposed in this GL.iNet UI, and a plain reboot didn't clear it); (2) a P4-specific IDF netif quirk serializing option 12 differently on the newer P4 Ethernet path. Since the shared code path is proven on two other boards, this is not treated as a code bug. Recheck after the P4's lease naturally expires, or on a different router, before spending more on it. + +### ESP32-P4 400 MHz on rev-3+ silicon + +Kind: Feature · Platform: P4 + +**Found:** bench, 2026-07-25 (Quindor Discord question about the P4's 400 MHz). + +The P4 build runs at **360 MHz** because IDF's `Kconfig.cpu` caps a `SELECTS_REV_LESS_V3` build (which we set, so a stock binary boots on the v0.x/v1.x P4 chips in the field) at 360; 400 MHz is IDF's default only for rev ≥ 3. Forcing 400 on our bench P4 (a **rev v1.3** chip) was tried and **bootloops** — `assert failed: esp_clk_init clk.c:105` — so 400 is a genuine hardware limit on pre-rev-3 silicon, not marginal stability. The ~11% compute headroom is left on the table for rev-3+ owners. + +The rev-3 image exists: `esp32/sdkconfig.defaults.esp32p4rev3` sets `CONFIG_ESP32P4_REV_MIN_300`, so the bootloader refuses a pre-rev-3 chip. What remains is `CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_400` in that fragment, which waits until a rev-3 P4 on the bench proves 400 runs clean: no untested clock config, per the same rule the S31/320 and this P4/400 bootloop both taught. + +### LED output from a Raspberry Pi's own pins (robwomp, 2026-10-11) + +Kind: Feature · Platform: Pi + +Suggested on Discord after the Teensy port: a Pi runs MoonLight today as the Linux desktop build, so its LEDs are reached only through the network drivers. +Driving a strip from the Pi's own header needs the platform backend that does not exist yet (`src/platform/rpi/`, the same prerequisite as the sensor item below), and a `LedPeripheral` behind it. +The routes differ by model: on a Pi 4 and earlier, DMA through the PWM, PCM or SPI peripheral, the way rpi_ws281x does; on a Pi 5 the header sits behind the RP1 chip, whose PIO blocks are the route. +SPI is the one path every model shares, one lane per SPI bus with each WS2812 bit encoded in SPI bits, and the place to start. +Portable GPIO libraries (WiringPi, libgpiod) are too slow for LED timing, so they serve buttons and relays, not this. +On a Pi 5, own PIO programs reach the hardware through the kernel's PIO driver, which piolib shows; Adafruit's PioMatter proves HUB75 that way, and HUB75 on a Pi is a second backend once this one exists. +The research, with sources: [the LED-driver analysis](analysis-leddriver.md#led-output-from-a-raspberry-pis-own-pins). + +### SE16 / LightCrafter power monitoring: sense pins free, module still to build (2026-09-02) + +Kind: Feature · Platform: S3 + +Both boards carry voltage and current sensors. MoonLight records the SE 16 V1's pins +(`MoonBase/Modules/ModuleIO.h`, board presets): **voltage GPIO 8, current GPIO 9**; the LightCrafter's are on [its page](../../reference/hardware/lightcrafter-16.md#power-sensing). + +**Still open**, per board: `voltagePin`/`currentPin` in the two device definitions, "Power +monitoring" moving from `planned` to `supported`, the SE16's scale factors (the divider ratio and +shunt value, which are NOT in MoonLight's preset and need the schematic), and a reading checked +against a meter. + +Worth doing because it also gives the ADC seam a real bench rig: an on-board analog signal beats a +hand-wired potentiometer. NOTE the pin move is unverified on hardware, both boards being offline +when it was made: confirm LED output still works after the change before trusting it. + +### LightCrafter 16: questions for the maker (2026-10-07) + +Kind: Investigation · Platform: S3 + +The [LightCrafter 16 page](../../reference/hardware/lightcrafter-16.md) holds what the catalog and limpkin's pull requests say. +These are open, to ask limpkin: + +- The module part, the level shifter, the output voltage and the connectors. +- The power input: the connector, the voltage range, the fusing and the reverse-polarity protection. +- Which USB-C connector is the S3's own USB and which the serial bridge, and which chip the bridge is. +- Whether the S3's own port supplies 5 V to a plugged-in device, such as a MIDI desk. +- The RS-485 transceiver and the Hall current sensor parts. +- A schematic and a pin-layout image. + +The device model's use of GPIO 19 and 20 as the LCD driver's unused signal lines is unverified on hardware. + +### Sensor input on Raspberry Pi 5: microphone, IMU, line-in + +Kind: Feature · Platform: Pi + +Audio-reactive lighting (and motion-reactive) is core to what WLED-MM / MoonLight are known for. The Pi 5 is the right host for it: it has the CPU and RAM for real FFT-based audio analysis that the Xtensa ESP32 struggles with, and a full Linux audio + I²C stack. The audio side exists: `AudioService` on the desktop backend captures any PulseAudio or PipeWire device on a Pi, and the effects read its frame. What is missing is a `src/platform/rpi/` backend for I²S and I²C, since the Pi runs the **desktop** backend, which has no GPIO access. So this is a domain expansion built on a real platform-backend prerequisite, not a small add. + +**Target sensors and their Pi 5 interfaces:** + +- **Microphone** — I²S MEMS mic, or a USB audio device read via ALSA. The high-value one: FFT → frequency bands + beat detection drive audio-reactive effects. +- **Line-in** — the Pi 5 has no native analog input, so this is a USB audio interface / DAC HAT feeding the same audio pipeline as the mic; only the source differs. +- **IMU / gyro** — an I²C device (MPU-6050 / 9250-class) on the Pi's I²C bus; tilt / motion → effect parameters. + +**Increments:** a real `src/platform/rpi/` hardware backend (GPIO/I²C/I²S/ALSA) first, the prerequisite; until it lands, the Pi runs the desktop backend with no sensors. The IMU and line-in then slot into the same source-module + platform-API shape `AudioService` has. + +### Bench-verified classic parallel setups, not yet in the catalog + +Kind: Feature · Platform: classic · Trigger: a classic board ships parallel output as its default + +- **Bench-verified classic parallel setups, not yet in the catalog** (2026-09-06). Every classic + board in `deviceModels.json` ships `RmtLedDriver`, so none carries a `ParallelLedDriver` entry, + and the driver's defaults (`clockPin` unset, `dcPin` 33) work on a classic board without one. + Two setups are proven on hardware and worth recording before they are lost: the **Olimex + ESP32-Gateway** drives 64 lights on GPIO 16 with `clockPin` 32 / `dcPin` 4, and its free pins are + scarce enough that a catalog entry should pin them explicitly rather than inherit a default (its + Ethernet PHY holds 18/23 as MDIO/MDC and 5 as reset, all of which the old defaults collided + with); the **QuinLED Dig-Next-2** drives 256 lights on GPIO 2 with `clockPin` unset / `dcPin` 33. + Add them when a classic board actually ships parallel output as its default, rather than + speculatively across the other 16 classic boards, none of which has been tested this way. + +### Classic-board memory: the wins are system-level, not per-module (2026-09-09) + +Kind: Investigation · Platform: classic + +The Dig-Octa on a clean boot with RmtLed has **88,284 bytes free internal, 86,016 largest block**. +Under load and after driver swaps that largest block fell to 24,576, so fragmentation matters as +much as the total. + +What the modules hold is NOT where the RAM went. The whole tree reports **9,342 bytes** of +`dynamicBytes` (Layer 3,586, Preview 2,560, RmtLed 1,536, Drivers 1,536). So per-module trimming has +almost nothing left to win, and the remaining ~230 KB of internal RAM in use is WiFi, lwIP, FreeRTOS +task stacks and the binary's static data. + +Where to look, in order of likely return: + +- **lwIP pool sizes** (`CONFIG_LWIP_*`): TCP PCBs, pbuf counts, and the send/receive windows are all + sdkconfig knobs, sized for a general-purpose device rather than a controller with a handful of + connections. +- **WiFi buffer counts** (`CONFIG_ESP32_WIFI_*_BUFFER_NUM`): the static RX/TX buffer pools are the + single largest tunable block on a classic board. +- **Fragmentation, not just totals.** A 24 KB largest block with 50 KB free is a placement problem; + allocating the long-lived buffers early and together is what fixes that. + +Each is an sdkconfig change measurable on the bench in minutes (`freeInternal` and `maxBlock` on a +clean boot), and each risks a different failure: too few WiFi buffers drops packets under load, too +small a stack overflows under a rare path. So measure one at a time on a board that is actually +serving the UI, not idle. The payoff is real: at 88 KB free a classic board is one large allocation +away from trouble, which is what drove both driver decisions on 2026-09-09. diff --git a/docs/work/future/backlog-process-and-docs.md b/docs/work/future/backlog-process-and-docs.md new file mode 100644 index 000000000..8d554a1d6 --- /dev/null +++ b/docs/work/future/backlog-process-and-docs.md @@ -0,0 +1,406 @@ +# Backlog: Process and docs + +To-build items for processes, tooling and documentation, grouped by module, each with its fields on one line. Other areas: [the future index](index.md). + +## AudioService + +### Flaky unit tests: the AudioService sync suite contends on a fixed UDP port + +Kind: Bug + +**Found:** 2026-07-27, caught by `premerge.py`; pinned to the exact cases by looping the suite and keeping the failing logs. Fails roughly **1 run in 10**. + +Two cases in `test/unit/core/unit_AudioService_sync.cpp` fail intermittently, both around the socket rather than the audio logic: + +- `:125` *"…drives frame_, then holds it and reports listening"* — `CHECK(strcmp(status(a), "listening") == 0)`. The receive path **binds** the fixed port `kTestSyncPort` (21988); when a previous run's socket is still in `TIME_WAIT` the bind fails and the service never reaches "listening". +- `:95` *"Local+send: lazy-opens once and reports sending"* — `CHECK(strcmp(status(a), "sending") == 0)` plus `syncOpenForTest()`. This one **connects** to a broadcast address rather than binding, so it is a *different* failure mode on the same suite. + +**An attempted fix that did not hold (recorded so it is not retried blind).** Deriving the port per run (from the steady clock) instead of the fixed constant cut the receive-path failures but left the send-path case failing ~3 in 20 — because that case never binds the test port, so the port was never its problem. The real fix has to address the send path's socket open on its own terms, not just port choice. + +**Why it matters:** the gate scripts run the suite on every commit and merge, so a 1-in-10 flake interrupts gate lists often enough to train the reader to re-run instead of investigate — the habit that hides a real regression later. + +**Fix (do not paper over it):** treat the two cases separately. For the receive path, bind an **ephemeral** port (bind 0, read the assigned port back) so two runs can never contend. For the send path, first determine *why* the lazy open fails under repetition: whether the broadcast connect is refused or the latch is left set by an earlier case in the same process. A retry wrapper or a sleep is explicitly the wrong fix: it hides the next genuine failure too. `unit_NetworkReceiveEffect*` (a real UDP round trip on localhost) and `unit_SineEffect` (timing) flake the same way, about 1 full run in 3, and deserve the same treatment while the fix is fresh. + +**The quiet-packet case waits on the wall clock** (2026-09-06). `test/unit/core/unit_AudioService_sync.cpp` drives the quiet-packet case with `platform::delayMs(1)` +inside a 100-iteration polling loop, so the assertion that `level` reaches zero depends on real +elapsed time and on loopback UDP delivering within that window. It passes today and has not been +seen to flake, but it is the shape that produces a rare CI failure nobody can reproduce, and it +spends real milliseconds in a suite that otherwise runs on a test clock. + +The fix is the deterministic UDP seam plus `platform::setTestNowMs`, the same pattern every other +timing test here uses: feed the packet, advance the clock explicitly, assert. Raised by CodeRabbit +on PR #96 and deliberately not taken in that pass: it is pre-existing rather than part of that +diff, and swapping a test's transport is a change that wants its own verification. + +## Across modules + +### Headers link the docs by page, not by host (2026-10-21, from the PR 150 review) + +Kind: Restructure + +Seventeen headers under `src/` carry absolute `https://moonmodules.org/MoonLight/...` links, so a docs move edits firmware sources and marks every firmware stale, as PR 150's move from `how-to/` to `reference/` did. A header names the page site-relative (`reference/hardware/panel-cards.md#...`) and `gen_help.py` resolves the host once, so the next docs move touches no source file. + +### Remove the tests' own clock resets, now the listener's job (2026-10-25) + +Kind: Restructure · Platform: desktop · Stage: Test + +The doctest listener in `test/unit/test_main.cpp` puts the test clock back on the real one before every test case and subcase rerun, so the 90 resets in `test/unit` (`setTestNowMs(0)` in a guard struct or at a test's end) do the same job twice. Remove them: a guard struct and its uses go, a guard that also restores the heap, the WiFi scan, the palette or the boot record keeps that half, and a bare reset in the middle of a test stays wherever the rest of that test reads the real clock. About 150 lines, proven by the full suite. + +### Three decisions left open by the code ratchets (2026-10-25, from the Code ratchets plan) + +Kind: Investigation + +- **Name spread's limit**, 10 files beyond a type's own (`MAX_SPREAD` in `check_code.py`), which no tool defaults: confirm or move. Recommended: confirm, since the count only ratchets down. +- **A gate runner for agentless development**: the gate lists are run by an agent today, and a local-model or agentless workflow wants one runnable command per event. `precommit.py` went on 2026-10-01; whether one returns, and in what shape. +- **Undocumented public declarations**: a generated page shows an empty entry for each, which argues for a docgen rule, while cut-first argues against comments for their own sake. The middle way counts only public declarations in headers that generate a page. + +### A catalog entry extends another (2026-10-18) + +Kind: Feature · Trigger: at the first two entries that share a base + +A shield is a carrier board plus its own pins, as a Serg shield is a D1-Mini32 with more wired. An optional `extends: ""` in the [device catalog](../../../mooninstaller/README.md#the-device-catalog) lets an entry take another's `state`: levels nest (MCU, carrier, device), a child overrides its parent at the same module and control, and modules merge by name. +**Build it** at the first two entries that share a base, as a pass in the installer and MoonDeck before they send the document, with `check_devices` resolving it too. + +### The prose sweep, now counted and ratcheted (2026-09-28) + +Kind: Restructure + +The em-dashes dominate and are the mechanical part: a comma, a colon or a full stop replaces each, +and the same sweep already ran once over `src/**/*.h` when `CComments` was added. The rest is +judgment per line. Do it as its own change with its own review, a directory at a time, rather than +inside a branch about something else: a blanket find-and-replace over comments is how a code +identifier gets rewritten by accident. + +### `check_docgen` reads declarations from the clang AST (2026-10-15) + +Kind: Restructure + +The "public member has no `///`" rule reads a declaration's shape with a regex and tracks access by counting braces. It missed 25 functions carrying `MM_NONBLOCKING` until the pattern learned that macros follow a parameter list, and its comments list the other shapes it once misread. `check_clang_query.py` and `gen_help.py` already parse every header with libclang, which knows each declaration's access, kind and attached doc comment. Moving the rule onto that parse removes the brace counting, the depth stack and the shape patterns, and it reaches `.cpp` files, which the regex cannot read. + +### An example: MoonLight in a pod beside the music server (belrik, 2026-10-21) + +Kind: Feature · Platform: Pi · Stage: Document and commit + +belrik runs MoonLight in a Kubernetes pod with PulseAudio, squeezelite and sendspin in containers of their own, playing 96 kHz / 24-bit files through RAOP, each part upgraded on its own. +That is audio-reactive light fed from a music server, and the reference docs do not yet show how containers share a PulseAudio server. +An example page under Reference → Examples, built from belrik's manifest, shows it; asked for on Discord. + +### Retire the OTA fallback repository, once a device has another recovery route + +Kind: Restructure · Stage: Release + +`FirmwareUpdateModule.h` names two: `kReleaseRepo` is tried first and `kFallbackRepo` catches a device whose firmware predates the move. It is tempting to call the second dead code after the rename, and it is not: `platform_esp32_ota.cpp:195` retries the alternate address whenever `esp_https_ota_begin` FAILS, which covers DNS, TLS, an outage and a variant whose asset is missing, rather than only a repository that has moved. So a v6.0.0 device on the happy path never reaches it and a v6.0.0 device whose first request fails still does. Removing it deletes that second chance for every board, which wants a replacement rather than a deletion: a configurable update URL, or a retry against the same address, would give a device somewhere to go. The one cost of keeping it is a wasted request on the failure path, which is already the slow path. + +### macOS code-signing (Developer ID) + +Kind: Feature · Platform: desktop · Stage: Release + +the release `.dmg` is now ad-hoc signed, which turns Gatekeeper's outright refusal into the "unidentified developer" prompt a user can accept via right-click Open. A paid Developer ID certificate plus notarization would drop that prompt too. + +### Windows code-signing + +Kind: Feature · Platform: desktop · Stage: Release + +drops the SmartScreen warning on first run of `MoonLight.exe`. Same shape as macOS signing; needs an EV / OV code-signing certificate (Microsoft Trusted Signing is the cheapest current option). Until then, the README notes the SmartScreen prompt. + +### Publish SHA-256 sums with release assets + +Kind: Feature · Stage: Release + +the Defender false-positive guidance can only +say "re-download over HTTPS" today; a checksums file per release lets a user verify a +quarantined installer before restoring it. One sha256sum step in the release staging. + +### Installer UX polish + +Kind: Feature · Stage: Install + +clear "Pre-release (beta)" warning on RC/latest picks, yank-by-asset-tag instead of yank-by-release-deletion. + +### Offer MoonLight/MoonLight as a library + +Kind: Feature + +a downstream sketch where another firmware/app consumes the light pipeline (or a subset) as an embeddable dependency rather than running the whole binary. `library.json` is already a PlatformIO *library* manifest, so the seed exists. When this is designed, give it a small public **identity surface**: one runtime constant the consumer reads (a `kProjectName`, likely a `ProjectInfo` bundle of name + version + url) that the network wire-strings (ArtNet/E1.31 source-name + CID), the UI banner, and any "About" string all *derive from*: the one place a consumer queries "what am I embedding." This is the genuine home for the name-centralisation that the rename ([the MoonLight plan](../past/plans/Plan-20260922%20-%20MoonLight%2C%20from%20v5.0.0%20to%20the%20rename%20(shipped).md)) deliberately *didn't* do: the rename is a one-time sweep (a constant would just split it), but a library consumer references the identity ongoing and widely, which is the test a constant must pass. Build it *then*, against the real library API, not speculatively now. + +### GCC Release-mode truncation warnings (17, non-blocking) + +Kind: Bug + +`uv run moondeck/build/build_desktop.py --gcc` reproduces CI's compiler (CI builds **Debug**, and that is clean). Building the same tree **Release** with GCC surfaces **17 further `-Wformat-truncation` / `-Wstringop-truncation` / `-Wstringop-overflow` warnings** in `Control.cpp`, `HttpServerModule.cpp`, `JsonUtil.h`, `MqttModule.cpp` — Release inlines more, so GCC can prove more about buffer sizes and gets stricter. + +They are **not** CI failures (CI is Debug) and each one inspected so far is a false positive of the same shape: a bounds check exists, but GCC cannot prove the degenerate case (e.g. `HttpServerModule.cpp:2400` warns "writing 1 byte into a region of size 0" on a line *guarded* by `if (wsLen + headerLen > sizeof(hdr)) return false;`). Still worth clearing: each is either a genuinely unprovable bound (fix the code) or a bound the code knows but does not state (make it explicit). Doing it in one pass beats letting them mask a real one later. + +Not done with the multi-destination/tab-UI merge because 17 warnings across four core files is its own change, not a tail on someone else's. + +**GCC below 16 needs four warnings demoted, and nothing exercises those versions.** (the demotion and its reasons are in `CMakeLists.txt`). It is a suppression, not an understanding: nobody routinely compiles with 12-15, so a REAL instance of one of these on those versions is now a warning nobody reads. Revisit when the runner's default GCC reaches 16, at which point the whole block can be deleted. + +### British spellings and em-dashes predate the prose gate (319 files) + +Kind: Restructure + +`check_prose.py` reports on ADDED lines only, so the American-spelling rule has been enforced from +the day it landed forward, and everything written before it was never swept. 118 files still carry +`colour`, `centre`, `behaviour`, `recognise`, `initialise` and friends, in comments and in a few +identifiers. + +**Em-dashes are the same story and the larger half**: 10,326 of them across 319 files under `docs/`, +against the same rule and missed for the same reason. They are worse than the spellings to sweep +mechanically, because the right replacement depends on the sentence (a colon where it explains, a +comma for an aside, a full stop between two clauses), so a blind substitution produces prose nobody +proofread. Count them per file and do the biggest offenders by hand. + +A sweep is its own change ([documentation-standards](../../contributing/documentation-standards.md#writing)), with the gate run over the whole tree afterwards rather than over a diff. + +Identifiers first and separately: a rename changes an API, where a comment does not. `centre()` in +the shipped `crosshair.mle` was one and is already fixed, since a shipped teaching script is the +highest-value case. + +### Network round-trip test: drop and reorder measurement + +Kind: Feature · Platform: desktop · Stage: Test + +`moondeck/scenario/run_network_roundtrip.py` measures desktop→device→desktop **latency and jitter** per protocol (ArtNet/E1.31/DDP) by timing how long a sent color takes to appear in the device's preview stream. It deliberately does **not** measure per-frame **drops or reorder**, because the path can't track individual frames cleanly: `NetworkSendDriver` re-clocks at its own fps (decoupled from receive) and `NetworkReceiveEffect` holds-last-frame, so frames don't pass through 1:1 — a sequence number embedded in frame N may be re-sent 0, 1, or several times downstream. The min/median/max spread the test already reports *is* the jitter signal (it surfaced multi-second outliers on the classic ESP32). To measure true drop/reorder, the firmware would need a sequence-faithful echo path (e.g. a `NetworkReceiveEffect` echo mode that re-emits each received frame 1:1 back to the sender, bypassing the fps re-clock), then the desktop could match sent↔received sequence numbers. The test's docstring lists this under "extend later" alongside per-frame sequence matching and the device→device chain. + +### MoonDeck doc-asset endpoint hardening + +Kind: Restructure · Platform: desktop + +`moondeck/moondeck.py::_serve_doc_asset` accepts any ROOT-relative path and serves the file. Path traversal *is* blocked (`asset_path.relative_to(ROOT.resolve())`), but inside the repo any file is served — including local-only artefacts like `moondeck/build/wifi_credentials.json` if present. MoonDeck binds to all interfaces by design (the existing comment in `main()` explicitly enables LAN reach), so anyone on the LAN can hit the endpoint. + +Two improvements when this matters: +- **Subdirectory whitelist** — only serve under `docs/` (and image asset paths the markdown renderer needs). Reject `moondeck/build/wifi_credentials.json` etc. with 403. +- **Extension whitelist** — only image / CSS / JS mime types via a small allowlist. +- **Optional bind-to-localhost flag** — `--bind 127.0.0.1` for users who don't want LAN reachability. Default stays "" (all interfaces) since the LAN-reach is the documented design. + +Not blocking — MoonDeck is a developer tool, not a production server. Pick this up when MoonDeck is in scope for hardening. + +### A tagged release does not reach the web installer until the next main deploy + +Kind: Bug · Stage: Release · Process: 15-release + +`deploy-pages` in `.github/workflows/release.yml` is gated `if: github.ref == 'refs/heads/main'`, because the `github-pages` environment's protection rule only allows main. The installer's release list is **staged into the Pages site at deploy time** (`install.js` self-hosts the last 5 stable + 5 prerelease releases; the release-asset URLs redirect to a host that sends no CORS header, so the browser cannot read them from the Pages origin). Together those mean **pushing a `vX.Y.Z` tag publishes the release but never updates the installer** — the new version reaches the picker only when something later pushes to main. + +Hit on v4.0.0 (2026-08-24): the release published at 16:15:59, a main deploy ran at 16:16 and enumerated releases *before* it existed, and the installer offered v3.0.0 as newest for hours. Re-running the workflow with the tag fixed it, and that manual re-run is the current workaround. + +Note the device's own OTA picker is unaffected — it reads `api.github.com` live ([app.js](../../src/ui/app.js) `RELEASES_API`), which is why a device could offer v4.0.0 while the installer could not. Two independent paths to the same release list. + +`restage-pages-for-tag` in the same workflow already covers this: on a `v*` tag it re-invokes the workflow on `main` with `tag=`, so `deploy-pages` runs under the allowed ref and the manifests land in `releases//`. The open item is narrower than the symptom suggested — whether the dispatched run can still lose the race, since it is a *second* run whose release enumeration happens later, and whether relaxing the `github-pages` environment's branch policy (needs repo-admin) would remove the indirection entirely. + +### CI: pin GitHub Actions to commit SHAs (supply-chain hardening) + +Kind: Restructure + +`.github/workflows/release.yml` references all 9 action types by mutable `@vN` tag (`actions/checkout@v4`, `astral-sh/setup-uv@v3`, `softprops/action-gh-release@v2`, `espressif/esp-idf-ci-action@v1`, …). A mutable tag can be force-moved to malicious code by a compromised publisher; pinning each `uses:` to a full commit SHA (with a `# vN` trailing comment) removes that vector. **Not done (this item):** SHA-pinning, because it carries an ongoing cost — pinned SHAs go stale and miss security patches, so it only pays for itself **alongside Dependabot** (or a Renovate config) to auto-bump them. Pick this up as a deliberate "CI hardening + Dependabot" pass, not piecemeal. Low risk today: every action pinned is a first-party `actions/*` or a well-known publisher (astral, espressif, softprops), not an obscure third-party action. + +### An offline test run takes 16 minutes instead of one (2026-10-03) + +Kind: Bug · Platform: desktop · Stage: Test + +With the host's network down, `test_desktop` ran 16 minutes and the scenarios stalled past the gate's hour; online, no test takes over 4 seconds, so something waits out network timeouts. +Next time the machine is offline, `build/macos/test/mm_tests --duration=true` names the test, which then gets a seam like `setTestJoinFails`. + +### The restructured scenarios lost four things the archive asserted (2026-09-29) + +Kind: Feature · Stage: Test + +What did not survive the move of the 27 archived scenarios to the per-module set, and nothing else in the tree covers: + +**Every performance contract and bound is gone**, measured rather than estimated: `contract` 40 to 0, `bounds` 20 to 0. +Both mechanisms are still implemented in the runner (`scenario_runner.cpp` enforces a `tick_us` ceiling and a `free_heap` floor, and the `bounds.fps` gate below it). +The new set records `observed` numbers, which is a trend rather than an assertion, so nothing fails on a tick or heap regression. +One document still states otherwise and should be corrected when this is: `docs/reference/performance.md` says each scenario step carries an enforced contract. +The `device/` set carries `observed` blocks from a real ESP32-S3 as of 2026-09-29, which is the window a contract would be set from; none is set yet, since one run per step is not a baseline. + +**The buffer checks run ONCE at the end of a scenario**, already filed under per-step assertions below, so a resize can still only assert that the control changed. +Nothing can say the buffer grew to 1024 lights at 32x32 and shrank back, which is the claim the scenario's own name makes. + +**`HttpServerModule` has no scenario**, though it is a scheduler root on every networked target and the transport the live tier itself runs over. + +What no unit test can reproduce is the cross-board comparative number set the peripheral sweep produced. + +**A measurement taken after a failed allocation is recorded as though the work happened (2026-09-30).** `scenario_Fluid_solver` on a classic ESP32 reads 107 us at 64x64 where an S3 takes 71,441 us, which no float-heavy solver does: the classic is 670x faster only because its grids never allocated. The tell is in the same block, where free heap RISES from 25,760 to 53,728 bytes as the tick collapses, so memory is being handed back while the effect ticks dark. `cube-20-d` reads the same way at 97 us. +Two halves, and neither exists today. `FluidEffect::prepare` refuses a grid it cannot allocate and leaves the effect ticking harmlessly, which is the right behaviour, but it publishes no status, so nothing downstream can tell a refusal from a cheap frame. And `run_live_scenario.py` records whatever tick it measures without asking whether the module producing it is in a state worth measuring. +The fix is a status on the effect (the shape `ParallelLedDriver` already uses for a bus that will not init) plus a runner check that labels or drops the sample. It needs a classic on the bench to verify, since that is the only board small enough to fail the allocation. Until then the affected rows are the classic's `grid-64-h`, `all-jets`, `long-persistence`, `cube-20-h` and `cube-20-d`, and they understate the cost rather than overstating it, so no contract set from them would be too lax. + +### The static set runs on every supported device before the rename (2026-09-29) + +Kind: Feature · Stage: Test + +The scenarios are the pre-cutover hardware check: a set that passes in-process proves the logic, and the same set against each board proves the platform layer under it. +That is the point of getting the static set solid first, and it is what the device sweep on cutover day reads from. + +Two of the three things it needed are now in place: the light scenarios render, and eight scenarios are marked `live_only` so a sweep asks a board something a laptop cannot answer. +What remains is a runner that takes the device list rather than one host, so the answer is a matrix across the chips we ship rather than a pass on whichever board was plugged in. +The first S3 run on 2026-09-29 found two defects the set had been hiding: a `channelsPerLight` prop that stopped being a control, and peripheral names renamed out from under the scenarios, where `optional: true` turned every wrong name into a silent skip and still reported a pass. + +`moondeck/moondeck.json` carries 24 registered devices and `run_live_scenario.py` takes a `--host`, so the missing piece is the loop and the per-device report, not the transport. + +### A scenario that runs until something breaks (2026-09-29) + +Kind: Feature · Stage: Test + +A static scenario proves a sequence works once. What it cannot find is the fault that needs a thousand random module additions, or a leak that takes an hour: the runner sets up, asserts and exits, so the long tail of lifecycle bugs has no test that reaches it. +A fuzz mode would run until killed on a device left on a bench, doing randomly chosen legal things and stopping the moment one fails. + +The runner already carries most of it: a step vocabulary, module add, remove and replace, control setting, measurement, and per-step failure reporting. +What it needs is a mode that generates steps rather than reading them, and four things that make a failure worth acting on. + +A **seed**, so a run is reproducible: a failure reprints the seed and the step index, and the same seed replays the same sequence. +A **legal-move set** the scenario names rather than the generator guessing, since a random string in a filepath control proves nothing about the pipeline. +**Halt on the first failure**, keeping the device in its broken state for inspection rather than tidying it away. +And a **floor under heap and tick**, because the faults this is for announce themselves as a slow drift rather than an assertion. + +Worth most where the gaps above are: random add and remove under a live pipeline is exactly what the archived `remove_module` steps covered, and a long run on a Raspberry Pi finds the leak a thirty-second scenario cannot. +Build it after the static set is solid, since a fuzz run is only as trustworthy as the assertions it fires. + + +- **Memory degradation cascade** — the output-buffer *allocation* decision (no buffer for a lone identity layer; a buffer for ≥2 layers or any LUT layer) is unit-pinned (`unit_Layers_container` "Drivers allocates the output buffer only when…"), and LUT-vs-identity is pinned by `unit_Layer_sparse_mapping`. What's **not** pinned is the *low-heap* half of [architecture.md § Degradation cascade](../../reference/hardware/firmware-variants.md#degradation-cascade): under heap pressure the LUT + driver buffer are skipped *together* (`lutSkipped()` true, forced 1:1), and below that the layer buffer *reduces dimensions* (halving to a 8×8 floor) rather than failing. The hook exists — `unit_BlendMap` already uses `platform::setTestMaxAllocBlock` to force allocation failure for the paging test — so a test could cap the block size and assert: (1) LUT+output buffer both skip and `lutSkipped()` flips, (2) the layer buffer shrinks to fit and never goes null. Pre-existing gap (predates multi-layer); the *happy-path* allocation contract is covered, only the OOM-degrade branch isn't. +- **Per-step assertions in scenarios (a framework gap, not a scenario gap).** A scenario step can + assert TIMING and HEAP (`bounds`, `contract`) and the run asserts the final buffer, but it cannot + say "after this step the fixture has 24 lights", "this module's status reports a compile error", + or "the rendered output changed". So a migrated script step proves the pipeline still ticks, not + that the edit did what it claims — the MoonLive scenarios walk break-and-recover cycles whose + most interesting states go unasserted. Wants a small vocabulary (`expect_lights`, + `expect_status`, maybe `expect_pixel`) added to **both** runners together: the desktop/live split + is exactly what let `write_file` exist on one and silently no-op on the other. Raised by + CodeRabbit against scenario_MoonLive_pipeline and scenario_MoonLiveEffect_controls; the sites are + listed there, but the vocabulary has to exist first. +- **UI page load time** — scenario step measuring HTTP response time for `/`, `/api/state`, `/api/system` via the live runner. Verifies acceptable load time on ESP32. +- **Module teardown memory** — scenario that tears down all modules and verifies heap returns to pre-setup baseline. Confirms no lifecycle leaks. +- **`syncVisibleControls` has no test in `test/js`.** `app.js`'s conditional-control DOM logic (`syncVisibleControls` reconciles which control rows are rendered when a `hidden` flag flips) was the source of a real re-render-loop freeze (Network static-IP toggle) caught only on hardware. A `jsdom` test that builds a card, flips a control's `hidden`, runs the reconcile, and asserts the right rows appear/disappear (and that it converges: the unchanged→no-op fast path) would have caught it. It needs an `app.js` seam first: extracting `reconcileControlRows` into a served file, wired through `embed_ui.cmake` + the two HttpServerModule routes like the other UI .js. +- **Browser-level Improv automation** (deferred) — `moondeck/build/improv_smoke_test.py` (added 2026-06-03) exercises the device-side Improv listener over plain serial; what's missing is the browser-side equivalent — Playwright driving Chrome's Web Serial, clicking through ESP Web Tools' install modal, filling the WiFi creds form, asserting `PROVISIONED`. Catches "ESP Web Tools changed its Improv handling in a way that broke our manifest format" failures the serial-only smoke test can't see. Hard to set up reliably (headless Chrome with Web Serial is finicky, needs a wired ESP32 in CI). Pick this up if a regression in the browser flow ever escapes the manual dev-environment test (preview_installer flash-ready mode at ). + +### Live full-suite run leaks state between scenarios + +Kind: Bug · Stage: Test + +`run_live_scenario.py --module all` runs scenarios in sequence against one device, and they share the live tree. Two scenario styles don't compose: +- **Canvas-preparing scenarios** (`scenario_perf_light`, `scenario_perf_full`) `clear_children` the containers and rebuild, then their cleanup leaves the tree **bare**. +- **Canonical-tree-assuming scenarios** (`scenario_GridLayout_resize`) are `mutate` scenarios that expect the boot tree (Grid / Noise / Multiply) to already exist and only tweak it. + +Run a bare-leaving scenario before a tree-assuming one and the latter fails pre-flight ("references ids neither on the device nor added by an earlier step"). Each passes **in-process** (fresh tree per scenario — the authoritative gate) and **live individually** (after a clean boot); only the chained live run trips. Not a product bug — a consequence of the "scenarios own their state, no restore" model the canvas-preparing scenarios follow, which the older ones predate. + +Fix options: (a) make every live mutate scenario clear+rebuild its own canvas (consistent with the newer ones) so order never matters; or (b) have the live runner reboot / restore the canonical tree between scenarios. (a) is the cleaner long-term shape. Until then, the in-process suite is the gate; live full-suite runs need a clean boot per scenario, or run scenarios individually. + +### Promote the one-line // cap to an error, once the 604 .cpp blocks are homed (2026-09-21) + +Kind: Restructure + +`MAX_CODE_COMMENT = 1` is already the rule in both kinds of file, and already an ERROR in a header. It stays a warning in a `.cpp` only because `_blocks()` stages the sweep by file kind, and that function names its own removal: a rule leaves the staging as the tree meets it, which no-hard-wrap already did. + +**The shape of what is left.** 604 blocks across 92 files, 2081 comment lines. By area: 239 in `test/`, 210 in `src/`, 155 under `src/platform/`. By length: 81 are two lines, 321 are three, 124 are four, so 526 of 604 are three lines or fewer and the tail is one 15-line block, two of ten and a handful between. + +It concentrates, which is what makes it approachable. `HttpServerModule.cpp` alone holds 100 and the top ten files hold 312, over half; the other 82 files average under four each. + +**Where the depth goes is the blocker.** Of the 92 files only 19 already carry an `@technical` appendix. 68 have a `///` lead with no appendix and 5 have neither, so most files need the appendix written before a single block can move. + +**The fix is not to split them.** Chopping one comment into two shorter lines leaves the text identical and satisfies nothing, which is why the cap counts a block rather than a line. Each one is depth that belongs in the file lead's appendix with an `@xref` back, or prose that says less. + +**Suggested order.** `HttpServerModule.cpp` first: 100 blocks, 444 comment lines, and it already has the appendix. Then the other nine heavy files for 212 more. Then `test/` as its own sweep, because the argument that a test comment explains why a test EXISTS is strongest there and may deserve a different budget rather than an appendix. Promote once at zero. + +**Cost.** Not a scripted pass. Each block is a judgement between depth that belongs in an appendix, an explanation that should be shorter, and history git already holds. About 35 done by hand in one session is the measured rate, so the first file is a session of its own and the whole sweep is several days. + +### ESP32 clang/LLVM toolchain: extend the clang checks to src/platform/esp32/ + +Kind: Feature + +Espressif ships an xtensa LLVM (their fork), but the installed `esp-clangd` package contains +**only `clangd`** — no `clang++` driver — so a clang analysis pass over ESP32 sources needs their +full LLVM installed separately. + +What it would buy: `-Wfunction-effects` (and clang-tidy, clang-query) over `src/platform/esp32/` +— 20 translation units, the only code the desktop build excludes. Everything else, including the +LED drivers, already compiles on desktop and is already checked. + +What it costs: a second ~1 GB toolchain, maintained purely for analysis — the firmware would +still be built by GCC, so the analysing compiler is not the shipping compiler. That is a real +"one rule, one owner" tension, and the reason this is a decision rather than an obvious yes. + +Worth revisiting when either the coverage gap bites (a hot-path bug traced to the platform layer +that the desktop check could not see) or Espressif's LLVM becomes the default toolchain. + +### clang-tidy: triage the remaining 30 findings + +Kind: Restructure + +`.clang-tidy` runs `*` minus a documented disable list and reaches zero on everything except the +path-sensitive `clang-analyzer-*` family: **30 findings**, led by +`bugprone-unchecked-string-to-number-conversion` (9), `security.insecureAPI.strcpy` (5), +`core.NonNullParamChecker` (4), `deadcode.DeadStores` (3) and `unix.cstring.NullArg` (3). Roughly +half sit in test code (`strcpy` into fixed local buffers, a deliberate divide-by-zero probe). + +The nine `unchecked-string-to-number-conversion` sites were read individually and are all safe: +HueDriver guards on `if (id > 0)`, JsonUtil documents 0-means-absent, MqttModule uses a `v = -1` +sentinel and clamps. They stay reported rather than suppressed — a report shows the real situation, +and a `NOLINT` would hide a genuine class of bug for the next person who writes one of these. + +### Pin the preview cancel over loopback (test) + +Kind: Feature + +`cancelBufferedSend` closes a preview client left mid-message, so the next frame is never read as the rest of this one, and nothing pins it. The loopback `WsClient` in `unit_HttpServerModule_subscribe.cpp` already drives `/wsp` and reads a frame whole; a client that stops reading mid-frame, then a cancel, then a check that its socket closed while a client that read the whole frame stays open, covers it. + +### MoonDeck scripts crash on Windows whenever their output is redirected (2026-08-22) + +Kind: Bug · Platform: desktop + +62 of the ~64 scripts print `→ ✓ ⚠ —` or box-drawing characters. A REDIRECTED Windows stdout takes `locale.getpreferredencoding()`, cp1252, and the first such character raises UnicodeEncodeError, *after* the real work has succeeded: `collect_kpi.py` measures everything, writes the metrics, then dies printing the summary arrow. An ATTACHED console has been UTF-8 since Python 3.6 (PEP 528), measured on this bench: `utf-8 isatty=True` from a console against `cp1252` through a pipe. So the entry's original framing was backwards. A human at a Windows terminal is the one case that works, and every redirected path fails: a pipe, a tee, a CI log, an agent's captured output. The gate runner that handed children `PYTHONIOENCODING=utf-8` is gone, which is why this surfaces now. + +The READ side breaks too, and that half is worse because `PYTHONIOENCODING` does not mask it: `Path.read_text()` and `open()` without an encoding decode as cp1252. A sweep found 43 unqualified `read_text`/`write_text` plus 47 bare `open()` across `moondeck/`. + +One Windows day (2026-09-29) hit it FOUR times in four different files, each fixed separately at its own call site: `_test_metadata.py` stopped the test runner starting, `test_desktop.py` would have killed a run on one non-ASCII byte from the test binary, `run_live_scenario.py` printed an arrow per step and so recorded a scenario the device PASSED as FAILED, and `preview_installer.py` died writing a staged page, leaving the web installer preview unusable on Windows. Four site fixes in one day is the argument for one home, made by the defect itself. + +The preamble is now copied into five scripts (`setup_esp_idf.py`, `ci/verify_version.py`, `docs/build_docs.py`, `scenario/run_live_scenario.py`, `scenario/run_scenario.py`), each carrying its own comment explaining the same fact. Per-script `sys.stdout.reconfigure()` is the wrong shape at 62 files: every new script would have to remember, and the one that forgets fails in the field. It wants ONE home, and the two candidates do not cover the same ground. `PYTHONUTF8=1` in whatever env MoonDeck's front ends already establish settles BOTH halves, because UTF-8 mode changes the default for `open()` and `Path.read_text()` as well as for stdio. A shared `moondeck/_stdio.py` imported by the entry points settles only the stdio half, since `reconfigure()` touches the standard streams and nothing else, so it leaves every unqualified `read_text`/`open` exactly as broken and those sites still need `encoding="utf-8"` written out. Pick when someone next runs a check by hand and it dies on a tick mark. + +**`run_scenario.py` was the fifth site, fixed 2026-09-30 ahead of a Windows run.** Its sibling `run_live_scenario.py` got the preamble on the Windows day; this one did not, and it relays the output of `mm_scenarios`, which prints an arrow and an em-dash of its own (`test/scenario_runner.cpp` carries 29 non-ASCII characters, several inside `printf`). Nothing had caught it because the in-process tier had only ever run on macOS, where an attached console and a pipe are both UTF-8. Left alone it would have read as a scenario failure rather than an encoding one, which is the misdiagnosis the live runner's fix was written to prevent. Five sites in two days is the count the one-home argument above should now be weighed against. + +### A Windows scenario run recorded tick averages 250x its own repeat, cause unknown (2026-09- + +Kind: Investigation · Platform: desktop + +A Windows run of `scenario_Layouts_resize_reallocates_live` recorded 245 ms, 564 ms and 458 ms for its three measure steps, against 41 to 89 us for the same steps on macOS. `tickTimeUs` is a one-second average (`src/core/module/Scheduler.cpp:127`), so those figures describe a second containing roughly one tick, which reads as a blocking call rather than slow rendering. The blocks were reverted rather than committed, since an observation filed at 245 ms teaches a future reader that Windows costs that. + +An immediate repeat on the same machine and binary did not reproduce it. The three steps came back at 913 us, 1097 us and 768 us, and the tick log showed `PanelCard:0us`, `Network:1us` and `Drivers:30us` throughout, with the tick dominated by `MoonLive:3000us`. Nothing was blocking during the repeat, so the first reading stands unexplained. The candidate that fits the shape is `ethBindRawInterface()` calling `pcap_findalldevs` (`src/platform/desktop/platform_desktop.cpp:1299`), measured separately at about 2.3 seconds on Windows against milliseconds on macOS and reached through driver `prepare()` on a resize, but the repeat neither confirms nor kills it: no raw interface was bound on either run. + +The enumeration cost is real and independently measured, so caching it or keeping it off any path a resize can reach earns its place on its own. + +**The second half of this is already answered, from the other side of the same day (2026-09-29).** The ask was for a max-tick beside the average, to tell one blocked tick from a uniformly slow second. The observation block records `min`, `max`, `p50`, `p95`, `n` and the raw `samples` per target, so a repeat of this run reports its own worst frame rather than only its mean: a single 245 ms tick inside a second of good ones now shows as a `max` far above the `p50`, which is the distinction that was missing. + +**Re-measure before reading anything into the old figures.** `scenario_Layouts_resize_reallocates_live` was rewritten the same day (59 lines changed): it now builds a full pipeline and asserts with `expect_control` where it previously only measured, so the Windows numbers above describe a scenario that no longer exists in that form. Whatever a Windows run reports next is the figure to reason from, and it will carry the spread this entry wanted. + +### build_esp32.py reports success without building, under Git Bash (2026-09-28) + +Kind: Bug + +Run from Git Bash it prints `MSys/Mingw is no longer supported`, stops after `Setting target to esp32s3...`, and **exits 0** ten seconds later with no binary. Run from PowerShell the same command builds correctly. A green exit code for a build that never ran is the worst shape a failure can take, and on a runner it would publish a stale binary while every gate stayed green. Either detect the MSYS environment and fail loudly, or re-exec through the shell the IDF tools expect. + +### build_desktop.py cannot relink while the app is running (2026-09-28) + +Kind: Bug · Platform: desktop + +Windows holds an executable open while it runs, so a rebuild fails with `LNK1104: cannot open file ... MoonLight.exe` where POSIX would replace it. `run_desktop.py` already stops a running instance before launching; `build_desktop.py` could reuse that and remove a papercut that costs a confusing error every time someone edits code with the app open. + +### disasm.py cannot run on Windows (2026-08-21) + +Kind: Bug · Platform: desktop + +The tool shells out to `c++` and `objdump` to build the per-ISA emitter and disassemble its bytes, and a Windows machine carrying only MSVC has neither. That holds for every ISA, not just the new `x86_64` one — but x86-64 is where it bites, because the host it disassembles is now a Windows desktop and the tool is what turns "the script did nothing" into an answer. The x86-64 backend was brought up without it, using byte-pinning tests plus WinDbg on the emitted image; that worked, and was slower than reading the instructions would have been. + +Closing it is a compiler/disassembler pair behind the two `subprocess.run` calls: `cl.exe` for the build, and for the disassembly either LLVM's `llvm-objdump` (which understands `-b binary` the way the tool already expects, and ships with the VS "C++ Clang tools" component) or a `.obj` wrapper around `dumpbin /disasm`, which does not. Prefer the former — the flags are already right, so it is a lookup rather than a second code path. + +### A preview GIF for every MoonLive script (2026-10-19) + +Kind: Feature + +The 140 compiled effects each show a GIF on their catalog card (`docs/assets/light/effects/.gif`, 25 MB in all); the 49 MoonLive effect scripts show none. +The pictures belong in `docs/assets/moonlive/effects//.gif`, mirroring the script subfolders, never in `moonlive/` itself, which the build globs and the UI fetches file by file. +One MoonDeck script makes them all: `screenshot_modules.py` learns to step through the MoonLive catalog, capturing from the preview socket the way the hpwit GIFs of 2026-10-18 were made. +At 0.5 to 1 MB each that is 25 to 50 MB more, so a smaller format (128 px, 2 s) is part of the decision. +**Decide first** where they show: a MoonLive scripts page with a card per script grouped by subfolder, or the script picker previewing one before its download. + +### sigrok/fx2lafw cross-check + MoonDeck LED driver test script + +Kind: Feature + +- **sigrok/fx2lafw cross-check + MoonDeck "LED driver test" Python script** — the independent-clock proof and the run-from-MoonDeck flow ([the LED-driver analysis](analysis-leddriver.md#the-sigrok-cross-check)). The on-board RMT-RX loopback (shipped) is the cheap CI correctness gate but a *compromised witness* for WiFi-induced flicker — the RX capture runs on the same ESP32 whose WiFi causes the glitch. The real flicker test is a **sustained capture (seconds) with WiFi associated + a packet flood**, decoding every frame for a byte-slip or reset-gap deviation; it validates the SHIPPED render↔encode split's WiFi isolation (drivers tick on core 1; WiFi lives on core 0). A DSLogic Plus (100 MS/s) upgrade is reactive — only if a flicker reproduces that 24 MS/s can't resolve. diff --git a/docs/work/future/backlog-services.md b/docs/work/future/backlog-services.md new file mode 100644 index 000000000..0630260e7 --- /dev/null +++ b/docs/work/future/backlog-services.md @@ -0,0 +1,124 @@ +# Backlog: Services + +To-build items for Services, grouped by module, each with its fields on one line. Other areas: [the future index](index.md). + +## InfraredService + +### IR RX live-reconfigure recovery (unconfirmed, park until it recurs) + +Kind: Bug · Platform: S3 · Trigger: it recurs + +- **IR RX live-reconfigure recovery — unconfirmed, park until it recurs** (bench 2026-07-13, SE16). IR reception on the SE16 (`IrService` pin 5) went dead mid-session and only a **hard reset** brought it back; a warm/API path did not. **Ruled out:** not hardware (hard reset fixed it, receiver+switch+wiring fine), not LED-count (IR survives the full 16384-light / 8 fps load — a received code still toggled a control at max load), not a regression from the i80 commit (`platform_esp32_ir.cpp` untouched, the 1250 ns glitch-filter fix intact). **Prime suspect (unproven):** the session's live pin churn — including transiently setting the i80 `clockPin` to **5, which IS the IR pin** — left GPIO 5 routed to the wrong peripheral, and the RMT-RX channel (a pin-keyed static behind `platform::irStop`/`ensureChannel`) didn't re-acquire cleanly on the next `irRead`; only a full GPIO re-init (hard reset) cleared it. This may be pure test artifact (nothing in a *normal* user flow points two live modules at GPIO 5). **To conclude:** from a fresh hard reset (IR working), in isolation set i80 `clockPin=5` then restore `clockPin=8` and check whether IR dies and whether it self-recovers *without* a hard reset — self-recovers → no bug (test artifact); stays dead → a real live-reconfigure gap in the IR channel re-acquire worth fixing (per *No reboot to apply a configuration change*). Small robustness/repro work; do it only if IR breaks again in real use. + +## SdCardService + +### An SD card inserted after boot mounts only when its module restarts (2026-10-12) + +Kind: Feature + +A mount attempt on an empty slot runs the SDMMC card-init sequence to its timeouts, so `SdCardModule` never retries one on the render thread: a card inserted later mounts when the module is switched off and on, on a pin edit, or at boot. A pull is still noticed within a second through a cheap presence probe. Detecting an insertion without that stall needs a card-detect pin where the slot has one, or a mount attempt off the render thread. The timed retry PR #136 carried was dropped for this reason before its merge. + +## Across modules + +### A Service that calls another device's API (robwomp, 2026-10-11) + +Kind: Feature + +Suggested on Discord: panels driven by another stack take their pixels from MoonLight over DDP or Art-Net, but their settings stay out of reach. +A user-added Service holds a list of HTTP requests, each a method, a URL and a body, and fires one when a control it watches changes, so a button, a MIDI knob or an OSC message drives any device that answers `curl`. +Inbound control already exists (`/api/control`, MQTT, OSC, MIDI); this is its outbound twin, and the generic complement to the typed command half of DevicesModule below, which knows each ecosystem's API. +Requests leave the render thread, since a slow peer must never stall a frame. + +### The Open Stage Control session's labels (2026-08-30) + +Kind: Investigation + +Found wiring a real control surface to the [OSC module](../../moonmodules/core/services.md). + +**The shipped Open Stage Control session renders no labels, and its pad matrix draws nothing.** +[`docs/reference/examples/open-stage-control.json`](../../reference/examples/open-stage-control.json) works for every fader, encoder and switch, but the widget names never appear and the `matrix` of pads is an empty box. +Five attempts at the label property failed (`@{}`, `JS{}`, `#{}` with `unit`, an explicit string, omitting it so `"auto"` applies), and notably a session SAVED by Open Stage Control itself carries no `label` key on a fader at all, so the name is drawn from something else. +The session was written by reading a minified app's bundled HTML docs; the reliable fix is to build one widget of each kind by hand in its editor, save, and copy the shape it produces. +Cosmetic: the control path works, only the labels and the pad grid are missing. + +### MoonCloud: share a MoonLive script over MoonTalk (2026-09-10) + +Kind: Feature + +Sketched 2026-09-10 while Talk was built. **The unique feature nobody else has**: MoonLight ships a scripting language whose programs are about a kilobyte of text, and a message board between devices. Together they mean a script someone wrote on their wall is one tap from running on yours. + +**What makes it plausible.** The scripts are tiny: the shipped `.mle` files run 780 to 1907 bytes and the whole library of twenty-odd is 36 KB. A script is self-contained by design, so the text is the artifact. And the device already compiles and runs arbitrary script text safely, which is the hard half and is done. + +**The design work is that a script is five to seven times a message.** Talk caps a message at 280 characters, deliberately: a chat message is a sentence, and one caller must not fill the table. Three options: + +- **A second endpoint** (`/api/script`) with its own size cap and table, and a message that references it by id. The chat table stays a chat table, a script is fetched when someone wants it, and a board read stays small. Most work, cleanest shape, and the recommendation. +- **An attachment column on the message**, capped separately. One table and one post, at the cost of every board read carrying script text unless the query is careful. +- **Share a URL.** A script already lives in the user's File Manager and a device could serve it over the LAN. Free, and limited to one network, which is the case that makes the feature interesting. + +**A shared script is code from a stranger**, in a way chat text is not. Three questions the design answers rather than discovers: + +1. **Loading is explicit and reversible.** A script arrives as something to look at, and taking it leaves what the user already has intact. `/moonlive` (user) shadowing `/.moonlive` (factory) is the existing trap. +2. **The device is the sandbox.** MoonLive reaches no filesystem and no network, so the blast radius of a hostile script is the LEDs and the render budget. Worth confirming rather than assuming: a script that never yields is a watchdog reset, which is a denial of service on someone's wall. +3. **Attribution and removal.** Any sender id can be claimed, and a message stays once posted. Fine for chat, weaker for code. + +Depends on Talk shipping and on the server being deployed. + +### The sensors an installation needs (2026-09-01) + +Kind: Feature + +**Why this is a commitment rather than a wish list.** One of the intended uses is art installations, +and an installation people can interact with has to sense them: that is stated in +[the architecture](../../explanation/architecture/index.md#the-problem). Sensing is therefore part of the product, not a +convenience, and an input peripheral is first-class alongside an output driver. The scope is still +narrow on purpose: a lighting controller that senses its audience, not a home-automation platform. + +Beyond the sensors [the input mapping plan](../present/Plan-20260901%20-%20Input%20mapping%20and%20scripted%20sensors%20(7%20of%2012).md) carries (PIR, distance, IMU, rotary encoder), the sensors worth having next: + +- **Touch**: the classic ESP32's capacitive touch pins need no external part, so a conductive + surface becomes an input. The Dig-2-Go's own button is on a touch-capable pin, though it reads + fine as a plain digital switch. +- **Light level (LDR or a BH1750)**: an installation that dims itself to the room, and the obvious + companion to a piece that runs day and night. + +**None of these need new architecture**, which is the point of recording them together: each is a +Service under the core `Services` container, reaching the rest of the system by the plan's two paths, events and streams. What they need is a +platform seam per sensing modality (the GPIO seam shipped; I2C exists; a pulse-timing seam does +not), and a module each. + +### Read a vehicle's CAN bus and drive lights from it (2026-09-11) + +Kind: Feature + +A MoonLight device in a car, reading the vehicle's own bus and driving aftermarket lighting from what it sees: underglow that pulses with engine RPM, sweeps with the indicator, flares on the brake. The vehicle to develop against is a VW Transporter (the product owner's). + +**Read-only, and that is the design, not a limitation.** Writing to a vehicle's bus reaches systems that are mandated safety equipment (indicators and brake lights are UNECE R48), usually on a segment shared with ABS and airbags, and modifying them is illegal in most jurisdictions. Reading has none of that exposure and gets the interesting half anyway: the car reports its state, MoonLight renders it. Anything the device drives is aftermarket lighting on its own output, untouched by the vehicle. + +**A Service, not a driver.** It senses rather than renders, so it belongs under the core `Services` container beside AudioService and ButtonService, and it produces both shapes the input mapping plan describes: an EVENT (indicator on, brake pressed) drives a control through `Scheduler::setControl`, while a continuous VALUE (RPM, speed) is published as a shared frame the way `AudioService::latestFrame()` is. Effects then read it, the input-mapping layer routes it, and a MoonLive script reads it as a system variable, so "underglow sweeps with the indicator" is a script someone writes and shares rather than firmware. + +**Two tiers, and only the first is write-once.** + +| | Standard | What it yields | How | +|---|---|---|---| +| OBD-II / UDS | Yes, legally mandated (EU: 2001 petrol, 2004 diesel) | RPM, speed, coolant, throttle, engine load | Request/response against ID `0x7DF`, so polling, a few times a second | +| Manufacturer body frames | No, proprietary per model and year | Indicators, brakes, doors, lights | Passive sniffing and reverse engineering | + +J1939 is NOT the answer here despite being the "CAN lighting standard" people reach for: it is the heavy-duty protocol (trucks, agricultural, marine), and a Transporter is passenger-car architecture running VW's own frames. J1939 would only apply if MoonLight targeted commercial vehicle equipment. + +So phase 1 is OBD-II only, which genuinely works on any car and needs no per-vehicle data. Phase 2 adds raw-frame sniffing, and there the frame IDs must be CONFIGURATION rather than compiled-in constants: a Transporter owner who discovers theirs shares a config file, and no firmware release is involved. That is what keeps a per-vehicle feature from becoming a per-vehicle maintenance burden. + +**What it needs:** + +- A `platform::twai*` seam (init, send, receive, stop). TWAI is the ESP32's CAN controller and is present on every chip MoonLight ships: 1 controller on classic and S3, 3 on the P4. +- **An external transceiver**: the ESP32 has the controller but no CAN PHY, and this is the only part that needs buying. An SN65HVD230 board (3.3V native, unlike the 5V TJA1050) plus an OBD-II pigtail to reach pins 6 (CAN-H) and 14 (CAN-L), so nothing on the vehicle is cut. Watch for a termination resistor fitted on the transceiver board: the vehicle bus is already terminated at both ends and a third resistor disturbs it. Power the device from USB while developing rather than OBD pin 16, so a crash cannot load the vehicle supply. +- A `VehicleService` module, and a desktop stub so the logic is testable without a car. + +**Phase 1 is already confirmed on the van**: an ELM327 running Torque pulls PIDs from it today, so the OBD port is live, CAN is reachable there, and the standard request/response layer answers. Nothing further needs discovering before building the seam. + +**Phase 2 is the open question, and one free test decides it.** Torque and ELM327 adapters speak the FILTERED request/response layer: you ask for a PID and the ECU replies, which says nothing about what else is on the wire. Torque itself can do the test: it has a dedicated `canbus monitor` protocol (selected in the vehicle profile, beside a hybrid mode that polls OBD2 and monitors at once) and imports DBC files in the PID editor to decode signals out of the stream. Traffic beyond one's own polled PIDs means the body frames are reachable on that port; only one's own requests and replies coming back means they sit on another segment behind a gateway, so phase 2 needs a different tap point. **The constraint is the adapter, not the app**: monitor-all (`AT MA`) is in the genuine ELM327 specification and many clones omit it outright, which makes them fine for standard PIDs and useless for reverse engineering. An ELM327 identifier app probes which AT commands a given adapter actually recognizes, so that is the thing to check first. Worth the five minutes before designing anything for tier 2. + +**Prior art, checked 2026-09-11.** The building blocks are all open source and the closest match is [can_wizard](https://github.com/okhsunrog/can_wizard) (MIT): an ESP32-C3 plus SN65HVD230 sniffer on ESP-IDF's TWAI driver, with hardware acceptance filters, live bus state, error counters and bus-off recovery, which is close to the seam described above. It uses the legacy `driver/twai.h`, deprecated in IDF 6.x, so the new `esp_twai.h` is what to write against. Also useful: [esp32-slcan](https://github.com/mintynet/esp32-slcan) exposes an ESP32 as a standard slcan device so desktop CAN tooling works against it, and [an Alfa Romeo dashboard project](https://github.com/ClaudeMarais/AlfaRomeoGiulia_DashboardInfo_ESP32-S3) runs an ESP32-S3 on two CAN buses at once in a real car. Driving LEDs from OBD2 RPM is well-trodden ground, which is another way of saying tier 1 carries little risk. + +**What is NOT available, and it shapes the design.** [awesome-automotive-can-id](https://github.com/iDoka/awesome-automotive-can-id), the main community collection, carries VW data for the MQB platform, Golf, Polo, Touran and e-Up, but **nothing for the Transporter and no lighting signals for any VW**: its coverage is drivetrain and engine management, not body control. So tier 2 starts from an empty database however much searching precedes it, which is exactly why the frame IDs belong in configuration a user discovers and shares rather than in a compiled-in table. (T6 owner forums do report the reverse signal traveling over CAN, so the body signals exist on a bus; they are simply not publicly documented.) + +**Related:** a convoy light show across several cars needs nothing from this. That is MoonSync (planned, not built) synchronizing timebase and effect state between devices, with each car rendering locally through whatever driver it has. The two compose for free and neither blocks the other. diff --git a/docs/work/future/backlog-ui.md b/docs/work/future/backlog-ui.md new file mode 100644 index 000000000..738a7653b --- /dev/null +++ b/docs/work/future/backlog-ui.md @@ -0,0 +1,124 @@ +# Backlog: UI + +To-build items for the web UI, grouped by module, each with its fields on one line. Other areas: [the future index](index.md). + +## FixtureProfilesModule + +### Sparse fixture-profile editor + +Kind: Feature + +- **Sparse fixture-profile editor.** A FixtureProfiles row currently shows one role Select per channel across the whole `channels` width: including the unmapped `, ` gaps a wide moving head has between its functions. For a fixture you usually only care about the few channels you drive (rgb, pan, tilt). The refinement: show only the *mapped* channels + an "add channel" affordance (pick a role → fills the first gap or grows the fixture), over the unchanged dense `roles[]` storage. A first attempt shipped and was reverted for edit bugs; redo it cleanly (the dense editor is the reliable interim). Prior art: GDTF / QLC+ fixture profiles (a fixture is a sparse `{channel → function}` map, not a dense per-channel array). + +## Across modules + +### The update overlay's Cancel button does nothing on a plain OTA (2026-09-14) + +Kind: Bug + +`showUpdateOverlay` shows a Cancel button on every firmware install and wires it to +`POST /api/firmware/cancel` (`src/ui/app.js:1479`). Only MoonBase implements that route +(`moonbase/main/moonbase_main.cpp:532`); the application's dispatch table has none, so the request +404s and the `.catch(() => {})` swallows it. The button therefore works during the MoonBase phase of +an install and is inert for a plain app OTA on a device without MoonBase. + +A file upload still cancels there, but by a different mechanism: `uploadCtl.abort()` drops the +connection on the line above. So the two install sources behave differently behind one button, and a +URL install on a non-MoonBase device is the case where pressing Cancel does nothing at all. + +Two ways out, and the choice is the question. Implement the route in the application, which means an +abort flag the OTA task polls between chunks, the shape MoonBase already uses. Or hide the button +where it cannot act, which is honest but leaves a long download uninterruptible. The first is the +better system and the larger change; the second is a few lines. Found while writing +[Update MoonLight](../../moonwalk.md#update-firmware), which deliberately does not mention Cancel: +documenting a button that half-works is worse than leaving it undocumented until it is one thing or +the other. + +### Split app.js by feature, and take the module names out of it (2026-10-15) + +Kind: Restructure + +The UI ships as one minified bundle with ETags, so splitting `app.js` costs nothing at runtime, and at 9,200 lines a feature is hard to find. [The rule](../../contributing/coding-standards.md#per-type-behavior-lives-with-the-type) is that `app.js` dispatches on control types and never on module names; it names about fifteen types today, from `renderFileManager` and the firmware install picker to `ALWAYS_SUBSCRIBED`, the System and Network lookups, the MoonStats and MoonTalk consent paths and the Gamepad and MIDI services. The textbook shape is a registry (`Map`) the modules fill from their own files, each widget a Custom Element with data in through a property and changes out through `/api/control`, so a new module's UI is one file and one registration. The File Manager goes first as the hardest case, and `fillListDetail` learns nested object arrays so a tabular module needs no full widget. The field survey behind this shape, Home Assistant cards and VS Code contribution points: [the UI extensibility analysis](analysis-ui-extensibility.md). + +### File Manager follow-ups + +Kind: Feature + +Deferred capabilities of the [File Manager](../../moonmodules/core/system.md#file-manager), each self-contained: + +- **Upload, recursive folder tier.** Multi-file / recursive folder drops: client-side `mkdir` + walk via `dataTransfer.items` `webkitGetAsEntry()`. +- **Download, folder as `.zip`.** Per-file download streams (`` on `/api/file`). A folder download needs the browser to walk `/api/dir` recursively, fetch each file, and build a `.zip` client-side — which means bundling a zip library into `app.js`. That's a permanent app.js size bump, and app.js is embedded in firmware, so it weighs on the flash budget. Gate on real demand; symmetric with the folder-upload tier. +- **Last-modified dates.** Needs a time source (NTP) + LittleFS mtime storage; the tree is name + size until both land. Backlogged with the NTP work. +- **`.ml` syntax highlighting in the editor.** MoonLive source wants *highlighting* (a color layer over the textarea), not the JSON-style reformat — a bigger editor change (a highlight overlay or a small tokenizer), added when MoonLive `.ml` files land on disk. The editor already has an extension seam (`fmPrettify`) for the reformat case; highlighting is the separate, larger tier. +- **Keyboard + screen-reader access for the tree.** The `fm-row` tree entries are mouse-only today (click to select/expand/open). Making them keyboard-operable (focusable, Enter/Space activation) with ARIA tree semantics (`role="treeitem"`, `aria-expanded` from `isOpen`, `aria-level`) is a real accessibility win but adds JS that lands in the firmware-embedded `app.js` (flash cost). Do it when a11y is a stated goal, together across the whole generic UI, not just this panel. + +### UI features deferred from v1 + +Kind: Feature + +What v1's frontend had and this UI lacks, each waiting on the engine work named: + +- **Health panel**: a `
    ` polling `GET /api/test` for a pass/fail table, which needs a runtime `/api/test` that runs the doctest suite; `ctest` covers this for now. +- **Log panel and log channel**: a ring buffer with severity coloring, stick-to-bottom and `GET /api/log` backfill, fed by a WebSocket `{t:"log",m:"…"}` pushed by the server, which needs an engine-side log producer. Both arrive together, when boot, network and persistence logs interest non-developers. +- **A setup dot before a card's name**: needs `setupOk()` and `health()` on MoonModule with a real failure mode; today both would always be `true` and `""`. +- **Module ID beside the name**: when instances need telling apart, such as two effects of one type under one Layer. +- **Category emoji on the card header**: `ROLE_EMOJI` exists in `app.js` for the picker, so showing it per card is a small step if card scannability needs it. +- **A module `category()`**: a taxonomy beyond `role()` for the picker; derived from `role()` until then. +- **Memory split, heap against PSRAM**: splitting `dynamicBytes` further needs `platform::isPsramPointer(p)` or per-allocation tracking. + +Two design questions stay open: + +- **Modifier chain visualization**: the `children[]` order is the apply order (modifiers compose as a chain, M₁∘M₂∘…), so a visual that shows the stacking, and that order matters, helps a user reason about a multi-modifier layer. +- **Canvas or node-graph view**: v2 tried it; powerful for complex setups but it doubles the UI surface, so it waits on user demand. + +### Double-click and multi-click on a button (2026-10-25) + +Kind: Feature + +From ESPHome's trigger vocabulary: the same debounce +timers with more states, so one button does several things. Built when a panel needs more actions +than it has buttons. + +### A desktop build reports firmware "unknown", so the update UI guesses from the browser (202 + +Kind: Bug · Platform: desktop + +Spotted on the container's Firmware card, which showed **Device: macOS arm64** while the image is +Linux amd64. It was not reading the device: `desktopKeyForThisHost()` (`src/ui/install-picker.js`) +and `desktopAssetPrefix()` (`src/ui/app.js`) both sniff `navigator.platform`, which describes the +machine holding the BROWSER. The container makes it visible because there the two always differ, but +the same card lies on any desktop instance opened from another machine. + +The download it offers is the failure mode worth naming: on a Mac the picker serves +`MoonLight-macos-arm64-*.dmg`, a real file that installs and runs perfectly, and updates the wrong +computer. Nothing errors, and the device stays on its old build. + +**Root cause is one missing build flag.** `MM_FIRMWARE_NAME` already exists for exactly this +(`build_info.h`, surfaced as the `firmware` control): `build_esp32.py` passes the variant key, and +the desktop packaging passes nothing, so every published desktop binary falls through to the +`"unknown"` default. `build_info.h:43` even records the assumption that made this safe, "local +builds aren't published" — the container is a published desktop build, which is what retired it. + +**What it would take:** have `package_desktop.py` pass +`-DMM_FIRMWARE_NAME="desktop--"` for the target it packages (the keys the picker's +`DESKTOP_LABEL` map already lists), then delete both browser-sniffing functions and read the +`firmware` control instead. `deviceFirmwareInfo()`'s `isDesktop` test (`firmware === "unknown"`) +needs the same treatment, since a named desktop build no longer matches it: test the +`desktop-` prefix. Local unflagged builds keep reporting `unknown`, where a guess is still better +than nothing. + +Worth doing with the OTA path rather than alone: a desktop cannot install its own archive anyway, so +the honest end state may be a card that names the platform and links the release rather than +offering a Download button that only ever means "fetch a file and swap it by hand". + +### RGBW preview end-to-end + +Kind: Feature + +The light `Buffer` already holds `channelsPerLight = 4` (RGBW), and the device output drivers handle it, but the **preview only ever sends/draws RGB** — the W channel is invisible in the UI. Once the self-describing header lands, carry the W channel on the wire and render it in the shader (W as a warm-white tint / brightness lift on the disc). Small, but gated on the header so it isn't another bespoke opcode. + +### Moving-head preview = peer interpreter + +Kind: Feature · Trigger: moving heads land + +- **Moving-head preview = peer interpreter.** When moving heads land, the previewer must interpret channel semantics (pan/tilt/RGBW-at-arbitrary-indices) to render a moving fixture: the same fixture-profile model physical drivers use, interpreted to screen. This is *why* the increments named the abstraction "interpret the profile" rather than "apply correction / opt out": so Preview becomes a full peer here without a rename. Its own design plan when moving-head support starts. diff --git a/docs/work/future/generative-fields-analysis-bottom-up.md b/docs/work/future/generative-fields-analysis-bottom-up.md deleted file mode 100644 index 60f0ade65..000000000 --- a/docs/work/future/generative-fields-analysis-bottom-up.md +++ /dev/null @@ -1,237 +0,0 @@ -# Generative fields: bottom-up analysis - -> **Forward-looking research document, the exception to CLAUDE.md's present-tense rule.** A Stage-1 bottom-up analysis of the effect family this document names *generative fields*: images computed from noise fields over a coordinate mapping, either fresh every frame (a shader) or by transporting the previous frame through a velocity field (advection). Part 1 states what we want to build in the terms of the field's primary sources. Part 2 lists implementations that do similar things and where each fills in a spot. Part 3 places the family in MoonLight's architecture: what exists, measured, and what is missing. The **top-down** companion (to be written from the prompt at the end) turns Part 3 into the implementation spec. Written 2026-09-03. Modeled on [power-functions-analysis-bottom-up.md](power-functions-analysis-bottom-up.md), which this extends: the fields, shader and particle families it cataloged are taken as read. - -## TL;DR - -> **Status (2026-09-05): the three gaps this document names are closed.** Advection shipped as `draw::advect`/`advect16`, gradient noise replaced value noise behind the same names, and color state above 8 bits shipped as effect-owned 16-bit planes rather than a wide Layer (the top-down's § 11 records why). The "What we need to add" list in Part 3 is therefore built, and the builtin count below (55) is now 67. The rest of the document is a Stage-1 snapshot and is left as written. - - -- **The target is two textbook techniques over one block set.** A **procedural shader**: color as a function of (position, time), computed per pixel from noise sampled through a coordinate transform. **Advection**: the previous frame transported by a velocity field and decayed, with sources drawn into it each frame. Both stand on the same primitives: a polar coordinate mapping, gradient noise with fractional Brownian motion and domain warping, low-frequency oscillators, a contrast window and palette, anti-aliased sub-pixel rasterization, a bilinear resampler, framerate-independent exponential decay, and fixed-point arithmetic with quantization last (Part 1). -- **Every algorithm has a name and an originator.** Perlin noise (1985, 2002), fBm, domain warping (Quilez), semi-Lagrangian advection and stable fluids (Stam 1999, 2003), curl noise (Bridson 2007), flow-field particle tracing (Hobbs; Shiffman), Wu's anti-aliased lines (1991), coverage from signed distance, exponential half-life decay, LFO modulation. None needs more than adds, multiplies, a sine table and a noise function (Part 1). -- **The cost model is arithmetic and decides the design.** A shader costs samples per pixel and scales with area × samples; advection costs a few loads and lerps per pixel per pass and scales with area × channels, plus memory for color state above 8 bits. For both, framerate is part of the rendering method: transport must stay sub-pixel per frame, decay must not step, oscillators must not alias. Throughput in pixels per second, not frames per second, is the honest metric (Part 1 § Cost). -- **The field has converged on the same shapes.** A polar-noise shader engine with an oscillator bank and a coordinate mapper; an emitter-plus-flow advection engine with six flow types, a modulator bank and a half-life fade; both now available in Q16.16 fixed point with bit-exact tests, on the ESP32 class we target; anti-aliased canvases; wave and fluid solvers; particle-trail systems. These are similar to what Part 1 describes, not identical, and each fills in a spot: measured throughput, parameter vocabularies users expect, fixed-point choices that work, and precision decisions (Part 2). -- **MoonLight has most of the blocks and lacks three.** Present: polar addressing, 16-bit value noise with fBm/warp/turbulence, 16-bit oscillators, palettes, a GLSL-vocabulary shader runner, SDF coverage, a 24.8 sub-pixel splat, particles, a persisting Layer buffer, and 55 MoonLive builtins that already express a polar-noise pixel. Missing: **a bilinear resampler of the previous frame** (so no advection), **color state above 8 bits** (so trails posterize and sub-integer accumulation is impossible), and **gradient noise** (value noise reads coarser at low frequency). The MoonLive gap is structural: per-pixel host calls cost ~5 µs per pixel measured, and a script has no frame of state; both families want whole-frame kernels a script composes (Part 3). -- **The ESP32 budget is known.** ~293 cycles per pixel at 128×128 @ 50 fps on 240 MHz; one noise sample per pixel measures ~750 cycles on the S3 today. A rich shader is a panel-class effect on any MCU; advection scales to walls in cycles and needs PSRAM for state. Every target has an FPU, the P4 and S31 add SIMD and hardware loops, the P4 measures ~3× the S3 and a desktop core 20-40×, so the P4 and S31 are where this family shines on an MCU: the ESP32 class stops at about one noise sample per pixel on a 128² wall, and the desktop continues on the same contract and drives the wall over the network. The levers are standard: LUTs, fixed point, fewer samples, a field below output resolution, a field below frame rate, per-target FPU and SIMD behind one contract (Part 3 § Budget, § Per-target headroom). -- **Out of scope for Stage 1.** API names and signatures; the 8-bit versus 16-bit buffer decision; where advection state lives; the MoonLive frame-kernel shape; which showcase effects come first. All Stage 2 (Part 3 § Bridge). - -## Why this document exists - -The product owner's goals for this family, recorded 2026-09-03: - -1. **MoonLight supports the building blocks of these effects**, via compiled functions and especially via MoonLive effects, on the power-function library ([power-functions.md](../../moonmodules/light/power-functions.md)), the same way it carries particles and SDFs. -2. **A few genuinely beautiful showcase effects** are built on those blocks, as the proof the blocks are right. -3. **The ESP32 is CPU-bound; memory is not the constraint.** Every choice is made against the per-pixel cycle budget, and the fact that these effects look better the higher the framerate is a design input. -4. **Industry-standard terminology and algorithms throughout**, per [CLAUDE.md § Principles](../../CLAUDE.md#principles): the textbook construct, named by its textbook name, from the primary source. - ---- - -# Part 1: What we want to build - -Stated from the primary sources only. Where a name is given, it is the algorithm's name in the literature; where a person is named, it is the originator of the algorithm, not an implementation. - -## The two techniques - -**A procedural shader** computes each pixel's color as a function of its position and the current time: `color = f(x, y, t)`. Nothing has to survive between frames, which is what makes it composable; state is welcome whenever it saves work: a polar table cached per geometry, a field rendered below the frame rate and interpolated, the dithering error carried forward, a value fed back into the next frame's parameters. Composition happens on the *coordinate*: transform where the pixel samples from (rotate, scale, fold, displace by another field) and the image transforms with it. This is the shader model of the GPU tradition, applied on a CPU one pixel at a time. - -**Advection** transports a quantity (here: the color already in the frame) along a velocity field. Each frame: sources add color where they are drawn; every pixel's new value is the old value found by stepping *backward* along the velocity (the semi-Lagrangian method: Stam, *Stable Fluids*, SIGGRAPH 1999; *Real-Time Fluid Dynamics for Games*, GDC 2003), read with bilinear interpolation; and the result decays. The frame buffer is the simulation state. - -They differ in one property that decides everything downstream: a shader needs no state and its cost is samples per pixel, with state used to cut samples; advection is all state and its cost is a resample per pixel per pass plus the memory to hold color above 8 bits. They share every block below the top. - -## The blocks - -### Coordinate mapping - -- **Cartesian to polar.** For every pixel, `r = hypot(x − cx, y − cy)` and `θ = atan2(y − cy, x − cx)` about a center. Precomputed once per geometry into two tables; nothing trigonometric runs per pixel at render time except the sine and cosine of a *modulated* angle. Polar addressing is what makes motion rotate around a center rather than slide across the panel. -- **Polar transforms.** Add to θ to rotate; multiply r to zoom; add `k·r` to θ to twist into a spiral; fold θ into n mirrored wedges for a kaleidoscope (`θ' = |((θ mod 2π/n) − π/n)|`); take `1/r` for a tunnel. Each is one operation on the coordinate before sampling. -- **Domain warping.** Replace `f(p)` by `f(p + g(p))` where `g` is itself a noise field (Quilez, *Domain warping*). The canonical forms are `fbm(p + fbm(p))` and `fbm(p + fbm(p + fbm(p)))`; the intermediate displacement vectors are free color inputs. Applied in polar space, warping the angle makes the field swirl and warping the radius makes it breathe. -- **Normalized shader space.** Center the pixel and scale by the short side so a circle stays circular on a non-square panel; the GLSL `uv` convention. - -### Noise - -- **Gradient noise** (Perlin, *An Image Synthesizer*, 1985; *Improving Noise*, 2002): a pseudo-random gradient at each lattice corner, a dot product with the offset, a quintic fade, trilinear interpolation. Smooth to the first derivative, statistically isotropic, zero-mean. Simplex noise (Perlin 2001) is the same idea on a simplex lattice, cheaper in higher dimensions. **Value noise** interpolates random values at lattice corners instead; it is cheaper and visibly lumpier at low frequency because its extrema sit on the lattice. -- **Fractional Brownian motion (fBm).** Sum octaves at doubling frequency and halving amplitude (`lacunarity = 2`, `gain = 0.5`); structure at every scale. **Turbulence** sums the absolute value instead; the creases read as flame and smoke. Octave count is the primary cost knob. -- **Noise as a source of motion.** The third noise dimension advanced with time evolves a 2D field without scrolling it. A noise value mapped to an angle (`θ = 2π · noise`) is the classic flow-field construction (Hobbs, *Flow Fields*; Shiffman, *The Nature of Code*, autonomous agents). -- **Curl noise** (Bridson, Houriham, Nordenstam, *Curl-noise for procedural fluid flow*, SIGGRAPH 2007). In 2D the velocity is the perpendicular gradient of a scalar noise potential, `v = (∂ψ/∂y, −∂ψ/∂x)`: exactly divergence-free, so transported color swirls and never piles up, with two finite differences per pixel and no solver. - -### Time: oscillators and modulation - -- A **timer bank**: N independent clocks, each `phase_i = (t + offset_i) · ratio_i`, all scaled by one master speed so a single knob scales the whole animation. -- From each clock the four standard **LFO shapes**: a ramp (the phase itself, for scrolling and zoom), a wrapped phase in `[0, 2π)` (rotation), a bipolar sine in `[−1, 1]` (breathing), and a noise-driven angle `2π · noise(phase)` (wandering). Unipolar variants in `[0, 1]`. -- **Modulation**: a parameter bound to a clock and a depth, applied multiplicatively about its base value (`p' = p · (1 + depth · lfo)`), so nothing in the image is constant and every parameter can breathe. The synthesizer model, by its synthesizer names. -- **Framerate independence.** Every rate is per second, integrated with the measured `dt`; a virtual clock `t += dt · speed` keeps oscillators and decay in step under one speed control. - -### Color - -- **Contrast window**: clamp the field value to `[low, high]` and rescale; a black point and a white point, which is how a soft noise field becomes shapes with edges. -- **Palette mapping**: the windowed value indexes a gradient; or three field layers drive R, G, B directly; or a **cosine palette** (`a + b·cos(2π(c·v + d))`, Quilez) gives a whole ramp from twelve constants. -- **Gamma** as the last nonlinearity before output, and **quantization last**: compute in more than 8 bits, round once, with **temporal dithering** (error spread across frames) where the output is 8-bit. - -### Sources: anti-aliased rasterization - -Anything drawn directly into the frame, at fractional coordinates, additively: - -- A **disc** by coverage from its signed distance: `cov = clamp(radius + 0.5 − dist, 0, 1)` per pixel; edges anti-alias for free. -- A **line** by Wu's algorithm (1991) or its generalization: step along the segment and distribute each step over the 2×2 neighbors with bilinear weights `(1−fx)(1−fy), fx(1−fy), (1−fx)fy, fx·fy`. The same 2×2 **splat** places a point source at sub-pixel precision. -- Sources move on the oscillators above: orbits, Lissajous curves (`x = A·sin(a·t), y = B·sin(b·t + φ)`), swarms with Reynolds steering, and borders or masks that are static geometry. - -### Transport: the velocity field and the resampler - -- **Velocity as a rule per pixel**, evaluated at sample time, not stored: a uniform wind (direction, speed, optional rotation); radial in or out; a spiral (rotate by α, move radially by ρ, in polar space); zoned rings (smoothstep-blended radii, each with its own swirl or drift); noise-driven shift (two independent 1D noise profiles, one per axis, applied separably); curl noise; or a stored field from a solver. -- **The resampler**: for each destination pixel, `src = dst − v·dt`; read the previous frame at `src` with **bilinear interpolation** (four loads, three lerps per channel); **wrap** at edges for a seamless tile or **clamp** so color flows off. A separable field (row shift then column shift) halves the work: two 1D passes through a scratch buffer. -- **Partial transport**: `out = (1 − b)·current + b·sampled` mixes what arrives with what was there; it reads as viscosity without a solver. -- **Stable fluids** (Stam 1999) when real fluid behavior is wanted: diffuse velocity, project to divergence-free (Jacobi or Gauss-Seidel iterations on a Poisson equation), self-advect, project again, optionally reinject lost swirl by **vorticity confinement** (Fedkiw, Stam, Jensen 2001), then diffuse and advect the dye. Orders of magnitude costlier than a velocity rule; curl noise gets most of the look for none of the solve. - -### Decay - -- **Exponential decay by half-life**: `k = 0.5^(dt / t½)`, multiply every channel by `k` each frame. Framerate-independent by construction; a half-life in seconds is the user-facing knob. A per-frame constant multiply (the 8-bit "fade by" idiom) is the same thing only at one fixed framerate. -- **Blur** as a second decay, separable, for softening trails. - -### Arithmetic - -- **Fixed point** throughout the hot path: Q16.16 for coordinates, velocities and color state; angles as a fraction of a turn in an integer (a 16-bit or 24-bit turn) so sine and cosine are table lookups and wraparound is free; square roots avoided by comparing squares or by an integer `isqrt`. -- **Lookup tables** for everything per-geometry (polar), per-turn (sine, cosine), per-curve (fade, gamma). -- **Precision graded by role**: the noise inner loop tolerates 8 fractional bits at a quality cost; color state does not. - -## Cost - -Per pixel, a shader pays `samples × noise_cost + transform + window + palette`; rich compositions run 3 to 12 samples. Cost is `area × samples`. Advection pays, per pixel per pass, four loads, three lerps and one multiply per channel, and per frame the field rule; cost is `area × channels × passes`, independent of noise, plus color-state memory of `area × channels × 4 bytes` (and a scratch copy, and any stored field). A 16-bit or wider color state is not optional for advection: an 8-bit state cannot hold sub-integer accumulation from splats or a slow decay, and a trail fading through 256 levels posterizes at the low end. - -**Framerate is part of the rendering method.** Transport per frame is `v · dt`; when it exceeds about a pixel the bilinear filter smears instead of moving and edges tear. A decay of `0.5^(dt/t½)` with a short half-life becomes a strobe at low fps. Temporal dithering averages only above the eye's integration rate. Oscillators sampled below twice their rate alias. The consequence is a design rule: the per-frame cost must stay small enough that the frame rate stays high, and the metric to report is **pixels per second**, which is what the frame rate at a given area actually measures. - -The standard levers, in the order they are usually pulled: lookup tables; fixed point; fewer samples (octaves, warp depth); render the field below output resolution and upscale bilinearly (a smooth field hides it); update the field below the frame rate and interpolate; vectorize the noise inner loop where the CPU has SIMD; use the hardware FPU for the kernels that are float by nature (square roots, arctangents, a reference algorithm kept in float), behind the same contract; move the field update to a second core; and above all of these, run the field on a machine with the cycles and feed the lights over the network. - ---- - -# Part 2: Examples from the field - -Implementations that do similar things to Part 1. None is the specification; each fills in a spot: a measured number, a parameter vocabulary users expect, a fixed-point choice that is known to work, a precision decision. - -| Example | What it is | Similar to | What it fills in | -|---|---|---|---| -| **ANIMartRIX** (Stefan Petrick; also in FastLED master as `fl::Animartrix`) | A polar-noise shader engine: per-pixel polar tables, a timer bank yielding ramp / phase / bipolar sine / noise-angle signals, a "5D coordinate mapper" (`newx = (offset_x + cx − cos θ·r)·scale_x`, likewise y, `z` from a ramp), gradient noise per layer, a low/high contrast window, per-channel or palette color, gamma, sanity clamp; 50 named animations of 1 to 12 layers | Part 1 shader, polar transforms, LFO bank, contrast window | The author's throughput on the Teensy 4.0 the engine was tuned on: ~730 k RGB pixels/s per layer at 600 MHz ("20 fps on 36k LEDs"), 53 k/s on an ESP32 core, 110 k/s on two; "3 layers at 400+ fps, 10 layers at ~50 fps" on a 16×16 class panel. Float throughout, quantized to 8 bits "in the very last step", temporal dithering. The parameter names users of this style know: `scale_x/y/z`, `offset_x/y/z`, `z`, `center`, `low_limit`, `high_limit`, `master_speed`, `ratio`, `offset` per timer | -| **FastLED master's fixed-point migration of the same engine** (`3e77a096`, 2026-09-02) | Q16.16 (`s16x16`) throughout; angles as A24 (24-bit turn) into `sincos32`; four gradient-noise precisions side by side (`s16x16` reference, `q16`, an `i16`-optimized inner loop "2× faster multiplies", `s8x8` "4× faster, trades accuracy"), a 4-wide SIMD noise; **bit-identical tests** against the float engine | Part 1 arithmetic | Proof that the whole shader family is exact in Q16.16 with a table sine and a table-driven gradient noise, on the ESP32 class; the precision-by-role grading; a fixed-point type family documented for MCUs ("integer math is 5-100× faster … no rounding errors; results are exact and reproducible") | -| **FlowFields** (4wheeljive, from a 2026 concept post by Petrick; forks: `ewowi/flowfields`, Petrick's `ColorTrails`) | An advection engine: **emitters** ("anything that is drawn directly") plus **flows** ("an invisible wind that moves the previous pixels and blends them together"). Six flows as displacement rules (noise via two decoupled 1D profiles, radial, directional wind with a perpendicular wobble, three-zone rings with swirl and drift, spiral, and a full stable-fluids solver with vorticity confinement), eight emitters (orbital, swarming and audio dots, Lissajous line, rainbow border, noise kaleidoscope, cube, fluid jet), a 20-timer modulator bank with multiplicative "breathing", float RGB grids, a half-life fade `0.5^(dt/persistence)`, dithered 8-bit output, a virtual clock under one `globalSpeed` | Part 1 advection, velocity rules, sources, decay, modulation | The flow vocabulary and defaults users expect (`persistence`, `blendFactor`, `windStep`, `angularStep`/`radialStep`, `innerSwirl`/`outerSwirl`/`midDrift`, `xShift`/`yShift`, `noiseFreq`, `viscosity`, `vorticity`, `gravity`; ~35 named controls); that separable noise transport with decoupled axes reads as diagonal flow; that partial transport reads as viscosity; the solver's cost (5 Jacobi iterations per solve, three solves per frame) as the upper bound | -| **`fl::FlowField`** (FastLED master, from the above, 2026-03-21) | The same engine distilled to two emitters and the noise flow, in a float variant and a **Q16.16 variant** with `i32` color state ("allowing sub-integer color accumulation during splats") quantized at output; coverage discs and 2×2 bilinear lines; a "noise punch" impulse into the profiles | Part 1 resampler, sources, decay, arithmetic | The measured share: **advection ~80% of frame time** in fixed point, with the hoisting and `restrict` tricks that get it there; six 32-bit grids (384 KB at 128²), hence "memory is large"; defaults `persistence` 0.86 s, `flow_shift` 1.8 px, `noise_freq` 0.33/0.32 | -| **`fl::gfx` canvas** (FastLED master) | An anti-aliased 2D canvas: line, disc, ring, stroked line with caps, additive by default, float / int / fixed-point coordinates | Part 1 sources | The API shape a sub-pixel rasterizer converges on | -| **Fixed-point sub-pixel graphics** (Sutaburosu; the anti-aliased canvas demo in MoonLight as `FixedPointCanvasDemoEffect`, and in FastLED master as the fixed-integer drawing `fl::gfx` credits him for, the SKIPSM Gaussian blur behind `fl::gfx::blur`, and the Elias water effect `FxWater`) | Twelve sub-demos on a Q16.16 anti-aliased canvas (clock, orbiting discs, star web, spirograph, Lissajous, thin and thick cube, organic walkers, boids, hypotrochoid, branching tree), "blazing fast fixed integer drawing"; a binomial two-pass Gaussian blur; a 2D ripple simulation after Hugo Elias | Part 1 sources, arithmetic, blur | That anti-aliased sub-pixel drawing is fully deterministic and FPU-free in Q16.16 at panel-to-wall sizes; the blur kernel shape a trail idiom converges on; a working catalog of source geometries (orbits, Lissajous, spirograph and hypotrochoid curves, boids, a recursive tree) for the emitter set. The canvas demo itself is parked by the product owner as a port candidate | -| **`fl::WaveFx`** (FastLED master, after Shawn Silverman) | A 2D wave-equation simulation with 2× to 8× supersampling, mapped to color by gradient | Part 1 stored-field transport | Supersampling as the artifact lever for a stored field; the comment that 2× "gives the best results for the CPU consumption" | -| **`fl::Luminova`** (FastLED master) | 256 particles with per-frame fade and blur, "soft white trails" | Part 1 decay + blur, with particles as sources | Fade-plus-blur as the trail idiom for point sources | -| **Flow-field generative art** (Hobbs; Shiffman) | A grid of angles from noise; particles or pen strokes follow the angle in small steps | Part 1 noise-as-motion | The tracing form of a flow field, where the particle is the source and the field never touches the frame; Hobbs's advice to distort with something other than Perlin noise once the look is familiar | -| **Stable fluids** (Stam 1999, GDC 2003) | The reference solver and its "linear backtrace" advection | Part 1 transport | The algorithm every advection effect above descends from, and the reason semi-Lagrangian advection is unconditionally stable at any `dt` | -| **Earlier 8-bit work in the same idiom** (FunkyNoise, FunkyClouds, 2014; a self-modulating simplex noise gist) | Polar angle per pixel shifted by noise; noise modulating the offsets, scale and palette index of the next noise pass | Part 1 warping, feedback modulation | That the idiom predates FPUs and was first done in 8-bit FastLED terms | -| **MoonLight's port of ColorTrails** (2026-03) and **FastLED-MM's FlowFields sketch** (2026-04/05) | The advection engine on `fl::CanvasRGB` + `s16x16`; the same engine at 128×128 inside MoonLight's earlier module runtime with 35 registered controls | Part 1 advection, in our own prior work | That the family has already run on our hardware and behind our UI; a bench reference for the advection showcase | - -Two things the examples agree on that Part 1 states as requirements: **compute above 8 bits and quantize last**, and **report pixels per second**. One thing none of them does yet: curl noise as a velocity rule, which Part 1 includes because it is the standard cheap answer to fluid-looking flow. - - ---- - -# Part 3: How this fits MoonLight - -## What we have - -Against Part 1's blocks, measured on this tree: - -| Part 1 block | MoonLight today | Status | -|---|---|---| -| Polar mapping | `atan16`, `dist16`, `kaleido` ([math16.h](../../src/core/util/math16.h)), computed per pixel; `PolarNoiseEffect` uses them | ✅ per pixel; ⬜ no precomputed LUT type | -| Polar transforms, `uv` | `shader.h`: `uv`, `rotate`, `repeat`, `mirror`; `kaleido` | ✅ | -| Noise | `inoise8`/`inoise16` **value** noise; `fbm8`/`fbm16`, `turbulence8`, `warp8` ([noise.h](../../src/core/util/noise.h)) | ✅ fBm, warp, turbulence; ⬜ gradient noise; ⬜ curl noise | -| Oscillators | `BeatPhase`, `beat`/`beatsin`, `sin16`/`cos16`, `smoothFollow`, easings, `hashInt` | ✅ single phase; ⬜ timer bank with the four LFO shapes and modulation binding | -| Contrast, palette | `map32`, `smoothstep`, `colorFromPalette`, `cosPalette` | ✅ | -| Gamma, dithering | `Correction` curves at the driver | ✅ gamma at output; ⬜ temporal dithering | -| Sources | `coverage` (SDF), `draw::splat` (24.8 sub-pixel, additive), `draw::line` (Bresenham, not anti-aliased), `particles.h` | ✅ disc, splat; ⬜ anti-aliased line | -| Resampler | `draw::scroll` (integer shift, wrap or clear) | ⬜ **no bilinear sample of the previous frame** | -| Velocity rules | none | ⬜ | -| Decay | `Layer::fadeToBlackBy` (8-bit multiply, once per frame, MIN across effects) | ✅ 8-bit; ⬜ half-life form on wide state | -| Blur | `draw::blur`, separable, every axis | ✅ | -| Color state | `Layer` buffer, `uint8_t` per channel ([Buffer.h](../../src/light/layers/Buffer.h)), persisting between frames | ✅ persistence; ⬜ **no state above 8 bits** | -| Fixed point | 16-bit contract, uint8 angle / `angle16` turn, 24.8 positions, 16.0 noise coordinates ([power-functions-analysis-top-down.md § 2](power-functions-analysis-top-down.md)) | ✅ | -| Shader runner | `shader.h`: `each` (one function of position and time, the loop, mapping and write handled) | ✅ | -| Particles | pool, gravity, drag, bounce, collide, splat render | ✅ | -| Stored-field simulation | none | ⬜ (out of scope unless the top-down wants fluid) | - -The persistence contract advection needs already exists: the Layer does not clear between frames, and "a read-prior effect reads last frame's pixels via `draw::get` / `draw::blur`; the persistence *is* its state" ([MoonLight, buffer persistence](../../explanation/architecture/moonlight.md#buffer-persistence-the-layer-keeps-what-it-drew)). What is missing is the resampler and the bit depth. - -`PolarNoiseEffect` is the Part 1 shader already: polar addressing, `warp8` in polar space (the angle warped by noise), `kaleido`, a palette, with `octaves` and `warp` exposed as the cost knobs and the header stating the cost ("~4 samples/pixel at octaves=2 … on a large wall drop `octaves` to 1"). - -## What MoonLive has - -55 builtins ([MoonLiveBuiltins_light.h](../../src/light/moonlive/MoonLiveBuiltins_light.h), [MoonLiveBuiltins_common.h](../../src/core/moonlive/MoonLiveBuiltins_common.h)): `noise`, `polarA`/`polarR`, `sin`/`cos`, `beat`/`beatsin`, `smoothstep`/`step`/`smin`, `uvX`/`uvY`, `scale`, `mod`/`div`/`fdiv`, `setRGB`/`setXYZ`/`setPaletteColor`/`fill`/`fade`/`line`, the audio set, and the particle set. A script can write a Part 1 shader pixel today (`octopus.mle` is one). The measured cost is the call rate, not the arithmetic ([backlog-moonlive.md § 4c](backlog-moonlive.md)): `plasma.mle` at 9 host calls per pixel is 16,031 µs on a 3,840-pixel S3 fixture (~4.2 µs/px); `polarR` alone ~3.5 µs/px because it wraps a real square root; the particle vocabulary, "one call per FRAME rather than per pixel", is 54× cheaper on the same fixture. The roadmap's conclusions apply unchanged: "per-pixel builtins want to be inline ops, not calls" and "a `frame()` entry shape would sidestep it entirely". - -Advection is not expressible from a script: the 64-byte arena holds no frame of state and no builtin resamples the previous frame. The particle pool shows the shape that works for stateful families: a handle to native state, whole-pool passes per frame, the script composing them. - -## The budget, measured - -Per-pixel cycles at 240 MHz ([power-functions-analysis-bottom-up.md § Shaders](power-functions-analysis-bottom-up.md)): ~15,600 at 16×16 @ 60 fps, **~293 at 128×128 @ 50 fps**. The S3 render-only sweep ([performance.md](../../reference/performance.md)), µs per frame: - -| Effect | 16² | 32² | 64² | 128² | per pixel at 128² | -|---|---:|---:|---:|---:|---:| -| Noise (simplex, 1 sample/px) | 913 | 2,951 | 11,661 | 51,230 | 3.1 µs (~750 cycles) | -| Plasma | 352 | 1,020 | 3,744 | 20,020 | 1.2 µs | -| Metaballs | 462 | 1,757 | 6,108 | 28,576 | 1.7 µs | -| LavaLamp | 309 | 974 | 3,612 | 21,243 | 1.3 µs | - -On the classic ESP32 (no FPU, no PSRAM) the Noise effect runs 1,117 / 324 / 71 / 17 fps across the four sizes. So one noise sample per pixel is already 2.5× the wall budget, which is consistent with the field's own numbers in Part 2 once clock and FPU are normalized: **the shader half is a panel-class family on every MCU, and the engineering is in Part 1's levers.** Advection scales to walls in cycles (no noise per pixel; the field's noise is per row and column) and its cost is state: six 32-bit grids at 128² are 384 KB, PSRAM territory, and a panel-class family only on a PSRAM-less classic. - -Framerate protection is therefore the rule for both, in different currencies: **samples per pixel** for a shader, **cycles per frame and bytes of state** for advection. - -## Per-target headroom: FPU, SIMD, clock, and where the desktop takes over - -What each shipped target brings, from the IDF SoC capability headers and our own measurements: - -| Target | Cores × clock | FPU | SIMD | State memory | Measured against the S3 | -|---|---|---|---|---|---| -| classic ESP32 (Xtensa LX6) | 2 × 240 MHz | single precision | none | internal only, or 4 MB PSRAM on WROVER / 2 MB on PICO | slower; beats the S3 only on memory-bound loops (internal RAM vs PSRAM latency) | -| ESP32-S3 (Xtensa LX7) | 2 × 240 MHz | single precision | 128-bit PIE (`SOC_SIMD_INSTRUCTION_SUPPORTED`) | 8 MB octal PSRAM | the reference row in this document | -| ESP32-P4 (RISC-V) | 2 × 400 MHz | single precision | PIE + hardware loops (`SOC_CPU_HAS_PIE`, `SOC_CPU_HAS_HWLOOP`) | 32 MB PSRAM | ~3× on heavy compute ([performance.md](../../reference/performance.md)) | -| ESP32-S31 (RISC-V) | 2 × 320 MHz | single precision | PIE + hardware loops | PSRAM | between the S3 and the P4 | -| desktop | GHz class | double and single | NEON / SSE / AVX | unbounded | 20-40× an S3 per core, plus SIMD ([performance.md](../../reference/performance.md), the `collide` measurement) | -| Teensy 4.x (Cortex-M7), a future target | 1 × 600 MHz | single and double | none (DSP instructions) | 1 MB internal, no PSRAM | not measured; listed in [architecture.md § Scaling to available memory](../../reference/hardware/firmware-variants.md#scaling-to-available-memory) as a supported class | - -**Can the FPU help?** Every target has one, so a float kernel is legal everywhere, and the repo already has the precedent: `raymarch.h` is compiled only where the SoC declares an FPU, as "the one bounded exception to the integer-only render path", while `shader.h` stays fixed point and runs everywhere ([power-functions.md § Raymarching](../../moonmodules/light/power-functions.md#raymarching-one-technique-inside-a-shader)). The honest expectation: on these cores a float multiply costs about what an integer multiply costs, so an FPU does not make a noise sample cheaper; it makes square roots, arctangents and trig cheap enough to skip the tables, and it lets a float reference algorithm run unconverted where an exact fixed-point port is not worth writing yet. The portable contract stays fixed point; the FPU is a per-target acceleration behind it, per the standing decision. - -**Where the family shines on an MCU.** The S3 is the baseline this document measures against because it is the bench board with numbers, not because it is the target. The P4 is the natural home of the shader half: the highest clock, four-lane SIMD, hardware loops and 32 MB of PSRAM put a 64² composition and a 128² single-layer field inside its budget, and the S31 sits next to it on every axis. The classic is the portable floor, the target that keeps the contract honest. The top-down should size the showcases for the P4 and S31, keep them running on the S3, and let the classic degrade by the cost knobs. - -**Can SIMD help?** Yes, and the P4 is where it pays: PIE processes four 32-bit lanes per instruction and the hardware loop removes the branch per iteration, which is exactly the shape of a noise inner loop, a bilinear lerp across four channels, or a separable advection pass. The S3 has the same width. This is the lever the field has already pulled (a 4-wide fixed-point noise exists in the wild), and it is a per-target implementation of the same function, never part of the contract. The classic has neither, which is why it is the target that decides the portable budget. - -**Where the border is.** Per pixel at 50 fps, the cycle budget is `clock / (area × 50)`; one noise sample costs ~750 cycles on the S3 today, ~250 on the P4 by the measured ratio, ~20-40 on a desktop core. Samples per pixel affordable at 50 fps, rounded down: - -| Grid | S3 (240 MHz) | P4 (400 MHz, ~3×) | desktop (one core, ~30×) | -|---|---:|---:|---:| -| 16×16 | 24 | 72 | hundreds | -| 32×32 | 6 | 18 | ~180 | -| 64×64 | 1 | 4 | ~45 | -| 128×128 | 0 (one sample = 19 fps) | 1 | ~11 | - -So the ESP32 class stops at one sample per pixel on a 128² wall (S3) to one or two (P4), and carries a rich 3-to-12-layer composition only up to about 32×32 (S3) or 64×64 (P4). Beyond that line the desktop continues without a change of code: it is the same effect on the same contract, with SIMD and the clock on its side, and it already drives lights over the network as a processing node ([MoonLight, drivers](../../explanation/architecture/moonlight.md#drivers): ArtNet, DDP, E1.31). Advection moves the border differently: its per-pixel cost is fixed and small, so the S3 and P4 carry it to a 128² wall as long as the wide color state fits PSRAM; the classic without PSRAM stops at panel size for lack of memory, not cycles. - -## What we need to add - -In Part 1's order, against the table above: - -1. **Gradient noise** beside value noise, in the existing 16-bit fixed vocabulary, as the quality upgrade for every field effect; keep value noise as the cheap inner-octave option (Part 2's precision-by-role grading). -2. **A polar LUT type**: angle and radius per pixel, built on the cold path, invalidated on geometry change; `PolarNoiseEffect` and every polar effect read it instead of calling `atan16`/`dist16` per pixel. -3. **An oscillator bank**: N clocks with offset and ratio under one master speed, the four LFO shapes, and a modulation binding (`parameter · (1 + depth · lfo)`), as one power function family rather than per-effect members. -4. **Curl noise** as a velocity rule, and the plain rules (wind, radial, spiral, rings, separable noise) as small functions of (pixel, t). -5. **The resampler**: bilinear sample of the previous frame at a fractional source, wrap and clamp variants, separable and full 2D forms. This is the advection kernel and the single largest missing block. -6. **Half-life decay** and an **anti-aliased line**, completing the sources-and-decay set beside `splat` and `coverage`. -7. **Color state above 8 bits** for advection and for dim trails: either a 16-bit Layer buffer (the LED-driver analysis already backlogs a "16-bit pipeline + dither", [backlog-light.md](backlog-light.md)) or effect-owned Q16.16 state quantized into the 8-bit Layer each frame. The examples are unanimous about *where* quantization goes (last) and split on *where the state lives*; the top-down decides. -8. **Temporal dithering** at the quantization step, once the state is wide. -9. **MoonLive frame kernels**: the prepare / emit / advect / decay passes and the polar-shader inner loop exposed as whole-frame builtins (inline ops or handle-based native passes), so a script composes frames the way it composes particle passes. Depends on the multi-argument host-call and arena work the language roadmap already names. -10. **Showcases**, three: a pure shader in the polar-noise idiom beyond `PolarNoiseEffect`; an advection effect with the noise, spiral and directional rules, two sources and the oscillator bank; and one that feeds a shader's output into a flow. - -## Bridge to the top-down - -The bottom-up settles what the top-down can take as given: the family is two techniques over one shared block set, every block has a textbook name and algorithm, the cost model is arithmetic and makes a shader a samples-per-pixel problem and advection a cycles-per-frame-and-memory problem, framerate is the rendering method for both, the field has proven the whole set exact in Q16.16 on our silicon class, and MoonLight lacks three blocks (resampler, wide color state, gradient noise) plus the bank, the LUT and the kernels that make them composable. What it leaves open is design: names and signatures in our fixed-point vocabulary; where wide state lives and whether the Layer goes to 16 bits; the MoonLive frame-kernel shape and its dependency on the host-call ABI; which per-target accelerations sit behind the one contract; the three showcases to the control level; the tests, the timing contracts, and the order the work lands in. The top-down owns those decisions and their resource accounting. - -### Prompt for the agent that writes the top-down - -> Read `CLAUDE.md`, `docs/explanation/architecture/index.md` (§ Hot path discipline, § Effects and Layer, § Buffer persistence, § MoonLive), `docs/contributing/coding-standards.md`, `docs/work/future/generative-fields-analysis-bottom-up.md` (this document, in full: Part 1 is the specification language, Part 2 the examples, Part 3 the gap), `docs/work/future/power-functions-analysis-top-down.md` (the shape and the standing decisions: dimension-generic, one contract everywhere with per-target acceleration, fixed point invisible to the writer, the 16-bit contract, particles as the stateful precedent), `docs/moonmodules/light/power-functions.md` (what exists), and `docs/work/future/backlog-moonlive.md` § 4c and § 2 (the per-pixel call cost and the multi-argument host-call blocker). -> -> Write `docs/work/future/generative-fields-analysis-top-down.md`: the implementation spec for the generative-fields family in MoonLight, for compiled effects and for MoonLive scripts, on the power-function library. Use the house format of the power-functions top-down (status legend, TL;DR, numbered sections, decisions for sign-off, out of scope). Present tense for what exists, forward-looking only under the banner. American spelling, no em-dashes. Specify every kernel from Part 1's primary sources by its textbook name and algorithm; use Part 2 only for measured numbers, parameter vocabularies and precision choices, never as a source of code, and record prior art per module the way `PolarNoiseEffect.h` does. -> -> Decide, with rationale and resource accounting per target (classic ESP32 without PSRAM, S3, P4, desktop): (1) the power functions to add for Part 3's ten items, with signatures in the repo's fixed-point vocabulary; (2) where wide color state lives (a 16-bit Layer buffer versus effect-owned Q16.16 state quantized into the 8-bit Layer), with the memory table per fixture size and the dithering plan; (3) the MoonLive frame-kernel shape: how a script composes whole-frame passes and reads a per-pixel shader through inline ops rather than per-pixel host calls, and which language-roadmap blockers it depends on; (4) the performance plan against Part 3's measured budget and per-target headroom table: samples-per-pixel targets for shaders, cycles-per-frame and bytes-of-state targets for advection, the field-below-resolution and field-below-frame-rate levers, which kernels get FPU or SIMD (PIE) variants per target behind the one contract, where the desktop takes over as the processing node, and how framerate is protected as the rendering method; (5) the three showcases, specified to the control level; (6) the tests that pin each kernel (golden frames on the desktop, per-target timing contracts in scenarios) and the bench criteria the product owner judges by eye on the S3 and on a wall; (7) the order the work lands in, each step verifiable on the desktop first. -> -> Where a decision needs the product owner, list it under decisions for sign-off with the options and your recommendation rather than deciding silently. The bar is the measured quality and cost of the effects themselves. diff --git a/docs/work/future/generative-fields-analysis-top-down.md b/docs/work/future/generative-fields-analysis-top-down.md deleted file mode 100644 index 36187dd71..000000000 --- a/docs/work/future/generative-fields-analysis-top-down.md +++ /dev/null @@ -1,582 +0,0 @@ -# Generative fields: top-down build spec - -> **Forward-looking design document, the exception to CLAUDE.md's present-tense rule.** Stage 2 of the generative-fields work: turns the [bottom-up analysis](generative-fields-analysis-bottom-up.md) (Part 1 the specification language, Part 2 the examples, Part 3 the gap) into an implementable spec: homes, types, signatures, the MoonLive surface, budgets, tests, and a step-by-step plan. Written 2026-09-03 against the five product-owner decisions in § 0. Where this document makes a NEW decision it is marked **(proposal)** and listed in § 10. Companions: the [power-functions top-down](power-functions-analysis-top-down.md) owns the library's homes, types and standing decisions, which this document extends rather than restates; the [MoonLive roadmap](backlog-moonlive.md) owns the engine. - -## Status legend - -| | Meaning | -|---|---| -| ✅ | **Done.** Built and in the tree. | -| 🔨 | **To do.** Open, with the reason stated. | -| ❓ | **Unsure.** A claim or number not yet verified; a question, not a fact. | - -An unmarked line is context or rationale. - -## TL;DR - -- **Five decisions taken by the product owner (2026-09-03)** (the first was later reversed on measurement, § 11): the Layer buffer goes to 16 bits per channel as the one format, subject to the memory analysis in § 2; gradient noise replaces value noise in place (one solution, the goldens move once); MoonLive gets each kernel in the same phase as the compiled function, with a shipped script; the stable-fluids solver is in scope as the last phase, a P4 and desktop showcase; every effect that does not run on the power functions is rewritten on them (algorithmic effects such as Game of Life excepted), judged against how it ran before and never made worse. -- **(proposal) The Layer width is a per-layer property fixed on the cold path, one template instantiated twice, never a per-light branch.** 16-bit is the default wherever it fits; 8-bit is the degradation step the existing adaptive-allocation cascade already has, so the classic without PSRAM keeps its proven 128² pipeline. The pipeline stays 16-bit end to end and quantizes **once, per driver, in `Correction`**, to the width that driver's wire takes: 8-bit with temporal dithering for WS2812-class LEDs and every network, Hue, video and preview output, 16-bit straight through for a 16-bit LED. Memory doubles per layer and for the output buffer (98 KB each at 128² RGB), bandwidth doubles in blend, extrude and preview, and the S3's PSRAM latency makes that measurable on cheap effects; the KPI gate reads it per batch (§ 2). -- **(proposal) Seven kernels, in the existing homes.** `core/noise.h`: gradient noise in place, plus `curl16`. `light/polar.h` (new): `PolarLut`. `core/oscillators.h` (new): `Oscillators`, the timer bank with the four LFO shapes and modulation. `light/draw.h`: `advect` (the bilinear previous-frame resampler, separable and 2D, wrap and clamp), the velocity rules, `decay` by half-life, `lineAA`, `disc`, and the 16-bit quantize with temporal dithering. `light/fluid.h` (new, last): the Stam solver. Every function names its canonical source; nothing is ported (§ 3). -- **(proposal) MoonLive operates on the Layer, not on a handle.** With the Layer at 16 bits the frame buffer IS the wide state, so advection, decay and the emitters are whole-frame builtins on the script's own layer, the shape `fill` and `fade` already have; the polar LUT and the oscillator bank are handles declared in `defineControls()`, the shape `pool()` has. Multi-argument host calls already ship (`emit` takes seven), so nothing here waits on the engine; the one engine change is the pixel-store inline ops learning the 16-bit element (§ 4). -- **Budgets are the bottom-up's, restated as targets per target.** Shaders: samples per pixel against ~750 cycles per sample on the S3, ~250 on the P4; advection: cycles per frame plus bytes of state, PSRAM-class for walls. Framerate is protected by rule: every kernel is `dt`-driven, transport stays sub-pixel per frame, and a showcase must hold its stated fps on its stated fixture or its cost knob is the default (§ 5). -- **Four showcases, three plus fluid**, specified to the control level: *Aurora* (a pure shader), *Trails* (emitters and flows), *Nebula* (a shader feeding a flow), *Fluid* (the solver, P4 and desktop) (§ 6). -- **Eight phases, desktop first, bench last; the product owner decides when enough is done to commit.** Noise swap; fields and Aurora; three dimensions everywhere; Coord3D in MoonLive; the 16-bit Layer; advection and Trails; composition, dithering and SIMD; fluid. The catalog sweep that decision 5 mandates is a follow-on plan, fed by the [effects × power functions inventory](effects-power-function-inventory.md); only the effects a phase touches directly (the noise effects in phase 0, PolarNoise in phase 1, the raw-byte writers in phase 2) are rewritten inside this plan. Each phase lands with its unit tests, its goldens, its scenario, its MoonLive builtins and script, and its catalog cards (§ 8). - -## 0. Inputs and the decisions taken - -From the bottom-up: two techniques over one block set; every block named from its primary source; the cost model; MoonLight's three missing blocks (previous-frame resampler, color state above 8 bits, gradient noise) plus the LUT, the bank and the kernels that compose them; the per-target headroom table with the P4 and S31 as the MCU home of the family. From the power-functions top-down, carried unchanged: dimension-generic; one contract everywhere with per-target acceleration; fixed point invisible to the writer; the 16-bit contract (`angle16`, `frac16`, `pos_t` 24.8); particles as the stateful precedent; determinism (pure function of position, time, seed; stateful kernels declare a resync point); golden frames as hashes; the `Canvas` descriptor. - -Product-owner decisions, 2026-09-03: - -1. **Wide color state: the 16-bit Layer**, as the cleanest, least-code, fastest alternative; decided per Layer at run time from free memory with 8-bit as the fallback (option B, agreed); and **quantized once per driver in `Correction`**, so the pipeline is 16-bit end to end and a 16-bit LED is a driver, not a pipeline change (agreed). Detail in § 2. -2. **Gradient noise replaces value noise in place.** One solution; no second function kept for compatibility (CLAUDE.md minimalism). -3. **MoonLive per phase, same PR.** -4. **Stable fluids in scope**, as the last phase; if it costs CPU it is a P4 and desktop showcase. -5. **Every effect runs on the power functions for whatever they cover; an effect that does not is rewritten.** Most effects were developed quickly to demonstrate a mechanism, and keeping them rendering the same is not a goal. The rule is a mandate, not an option: an effect whose mechanism is one the library provides (motion, fields, noise, polar addressing, trails, particles, oscillators) is rewritten on the library's kernel. Bouncing balls run on `particles`; every noise-like effect runs on the noise this plan builds; every trail decays by half-life; every polar effect reads the LUT. The one exception is the **algorithmic** effect, where a rule set *is* the effect rather than a natural motion: Game of Life is the type case, and it keeps its logic and uses the library only to draw. The guard that came with "behave identically first" stays: an early Game of Life port was much worse than its MoonLight origin, so a rewrite is compared against the effect as it ran and lands only if at least as beautiful on the panel. The aim is effects at a level no other LED firmware reaches, not a 1:1 rendition of another project's catalog. - -## 1. Homes and style ✅ *(the rule; the headers are new or grown)* - -The power-functions rule stands: grow existing homes, one style, free functions over `const Canvas&`, fixed-point vocabulary, a doc block naming the canonical source. **(proposal)** - -| Home | Gains | Notes | -|---|---|---| -| `core/noise.h` | gradient noise behind the existing `inoise8/16`, `fbm`, `warp`, `turbulence` names; `curl16` | Domain-neutral. The name promises the field; the algorithm changes underneath (decision 2). | -| `core/oscillators.h` **(new)** | `Oscillators`: N clocks, four LFO shapes each, modulation binding, one master speed, `dt`-driven | Domain-neutral: audio meters and services want the same LFOs. | -| `light/polar.h` **(new)** | `PolarLut`: `angle16` and radius per pixel, built at `prepare()`, 8-bit default with a 16-bit opt-in (the power-functions decision) | Was planned there and never built; `kaleido` stays in `math16.h`. | -| `light/draw.h` | `advect` (separable and 2D), the velocity rules, `decay`, `lineAA`, `disc`, `quantize` with dithering | The frame-ops family, which is where `scroll` and `blur` already live. | -| `light/layers/Buffer.h`, `BlendMap.h`, `Layer.h` | bytes per channel; the 16-bit format and its quantize-last | § 2. | -| `light/fluid.h` **(new, last)** | `Fluid`: velocity grids, diffuse, project, advect, vorticity confinement, dye transport | Stateful kernel with a resync point, like particles. | -| `light/moonlive/MoonLiveBuiltins_light.h` | one builtin per kernel, per phase | § 4. | - -## 2. The 16-bit Layer - -### What the codebase says - -[MoonLight, memory strategy](../../explanation/architecture/moonlight.md#memory-strategy): all buffers are raw `uint8_t*` arrays sized `channelsPerLight × nrOfLights`; "there is no fixed channel layout: `channelsPerLight` is a runtime value, so RGB, RGBW and multi-channel DMX fixtures all use the same code path; the buffer simply gets wider". Adaptive allocation checks heap before every allocation with a 32 KB reserve, and the degradation cascade "reduces layer dimensions until the buffer fits, minimum 8×8". The architecture "does not assume PSRAM"; the classic without PSRAM is "proven up to 16 K lights (128×128 measured live on Olimex)". CLAUDE.md: minimal memory, fastest hot path, "the standard, complete construct beats a hand-rolled special case", and "no `#ifdef`, no per-light virtual call, data over objects". The LED-driver analysis already planned this as its mode 3, "16-bit pipeline (incl. dither): doubles RAM; best gradient quality; required for 16-bit-native LEDs (UCS7604, HD108)", with the driver declaring its input width and the pipeline building Layer buffers accordingly ([leddriver-analysis-top-down.md § 7.3](../../work/future/leddriver-analysis-top-down.md)). - -So the width is a property the architecture already treats as runtime data (like `channelsPerLight`), the cascade already knows how to shrink a layer that does not fit, and the pipeline decision was already made in principle. What is new is doing it. - -### Memory, per fixture, RGB, one layer - -The last column is an **illustration** from the classic's measured free heap today (~104 KB running at 128² with mirror, Ethernet and mDNS up), not a threshold: the decision is made at run time from free memory, never from the grid size, so the same layout may run 16-bit on one device and 8-bit on another depending on what else is loaded. - -| Grid | 8-bit | 16-bit | Illustration: would it fit today's measured classic heap without PSRAM? | -|---|---:|---:|---| -| 16×16 | 768 B | 1.5 KB | yes | -| 32×32 | 3 KB | 6 KB | yes | -| 64×64 | 12 KB | 24.6 KB | yes | -| 96×96 | 27 KB | 55 KB | yes, one layer | -| 128×128 | 49 KB | 98 KB | **no** as a second allocation beside the 49 KB output buffer: the reserve is 32 KB | -| 48×256 (the wall) | 37 KB | 74 KB | PSRAM already required by `nrOfLightsType` | - -The driver output buffer, where one exists (two or more layers, or a mapping LUT), follows the Layer's width, so the composite case costs two 16-bit buffers; it is part of the same runtime allocation check. Advection needs a scratch copy for the separable pass (a second buffer of the same size) and, for the fluid solver, velocity, pressure and divergence grids (four more `int32` planes). Those are effect-owned `ScratchBuffer`s, allocated at `prepare()`, reported through `dynamicBytes()`, PSRAM-preferred; the memory ladder shows them exactly as it shows a particle pool. - -### Can 8 or 16 be decided dynamically without hot-path cost? Yes, and it is the existing mechanism - -The width is decided **per Layer at `prepare()`**, on the cold path, by the same allocation check that decides whether a LUT or an output buffer exists: the Layer attempts the 16-bit allocation against the free heap after the 32 KB reserve, keeps it when it succeeds, and otherwise takes 8-bit as the first step of the cascade (before dimension reduction). It is a measurement, not a table: no grid size is special, and the answer re-decides on every `prepare()` (a layout change, an added layer, a freed module). Every per-pixel loop that reads or writes Layer bytes is written **once as a template on the sample type** and instantiated for `uint8_t` and `uint16_t`; the choice happens **once per pass per frame** (a function pointer or a branch at the top of `blendMap`, `extrude`, `fadeToBlackBy`, `advect`), never per light. That is the "data over objects, no virtual call per light" rule satisfied, and it is one algorithm, not two implementations. The price is code size (two instantiations of the frame-op set) and a second configuration to test, which the goldens cover by running both widths. - -**(proposal)** Two options, with the recommendation: - -- **A. 16-bit only.** One format, the cascade shrinks a layer that does not fit. Least code and one test configuration; the classic without PSRAM loses its stated 128² full pipeline (it drops to 96² single-layer, 64² with two layers), which is a documented capability regression on a shipping board class. -- **B. 16-bit default, 8-bit as the first cascade step** (recommended). One template, two instantiations, chosen per layer on the cold path; the classic keeps 128²; the hot path pays nothing per light. Minimalism is respected in the way that matters here: one algorithm with one home, and the 8-bit path is a fallback the architecture already promises, not a second feature. - -### Quantize last: once, per driver, in `Correction` (agreed) - -The pipeline is 16-bit end to end: Layer → `blendMap` → output buffer, all at the Layer's width. Quantization happens **once, in `Correction::apply`, per driver**, which every driver already runs through `DriverBase` (LED, ArtNet/DDP/E1.31, PanelCard, Hue, NDI, HLS, Preview): white balance, brightness, the power limiter and the perceptual curve run at 16-bit input, and the last step narrows to the width the driver's wire declares. **8-bit with temporal dithering** for WS2812-class LEDs and every network, video and preview output; **16-bit straight through** for a 16-bit LED. This is mode 3 of the LED-driver analysis ("driver input 16-bit, wire 8-bit, the driver downsamples and dithers; required for 16-bit-native LEDs"), and its `inputBitsPerChannel()` stub becomes real: the driver declares its wire width, `Correction` quantizes to it. - -Why here and not at `blendMap`: quantizing earlier throws the extra bits away before the perceptual curve, which is exactly where 8-bit input crushes the low end, and it makes a 16-bit LED impossible. One site for the whole system also means one dither state (per driver, one byte per channel of carried error, or a stateless ordered matrix) and one place to test. - -Two consequences. The zero-copy path (a single layer with no LUT, the driver reading the Layer directly) needs no view: `Correction` reads the Layer's width like any other source. The `Correction` LUTs stay 256 entries with interpolation on the 16-bit input (the shape `sin16` uses), not a 64K table. In phase 2 the narrowing is `>> 8` so every existing effect renders bit-identically and no golden moves; dithering is the phase 4 switch. - -**The four combinations, and the hot path.** The Layer's width (decided by memory) and the wire's width (declared by the driver) are independent facts that meet in one `Correction` template; the combinations fall out, none is a mode to maintain: - -| Layer | Wire | `Correction` does | When | -|---|---|---|---| -| 16 | 16 | curve and brightness at 16, no quantization | the full pipeline: a PSRAM board with a 16-bit LED | -| 16 | 8 | curve and brightness at 16, quantize once with dithering | the common case: 16-bit fits, WS2812-class LEDs, and every network, video and preview output | -| 8 | 8 | today's path, byte for byte | a board where memory forced the 8-bit fallback | -| 8 | 16 | widen (`v · 257`), curve and brightness at 16, out at 16 | the fallback board driving a 16-bit LED; smoother dimming than 8-bit scaling, the effect's own 8-bit steps remain | - -No path you have today gets slower: the 8-bit instantiations are the same code compiled for `uint8_t`, chosen once per pass per frame, so the per-light instruction count on an 8-bit Layer is unchanged. The 16-bit paths cost what they carry: twice the bytes moved (the S3's PSRAM latency shows this on cheap effects; the KPI gate reads it per batch), the same ALU ops on a 32-bit core, one interpolation per channel in `Correction` on top of the LUT lookup, and dithering as one add and one compare per channel, only on a 16-to-8 wire and only when switched on. No per-light branch, no virtual call, no allocation on the path. - -**16-bit LEDs are enabled, not built.** HD108 (16-bit per channel, clocked SPI) and UCS7604 (16-bit per channel, one-wire) are the known families; APA102/SK9822 are 8-bit color with a 5-bit current gain and do not qualify. Each is its own driver increment: verify the datasheet first, then an encoder that takes 16-bit wire values from `Correction` (an SPI driver for HD108, a wider symbol table for UCS7604). - -### The surface, counted - -What touches Layer bytes and therefore changes in phase 2: `Buffer` (bytes per channel, 1 or 2), `Canvas` (a sample-width field), the 25 `Canvas` and 12 `Buffer` overloads in `draw.h` (one template each), `BlendMap` (identity and LUT paths, both widths), `Layer::extrude` and `fadeToBlackBy` (bytes-per-light already, width-agnostic after the template), `Correction::apply` (16-bit input, quantize to the declared wire width; the one quantization site), `DriverBase` (the declared wire width), `PreviewDriver` (quantized through the same path), `NetworkReceiveEffect` (widens), the **7 effects that write raw bytes** (AudioVolume, BouncingBalls, Fire, Lines, NetworkReceive, Rainbow, Noise), `colorFromPalette` (unchanged: returns 8-bit RGB, widened at the write), the MoonLive `StoreElem` / `FillElems` inline ops on all three backends (element = `channelsPerLight` × the Layer's bytes per channel, NOT a fixed 3 × 2: the ops already store N bytes rather than a fixed RGB, and a phase that hardcoded three channels would break the RGBW and multi-channel DMX fixtures the same buffer serves), and the zero-copy driver path. Modifiers are coordinate-only and untouched; the mapping LUT is index-only and untouched. - -## 3. The kernels - -Signatures are shapes, not final names; the PR is the spec. All fixed point, all `dt`-driven where time enters, all pure functions of (position, time, seed) unless stated. **(proposal)** - -### 3.1 Gradient noise, in place (decision 2) - -`inoise8/16(x[,y[,z]])` keep their names, their 16.0 coordinate convention and their output ranges; the implementation becomes **Perlin's improved noise** (2002): a 256-entry permutation table, gradients from the 12 edge directions of a cube (8 in 2D), the quintic fade `6t⁵ − 15t⁴ + 10t³`, integer throughout, output rescaled to the full range the header already promises. `fbm`, `warp` and `turbulence` sit on top unchanged. The seven noise effects (Noise, NoiseMeter, Noise2D, PolarNoise, Wave, Tunnel, and EffectBase's users) change look in one commit; their goldens move in that commit with the reason in the message. Under decision 5 the question on the panel is whether they look at least as good as before; any that look worse are re-tuned before the swap is accepted, since a noise effect that ran beautifully on value noise may not degrade on gradient noise. The header's note that ports from FastLED "look slightly different for a reason the author cannot see" is deleted: they no longer do. - -Cost target: within 1.3× of the current value noise per sample (the gradient dot products replace the value lerps; the fade is the same), measured by the micro-bench in phase 0 before the swap is accepted. - -### 3.2 `PolarLut` (`light/polar.h`) - -```cpp -polar::Lut lut; // POD over ScratchBuffer -lut.build(dims, center, precision); // prepare(): angle16 + radius per pixel; 8-bit each by default, 16-bit opt-in -lut.angle(i); lut.radius(i); // hot path: two loads per pixel, no atan, no sqrt -``` - -Rebuilt on geometry or center change through `affectsPrepare`; reported as `dynamicBytes`. `PolarNoiseEffect` migrates to it (pixel-identical at the 16-bit precision, golden-pinned; the 8-bit default is a deliberate, bench-judged divergence). A polar modifier consuming the same LUT is the modifier-first path the earlier decision named; it is not in these phases. - -### 3.3 `Oscillators` (`core/oscillators.h`) - -```cpp -Oscillators osc; // N clocks; N is the effect's choice (4 to 20) -osc.set(i, ratio, offset); // per clock, at prepare() or on control change -osc.advance(dtMs, masterSpeed); // once per frame: phase_i += dt · ratio_i · master -osc.ramp(i); osc.phase(i); // 32-bit ramp; angle16 wrapped phase -osc.bipolar(i); osc.unipolar(i); // sin16 of the phase; 0..65535 form -osc.noiseAngle(i); // angle16 from inoise16(ramp), the wandering angle -osc.modulate(base, i, depth); // base · (1 + depth · bipolar(i)), the multiplicative binding -``` - -Frame-rate independent by construction (`dt`, never a frame count, per coding-standards § Animate on elapsed time). Determinism: the phases derive from the shared time origin, so two devices agree (§ 6b of the power-functions top-down). `BeatPhase` stays for single-tempo effects. - -### 3.4 Velocity rules and `curl16` (`draw.h`, `noise.h`) - -A velocity rule is a function `(x, y, t, params) → (vx, vy)` in `pos_t` per frame; none is stored. The set from Part 1: `flowWind` (direction, speed, rotation rate, perpendicular wobble), `flowRadial` (in or out), `flowSpiral` (angular step, radial step), `flowRings` (three zones with smoothstep boundaries, per-zone swirl and drift), `flowNoise` (two 1D profiles, one per axis, decoupled), and `curl16(x, y, t, scale)`: the perpendicular gradient of a scalar noise potential by two central differences, divergence-free by construction (Bridson 2007). The profiles of `flowNoise` are computed once per row and column per frame, not per pixel. - -### 3.5 `advect` (`draw.h`) - -```cpp -draw::advect(dst, src, rule, Edge::Wrap|Clamp); // backward sample, bilinear, per channel -draw::advectSeparable(cv, scratch, xProf, yProf, Edge); // two 1D passes through the scratch plane -``` - -**Shipped (2026-09-04), with the 3D cost measured.** The signature took `dst, src` rather than -`cv, scratch` and dropped `dtMs`: the two planes must differ (reading and writing one buffer samples -pixels the same pass already moved, smearing along the walk order instead of the flow), and the rule -returns sub-pixel units per frame, so only the caller knows the cadence to scale by. `sampleWrap` -already existed and covered the Wrap half; `sampleClamp` and `sampleEdge` are new, and Clamp is what -a wind wants, since a trail leaving the panel must not reappear on the far side. - -**The PO's prerequisite, that 3D awareness must not cost the 2D path, holds: 1%.** Desktop, median -of 11, every destination byte verified written: a 20x20 panel is 16.98 ns/light, the 20x20x20 cube -17.09. The z loop runs once at depth 1, so a panel pays a loop counter. In absolute terms the cube -advects in **0.14 ms**, under 1% of a 20 ms frame, so transport is cheap and the flow rules' -noise sampling is what will set the cost. - -The semi-Lagrangian step (Stam 1999): for each destination pixel, `src = dst − v · dt`, read the previous frame at `src` with bilinear interpolation (four loads, three lerps per channel), write to `scratch`, swap. On the 16-bit Layer the color state is the Layer itself; `scratch` is one effect-owned plane of the same width. Partial transport (`blend` fraction) is a parameter. Budget: ~40 cycles per pixel per channel per pass on the S3 (the field's 80% share is what this is measured against). - -### 3.6 `decay` (`draw.h`) - -`draw::decay(cv, halfLifeMs, dtMs)`: multiply every sample by `k = 0.5^(dt / t½)`, `k` computed once per frame from a 16-bit `pow2` table (`mm::halfLifeKeep`). Framerate-independent by definition; a unit test pins `decay(2·dt) == decay(dt)²` within one LSB. - -**Shipped, with a measured limit (2026-09-04): an 8-BIT plane cannot hold this decay at a high framerate, and no rounding rule fixes it.** Decaying 200 over a 500 ms half-life in 500 ms of frames, exact answer 100: truncating gives 96 at 50 ms frames, 73 at 5 ms and **0** at 1 ms; rounding gives 100, 100 and **200**, the trail frozen solid. Both are the QUANTIZATION rather than the weight, the value being re-rounded to a byte hundreds of times a second. A 16-bit accumulator holds 100/101/102. So `decay` on a byte canvas is honest at a slow cadence and `decay16` on a wide scratch plane is what a trail uses: the precision belongs in the ACCUMULATOR, not the frame buffer, which is the same conclusion phase 2 reached from the other side. The Layer's collected `fadeToBlackBy` remains the one 8-bit-per-frame decay for effects that want it; `decay` is the half-life form on wide state. - -### 3.7 `lineAA`, `disc` (`draw.h`) - -`lineAA` steps the segment at ~3 samples per pixel and splats each through the existing 2×2 bilinear `splat` (Wu 1991). `disc(cv, center, radius, color)` writes coverage `clamp(r + 0.5 − dist, 0, 1)` per pixel in the bounding box, using the squared-distance `coverage` path already in `draw.h`. Both additive with saturation, the particle default. - -### 3.8 `quantize` with dithering (`draw.h`, phase 4) - -`draw::quantize(src16, dst8, ditherState)`: `>> 8` in phase 2; in phase 4 the truncation error is carried per pixel into the next frame (one byte per channel of state, effect-owned, or a 4×4 ordered matrix with no state, selectable). Applies at every 16-to-8 boundary listed in § 2. - -### 3.9 `Fluid` (`light/fluid.h`, phase 5) - -The Stam 1999 solver over effect-owned `int32` Q16.16 grids: `diffuse(velocity)`, `project()`, `advect(velocity by itself)`, `project()`, then dye advected by `draw::advect16` along the stored field. Wall boundaries; `addVelocity(x, y, dvx, dvy, z)` and the dye splat are the source terms. SHIPPED with three differences: the relaxation is Gauss-Seidel rather than Jacobi (it converges in fewer sweeps for the same code, and the sweep count is the `iterations` control, default 5); vorticity confinement was not built; and the dye is the EFFECT's own 16-bit plane rather than the Layer, which is what kept the buffer 8-bit (§ 11). Declares a resync point (re-seed to rest). Sized for panels; the P4 and desktop are its home. - -## 4. MoonLive: the frame kernels, per phase (decision 3) - -**The Layer is the state, so the kernels are whole-frame builtins on the script's layer**, the shape `fill(r,g,b)` and `fade(amt)` already have: no handle, no arena bytes, one host call per frame. Stateful helpers that are not the frame (the polar LUT, the oscillator bank, the fluid grids) are **handles declared in `defineControls()`**, exactly like `pool(n)`: sized once, allocated by the binding outside the arena, whole-object passes per frame. Multi-argument host calls ship (`emit` takes seven), the builtin table holds 96, so nothing waits on the engine. **(proposal)** - -**What shipped, against this table.** The vocabulary below is the design proposal; the names in the -code differ where implementation taught otherwise, and the third column says which. Read the -registered set in `MoonLiveBuiltins_light.h` as the authority. - -| Phase | Builtins | Script shipped | -|---|---|---| -| 1 fields | SHIPPED as `osc(rate, ms, shape)` alone, which is STATELESS: two oscillators sharing a rate hold their phase relationship without a bank, so the handle and its eight accessors were not needed. `polarA`/`polarR` still compute per pixel (no LUT handle). The gradient-noise swap is invisible to scripts, as planned | `aurora.mle` | -| 3 advection | SHIPPED as `trail(1)`, `flowNoise(zoom, strength)`, `flowCurl(zoom, strength)`, `trailDecay(halfLifeMs)`, `emitTrail(x, y, z, index, bri, radius)`. The flow builtins ADVECT rather than setting a rule for a separate `advect()`, which halves the host calls; `decay` became `trailDecay` because a plain `decay` is a name scripts already declare. `flowWind`/`flowRadial`/`flowSpiral` exist in `draw.h` with no script binding; `lineAA` shipped earlier (2026-08-07) and has no script binding either; only `flowRings` is absent | `trails.mle` | -| 4 composition | SHIPPED as `fieldRate(n)`, the frame-skip lever a per-pixel script needs on a large fixture. `fieldScale` stayed compiled-only: a script cannot own the second plane it needs | `nebula.mle` | -| 5 fluid | NOT BUILT: the solver has no script binding, so `fluid.mle` is jets pouring into a curl flow through the phase-3 builtins, which looks like the effect without being it. Exposing `Fluid` to scripts is open | `fluid.mle` | - -The per-pixel shader path improves without new builtins: the LUT turns `polarA`/`polarR` from a 3.5 µs square root into two loads, and the roadmap's inline-op work (§ 4c) is what removes the remaining call cost; that work is the roadmap's, not this document's. - -The one engine change this plan requires: the `StoreElem` and `FillElems` inline ops lower a 16-bit element on all three backends when the layer is 16-bit (phase 2). The lowering already carries the element width as "N bytes"; the width becomes a per-binding constant. - -## 5. Performance plan - -**Currencies.** Shaders: samples per pixel, against ~750 cycles per sample on the S3 and ~250 on the P4 (bottom-up, Part 3), with the gradient-noise swap held within 1.3× of that. Advection: cycles per frame and bytes of state. The target is ~40 cycles per pixel per channel PER PASS, and separable advection is two passes (x then y), so a 128² RGB advect is 128·128·3·40·2 cycles: **~16 ms on the S3** at 240 MHz and **~10 ms on the P4** at 400 MHz. (An earlier draft said 4 ms and 1.3 ms, which matched neither one pass nor two: the arithmetic gives 8.2 ms for a single S3 pass. The numbers below follow from the corrected figures.) That is 60 fps on the P4 with room for the emitters, and on the S3 it is the whole frame at 60 fps, so a 128² wall runs advection at 30 fps or drops to 64² for 50. The phase-3 scenario sets the contract per target from measurement rather than from this estimate. - -**Framerate as the rendering method, by rule.** Every kernel takes `dt`; no kernel takes a frame count. `advect` clamps the per-frame displacement to one pixel and reports when it clamps (the `frameTime`-style status), so a slow frame smears rather than tears. `decay` is a half-life. The oscillator bank is `dt`-integrated. A showcase's stated fps on its stated fixture is a scenario contract; when it fails, the default of its cost knob moves, not the contract. - -**Levers, in the order the plan pulls them.** LUTs (phase 1); fixed point (already); cost knobs as controls with honest defaults (every showcase); field below output resolution with bilinear upscale (phase 4: a `fieldScale` control, the shader renders into a smaller scratch plane and `draw::upscale` bilinearly fills the layer); field below frame rate (phase 4: `fieldRate`, the shader updates every N frames and the oscillators still advance every frame); SIMD (phase 4: a PIE variant of the gradient-noise inner loop and of the bilinear lerp for the S3 and P4, behind the platform boundary, selected by `platform_config.h` capability flags, bit-identical to the scalar form and tested as such); FPU (a float variant of `curl16` and the fluid solver where `hasFpu`, the `raymarch.h` precedent, same bit-identical-within-tolerance test); a second core (the field update as a `multicore` producer, not in these phases). - -**Where the desktop takes over.** The same effects, the same controls; the desktop as processing node feeds a wall over ArtNet or DDP. The per-target headroom table in the bottom-up is the guide: rich compositions above 32² (S3) or 64² (P4) are desktop work, and a showcase's card says so. - -## 6. Showcases - -Each is a compiled effect and a MoonLive script of the same look, so the script proves the builtin surface. Controls are the cost knobs and the look knobs, with defaults that hold the stated fps on the stated fixture. **(proposal)** - -**Aurora** (phase 1; shader; 💫🖌️ 3D). Layers of gradient noise over a `PolarLut`, each layer's transform driven by its own oscillators; which layer wins picks the palette region, and a contrast window decides what is visible at all. SHIPPED controls: `speed`, `scale`, `layers` (1 to 4, the cost knob), `warp`, `twist`, `segments` (kaleidoscope), `contrast`, `octaves`, plus `PolarLut`'s `polarTable`, `polarTable16` and `mapping`. - -**Trails** (phase 3; advection; 💫🖌️ 3D). Emitters drawn into the effect's own 16-bit plane, then advected and decayed. SHIPPED controls: `speed`, `dots` (a density, scaled by the fixture), `scale`, `persistence` (a half-life), `breathe`. The planned `emitter`, `orbit`, `flow` and `blend` controls were not built: one Lissajous walk and one flow rule proved enough, and a flow SELECT is open. - -**Nebula** (phase 4; composition; 💫🖌️ 3D). A noise field, thresholded against its own measured range so most of it is black, is the emitter; a curl flow carries it. SHIPPED controls: `speed`, `scale`, `contrast`, `persistence`, `octaves`, `fieldScale` (compute the field at half or quarter resolution), `fieldRate` (recompute it every N frames). - -**Fluid** (phase 5; solver; 💫🖌️ 3D; P4 and desktop). Jets pour velocity and dye into a Stam solver, one independent medium per depth slice. SHIPPED controls: `jets` (1 to 4), `force`, `swirl`, `viscosity`, `persistence`, `iterations` (the pressure solve, and the cost knob). `angle`, `vorticity` and `gravity` were not built. - -Each showcase gets its card in [effects.md](../../moonmodules/light/effects.md) in the PolarNoise form (one line per control, the cost knob named as such), and its numbers in [performance.md](../../reference/performance.md). - -## 7. Tests and bench criteria - -- **Unit, per kernel, behavior-named.** Gradient noise: zero-mean over a large sample, range, continuity across cell boundaries, the same value on every ISA (a fixed-seed table of 64 samples). `PolarLut`: the LUT equals per-pixel `atan16`/`dist16` at 16-bit precision; the 8-bit default within one LSB of angle. `Oscillators`: `dt`-independence (two steps of `dt` equal one of `2·dt`), wrap, modulation bounds. `advect`: a single lit pixel moves exactly `v·dt` (sub-pixel, so two neighbors share it by the bilinear weights), wrap and clamp edges, the separable form equals the 2D form for an axis-aligned field, no write outside the buffer. `decay`: `decay(2·dt) == decay(dt)²` within one LSB, and a value never rises. `curl16`: divergence of the sampled field is zero within tolerance on a grid. `lineAA`/`disc`: total coverage of a disc equals its area within 2%; a line's coverage is independent of direction within 5%. `quantize`: `>> 8` bit-identical to the 8-bit path; dithering averages to the 16-bit value over 256 frames. 16-bit Layer: every `draw::` primitive tested at both widths. -- **Goldens.** A golden pins the plumbing, not the look (decision 5): phase 2 moves none, because a bit-identical quantize is the proof that widening the Layer is mechanical; phase 0 and phase 6 move goldens deliberately, with the reason in the commit and the product owner's comparison against the old frame on the panel, and that is the harness working. Each showcase adds its golden at both widths. -- **Scenarios, per family.** Fields: a `PolarLut` sized at prepare and rebuilt on a grid resize, with the memory ladder. Advection: Trails on a live pipeline at 16², 32², 64², 128² with the scratch plane in the ladder and the tick within its per-target contract. The 16-bit Layer: the existing `scenario_Layer_memory_1to1` and `scenario_MultiplyModifier_memory_lut` at both widths, and the cascade stepping from 16 to 8 bits on a constrained heap. Fluid: its grids in the ladder and its tick contract on the P4. Every showcase's fps target is a `contract` in its scenario. As shipped the contracts are -`desktop-macos` only and expressed in `tick_us`; no device contract exists yet, because the S3 and P4 -numbers have not been taken (§ 8, phase 3 step 5 and phase 5 step 3). -- **Perf.** The micro-bench target from the power-functions plan gains a row per kernel; `collect_kpi.py --commit` per phase on the S3 and P4; the desktop tick is the fast alarm. -- **The final gate is the product owner's eyes.** Per phase: the S3 bench board (shiffy, 80×48) for the panel class, the P4 (.139) for the MCU home, the desktop at 128×96 for the ceiling, and a wall run for Trails and Nebula. The criteria are visual: no tearing at the stated fps, no visible steps in a trail's tail, a shader that turns rather than scrolls, and the classic at 32² still smooth. - -## 8. Step-by-step implementation plan - -The steps below are work order, not commit order. Each step is finished on the desktop first, then the S3, then the P4, then the wall where it applies, and the product owner tests as steps land. **When to commit, and how much rides in one commit or PR, is the product owner's call** ([CLAUDE.md § Commit](../../CLAUDE.md#commit)): the pre-commit and pre-merge gates cost real time, so a commit carries several steps and a PR carries several phases. Nothing here implies one PR per phase. Order chosen so the first showcase lands before the one cross-cutting change. - -### Phase 0: measure, then swap the noise (small) -1. Add the kernel rows to the micro-bench target: value noise as it is, per sample, on desktop; record. -2. Implement Perlin improved noise behind `inoise8/16` (2D and 3D; 1D as the 2D with y = 0), integer, same coordinate convention, same output ranges. Delete the "value noise" note in `noise.h`. -3. Run the micro-bench: accept only within 1.3× per sample; else optimize (the 8-gradient 2D form, a 12-entry gradient table) before continuing. -4. Desktop build, `ctest`: the seven noise goldens fail as expected; update them in the same commit with the reason. -5. On the product owner's go-ahead (CLAUDE.md § Build: a board is written to only when they say so), flash S3 and P4; `collect_kpi.py --commit`; they judge Noise, PolarNoise and Tunnel on the panel. Catalog cards: no text change unless the look note in a card mentions value noise. - -### Phase 1: fields and Aurora (medium) -1. `core/oscillators.h` with its unit tests. -2. `light/polar.h`: `PolarLut` with 8-bit default and 16-bit opt-in; unit tests against `atan16`/`dist16`. -3. Migrate `PolarNoiseEffect` to the LUT (golden-pinned at 16-bit; the 8-bit default is a deliberate move judged on the panel) and to the bank for its drift. -4. `AuroraEffect`: the three-layer shader, controls per § 6, card in effects.md, golden. -5. MoonLive: `polar()` and `oscillators()` handles with their builtins; `polarA`/`polarR` read the LUT when declared; `aurora.mle`; the compile-every-script test and a script golden. -6. Scenario: fields (LUT rebuild on resize, memory ladder), Aurora's fps contract per target. -7. Desktop first; then S3 and P4 on the product owner's go-ahead; performance.md rows; their look. - -### Open from phase 1 - -**Aurora hitches on the desktop and not on the S3 (2026-09-04).** The product owner sees a periodic -hitch in the desktop preview; the same firmware on an ESP32-S3 at 64x64 looks smooth, and the S3's -LEDs were not yet judged. Five causes were tested and ruled out with measurements: - -| ruled out | the measurement | -|---|---| -| an integer overflow | every expression stays in range at every control setting, checked by hand | -| the drift sawtooth wrapping | worst single-frame field change at a wrap is 7 of 255, no spike | -| the field moving too fast | per FRAME the desktop moves 15x LESS than the S3 (0.009 against 0.136 noise cells), so it should read smoother | -| the contrast window's per-frame easing | its absolute worst frame is SMALLER at 385 fps (17.6) than at 25 fps (30.2) | -| the preview transport at a large grid | the product owner set the desktop to 64x64, matching the S3, and the hitch stayed | - -What is known: brightness changes are distributed rather than discrete (median 17 per frame, 99th -percentile 101), so there is no single glitching frame; and the desktop renders at ~385 fps against -the S3's 25, which is the one large difference left standing. The next thing to try is a frame-rate -cap on the desktop: if the hitch tracks the render rate rather than the wall clock, something in the -pipeline is per-frame where it should be per-unit-time, and the effect is not the place to look. - -**Aurora's layer crossover (2026-09-04).** With a winner-takes-all palette index, 12.7% of pixels -change which layer owns their color every frame, and 35% on the worst frame; each change moves the -hue a whole palette region. A weighted crossover (cubed weights, so a dominant layer still -dominates) removes the discontinuity and measured 2.7x less frame-to-frame change, for 3.4% more -time per pixel (42.8 against 41.4 ns on the host). Built, measured, then REVERTED on the product -owner's call: it did not address the hitch above, and the cost is real. Revisit when the hitch is -understood, since the two may be related. - -### Phase 1b: three dimensions everywhere (medium) - -Dimension-generic is a stated input of this plan (§ 0), and the library is not there yet. The gap is -uneven rather than absent, and the hot-path worry it raises turns out not to be the blocker. - -**Where it already holds.** `draw::` is `Coord3D` throughout (pixel, line, blendPixel, addPixel, -glyph). `blur` runs a third pass behind `if (z > 1)`, so a 2D layout pays nothing for the capability. -`inoise8` and `inoise16` have real 1D, 2D and 3D forms, each compiling to its own straight-line body -over exactly its corners after the phase 0 swap. - -**Where it does not.** `fbm16`, `turbulence8` and `warp8` stop at 2D; only `fbm8` has a 3D overload. -`PolarLut` stores angle and radius on a plane, with no z. `atan16` and `dist16` are planar by nature. - -**A 3D-capable kernel costs a 2D layout nothing**, and the tree already shows both ways to get that: -the third axis is either a template parameter that compiles away (`if constexpr (Dims > 2)` in the -noise core) or a runtime guard on a value that is 1 (`blur`). Neither is a per-pixel branch. So the -missing overloads are mechanical. - -The one that is not mechanical is **polar in three dimensions**, and the answer is a control rather -than a decision. Spherical (two angles and a radius), cylindrical (an angle, a radius and a height) -and distance-from-center are three different looks, and which one is right is a property of the -FIXTURE: a sphere wants the first, a tube or curtain the second, a cube may want any of them. The -choice costs nothing per pixel because it is made once when the table is built, and the table is the -same size either way. Cylindrical is the default: it reduces to exactly today's behavior when depth -is 1, so no existing 2D fixture changes. - -**What "dimension-generic" does NOT claim.** The KERNELS take any arity; an EFFECT still looks good -only at the arities its design has. Aurora on a strip is one radial line through the field, which -reads as a slow flicker rather than as curtains, and no amount of library work changes that. The -Layer already handles the mismatch (a D2 effect on a 3D layer has its z=0 slice copied across z, a -D1 effect its column copied across x), so an effect that declares less than the fixture is correct -rather than broken. Which effects earn a genuine 1D or 3D form is per-effect judgment, made in the -catalog sweep, not a property this phase delivers. - -1. `fbm16`, `turbulence8` and `warp8` gain their 3D overloads, matching `fbm8`'s existing shape. -2. `PolarLut` gains a z axis and a `mapping` control (cylindrical default, spherical, radial), with - the 2D path bit-identical to today's at depth 1. -3. The effects that are `Dim::D2` only because their kernels were: Aurora, PolarNoise and Tunnel are - the three that a volumetric fixture would show something new, and all three are blocked on step 2 - rather than on their own code. Noise2D needs no polar work at all: it already samples 3D noise - with time on z, so a volumetric form is passing the light's own z instead. -4. ✅ **Noise2D and Noise were the same effect**, and are now one: Noise - already uses the light's real z when depth > 1, and the two differ only in control names and - scale defaults (`bpm`/4 against `speed`/64). Merging them is a catalog decision with a golden and - a card, so it was done here rather than deferred: `motion` chooses drift (the old Noise) or morph - (the old Noise2D), the drift golden is unchanged, and the second effect is deleted. -5. Unit tests per kernel that a 2D call is exactly the 3D call with z held at zero, which is what - makes the extension safe to make everywhere else. ✅ *(shipped: `fbm8`, `fbm16`, `turbulence8` and - `warp8` all have 3D forms, and `warp8`'s 2D form is now literally the 3D one at z = 0, so the two - cannot drift apart.)* - -### Phase 1c: Coord3D in MoonLive (medium) - -A script computes a position as three loose integers today, and flattens it by hand -(`mod(bx+dx, width) + mod(by+dy, height) * width`), which is the buffer layout leaking into every -effect. The engine already passes `Coord3D` everywhere, so the script vocabulary is the odd one out. - -This is **not** general struct support. The MoonLive roadmap settles that: a predefined struct is one -the compiler knows by layout, needing no user-declarable struct machinery -([roadmap § 4b](backlog-moonlive.md), with user structs deferred to § 10 for readability -rather than capability). It depends on the multi-value call ABI (§ 2 there), which is what makes a -coordinate in and a color out expressible at all. - -**`Coord3D` yes; the color struct waited for phase 2, which was reversed (§ 11), so the dependency is void and the struct is its own question.** The roadmap pairs `Coord3D` with a `CRGB`, -and that pairing predates the 16-bit decision. A script-visible color fixed at three `uint8_t` would -be the one place in the pipeline where the channel width stops being runtime data, exactly as phase 2 -makes `draw.h` a template over both widths and has `Correction` quantize once at the driver. So the -color type is specified AFTER phase 2 knows what it is, and it takes our own name rather than -FastLED's (CLAUDE.md: our own code, our own names). A coordinate has no such dependency, which is -why it goes first. - -**How far it goes: as far as every other type.** A predefined struct that only appears in builtin -signatures would be a special case, and the language does not need another one. `Coord3D` is a -class member, a local, a function argument and a return value, the same as `int`, `byte`, `bool` and -`fixed`. Arguments and returns are roadmap § 6, which this step therefore depends on. Two -consequences worth stating: a member costs three of the 16 member records and 6 of the 64 arena bytes -unless the compiler packs it as one record, which is worth deciding when the type is added rather -than after; and a local costs one frame slot per field under today's flat allocator, so a script -holding several coordinates meets the 16-slot ceiling faster (§ 8b there). - -Ordering note: this is the step that makes a 3D script possible, so it follows phase 1b rather than -leading it. A script that can hold a coordinate but calls kernels that ignore z has gained nothing. - -### Phase 2: the 16-bit Layer: BUILT, MEASURED, REVERSED (2026-09-04) - -**Decision: the wide frame buffer is not the way to precision; phases 3 onward are 8-bit with -effect-owned 16-bit planes.** The whole arc, the measurement that reversed it, what replaced it and -what is worth salvaging: [§ 11](#11-the-16-bit-question-what-it-was-both-answers-and-what-is-left). - - -### Phase 3: advection and Trails (medium) - -**Volumetric from the start (PO, 2026-09-04).** What shipped is per-slice: every slice gets its own -flow and they differ, but `advect16`'s rule yields vx and vy only, so light is carried WITHIN a slice -and not between them. The trilinear sampler and the vz below were not built, and 3D transport is -open. TrailsEffect is `Dim::D3`, as Aurora is: the bench -S3 is a 20x20x20 cube, so a flat trail would be the thing on the wall. Per function: `decay` and -`decay16` are already dimension-blind (they walk the buffer flat); `advect` needs a TRILINEAR -sampler for 3D, since `sampleWrap` takes `pos_t x, y` with a whole-pixel z, which is 8 corner reads -instead of 4 plus a `vz` from the rule; `flowWind`, `flowRadial`, `flowSpiral` and `curl16` all have -3D forms (3D curl is a cross product of gradients, not one perpendicular) while `flowRings` stays -planar; `disc` becomes a sphere through `sdSphere`. The effect is written 3D-aware from the start -because `dimensions()` and the plane sizing are decided once, but `advect` ships 2D first and gains -the trilinear path in step 4, so step 3 still reaches the wall early. **Memory is the constraint a -volumetric trail meets first**: a 16-bit RGB plane is 48 KB on a 20-cube and 1.5 MB on a 64-cube -(PSRAM only), against 24 KB for a 64x64 panel. - -1. ✅ `advect`, the velocity rules, `curl16`, `decay`, `disc`, with their unit tests. Two parts of the step did not ship: the SEPARABLE form of `advect` (the 2D and 16-bit forms are what exist), and `lineAA`, which turned out to be already in `draw.h` from 2026-08-07. -2. ✅ `TrailsEffect`: emitters, flows, the bank for breathing, scratch plane in `ScratchBuffer`, card, golden. -3. ✅ MoonLive: the flow, advect, decay and emitter builtins; `trails.mle`. -4. ✅ Scenario: `scenario_Trails_ladder`. -5. Desktop ✅ and the product owner's look ✅. S3, P4 and the wall are open, and performance.md has no Trails row yet. - -### Phase 4: composition, dithering, levers, SIMD (medium) -1. ✅ `quantize` with temporal and ordered dithering; tests. -2. ✅ `fieldScale` and `fieldRate` (`draw::upscale16`, and a frame counter the binding owns). -3. ✅ `NebulaEffect` and `nebula.mle`; card and golden. No Nebula scenario: the Fluid one covers the shared field machinery, so it was not worth its own. -4. The PIE variants of the noise inner loop and the bilinear lerp for S3 and P4 behind `src/platform/`, bit-identical tests against the scalar forms; the FPU `curl16` where `hasFpu`. - - **The P4's PPA is for `upscale`, not for advection (2026-09-04).** The Pixel Processing - Accelerator is a 2D-DMA blitter: it transforms a whole rectangle by ONE affine transform - (scale, rotate, translate, blend) with no CPU in the loop. Advection displaces every pixel by - its OWN velocity, so the interesting rules (`flowNoise`, `curl16`) do not map onto it at all, - and the ones that would (a rigid scroll, `flowRadial`) are the cheap cases anyway. Where it - does fit is `fieldScale`'s bilinear `draw::upscale` above, which is exactly a scaled blit, and - the sprite blit already backlogged under [Sprite follow-ups](backlog-light.md). Both are P4 - only, so PPA can be an acceleration behind an existing signature, never the main path. -5. KPI per target; the product owner's look on the wall. - -### A scripted control's name can disagree with the file that declares it (2026-09-04, open) - -Reproduced on the desktop with `Nebula.mle`, and the three facts contradict each other: - -- the module's `script` control holds `Nebula.mle` (so the UI is NOT lowercasing the selection); -- `GET /api/file?path=/moonlive/Nebula.mle` returns a file whose line 20 is `addControl("rate", ...)`; -- the UI renders that slider as **`rate1`**, a name the file no longer contains anywhere. - -So the compiled program disagrees with its own source. `resolveScript` is exact-match and prefers -the user's copy, which is correct, and the picker sets `option.value` to the exact filename, so -neither is the cause. The `rate1` text is a name the file held EARLIER in the session, which points -at the control list surviving a recompile rather than at the resolver or the UI. - -**Ruled out by measurement, so a future look need not repeat it**: the built-in table has room (67 of -96); `kMaxCtrls` is 8 against 5 declared; the 64-byte arena holds five `byte` members; declaration -ORDER is irrelevant (moving the control first changed nothing); the control NAME is irrelevant -(renaming it changed nothing); `aurora.mle` and `trails.mle` have the identical shape and are fine. - -**Where to start**: `rebuildControls()` after a recompile, and whether a declared control that -vanishes from the script is cleared from `ControlList` or merely left behind. A test that compiles a -script, edits one control's name, recompiles and asserts the OLD name is gone would pin it. - -**A second, smaller thing this exposed**: `/moonlive/Nebula.mle` and `/.moonlive/nebula.mle` are two -files on the device and one on a case-insensitive desktop, and nothing stops a user creating a -capital-N file beside a lowercase catalog entry. The fork mechanism assumes the two names match. - -### Phase 5: fluid (medium; P4 and desktop) -1. ✅ `light/fluid.h` with its unit tests (divergence after `project` within tolerance; a jet moves dye; rest state is stable). -2. ✅ `FluidEffect`; card, golden, and `scenario_Fluid_solver` (host; a P4 run needs a board). `fluid.mle` ships too, but NOT on the solver: the plan's `fluid`/`fluidJet`/`fluidStep` builtins were not built, so the script is jets pouring into a curl flow, which looks like the effect without being it. Exposing the solver to scripts is open. -3. ✅ performance.md carries the desktop rows; the P4 and S3 rows are open, since they need a board. - -**Two bugs the jets taught, both worth keeping.** The first shipped picture was a hollow RING, and -the cause was the forcing rather than the solver: jets pinned to one circle at `w/3`, all sweeping -the same direction, all aimed purely tangentially. Measuring speed by radius showed the energy in a -band (1.35 at r5 against 0.33 at the center) while `|grad v|` was a healthy 0.30 per cell, so the -medium was working and being asked the wrong question. The fix is three-part: the radius breathes -between center and wall, the aim leans either side of the tangent, and alternate jets sweep against -each other so they collide, because colliding jets are what roll up vortex pairs. After it, the peak -sits mid-panel (0.78 at r2) and the outer band halves. - -The second: a purely time-paced pour never fires on the opening tick, because `dt` is deliberately 0 -there, and on a fast device it then takes many frames to owe a whole period. The panel stays black -in the meantime and every resize repeats it. The first pour is now unconditional. The scenario's -`buffer non-zero after render` check is what caught it, at 16×16. - -### After the phases: the catalog sweep (large; its own plan) -Decision 5 mandates that every natural-motion effect runs on the power functions. That sweep is not a phase of this plan: it is sized and sequenced in its own plan once the kernels exist, from the [effects × power functions inventory](effects-power-function-inventory.md), which records for each of the 58 effects what it uses today and what it could use. Two rules carry over: each rewrite is compared on the panel against the effect as it ran and lands only if at least as beautiful, and an effect a showcase supersedes outright is deleted, the way a particle-system effect replaces its non-PS twin. - -## 9. Resource accounting - -- **Flash:** the gradient noise is a 16-entry gradient table (Perlin's twelve cube-edge gradients padded to sixteen so the index is a mask) and NO permutation table: the corner hash is a multiply, which is what made it branch-free on Xtensa; the LFO bank and LUT are small; the 16-bit Layer instantiates the frame-op set twice (estimate low single-digit KB per target ❓, read from the repo-health flash table per batch); the SIMD variants are per-target only. -- **RAM:** all sized buffers are `prepare()`-time, PSRAM-preferred, `dynamicBytes`-reported, zero static (`check_footprint`). As shipped: `PolarLut` 2 B/pixel default; a wide-plane effect carries two 16-bit planes plus a 1-byte dither carry, so 15 B/light on top of the Layer's 3 (5 bytes per channel, three channels) (measured, Trails at 64x64x10: 614,400 B against the Layer's 122,880); the fluid holds two velocity grids sized to the volume plus four working grids sized to ONE SLICE (`int32` throughout). The 16-bit Layer's row is gone with the decision (§ 11). -- **Cycles:** budgets in § 5; the KPI gate per phase; the desktop tick as the fast alarm. -- **Repo:** goldens are hashes; scenarios record their observations; each phase adds ~1 effect, ~1 script, ~1 scenario. -- **Boundary:** `noise`/`oscillators` core; `polar`/`draw`/`fluid` light; SIMD and FPU variants only under `src/platform/`, selected by `platform_config.h` flags, never an `#ifdef` outside. -- **Complete construct, real consumer:** every kernel lands with its showcase and its script in the same PR. - -## 10. Decisions for sign-off - -1. ↩️ **Layer width mechanism: B** (16-bit default, 8-bit as the first cascade step, decided per Layer at prepare from free memory, one template). Agreed 2026-09-03, BUILT, then REVERSED on the measurement (§ 11): the buffer is 8 bits and precision is held in effect-owned planes instead. -10. ⬜ **Quantization lives in `Correction`, once per driver, to the driver's declared wire width**; the pipeline is 16-bit end to end and 16-bit LEDs become driver increments. Agreed 2026-09-03, NOT built: it followed from decision 1, which was reversed. Quantization lives in `draw::blit16`, at the one point a wide plane meets the 8-bit buffer. -2. **Gradient noise swap accepted on the 1.3× per-sample bound**, goldens moved in one commit, judged on S3 and P4 (§ 3.1). -3. **MoonLive kernels operate on the script's Layer; LUT, bank and fluid are handles** (§ 4). -4. **Phase order**: noise, fields and Aurora, the 16-bit Layer, advection and Trails, composition, fluid (§ 8). Alternative: the 16-bit Layer first, if the cross-cutting risk should be retired before any showcase. -5. **Showcase names and targets** (§ 6): the fps-per-fixture numbers become scenario contracts on acceptance. -6. **`PolarLut` 8-bit default** (the earlier decision, restated because Aurora's angular resolution on a 128² wall may want 16-bit as its default) ❓. -7. **Temporal dithering default**: on for 16-bit layers once phase 4 lands, or off with a per-driver switch (the leddriver analysis's per-driver mode) ❓. -8. **Fluid on the S3 and classic**: runs with its card stating the panel size it holds, or hidden behind a capability flag. Recommendation: runs, honestly labeled. -9. **Two documents still state the old stance and need the PO's edit under decision 5**: [power-functions.md § Migrating an effect](../../moonmodules/light/power-functions.md) ("Step 1, the port: behave identically") and the header of `test/unit/light/golden_frame.h` ("pixel-identical by default"). Proposal: "faithful first" stays, and its purpose is stated: it guards against degrading an effect into a quick-and-dirty one (the early Game of Life port), not against improving it. Two sentences follow it: every effect runs on the power functions for whatever they cover, and an effect that does not is rewritten on them, with the effect as it ran as the reference and "at least as beautiful on the panel" as the bar; algorithmic effects (a rule set is the effect) keep their logic. The golden is re-baselined in the same commit with the reason, and the harness comment reads "a golden pins the plumbing; a deliberate re-baseline records an improvement the product owner judged". - -## 11. The 16-bit question: what it was, both answers, and what is left - -The plan opened with a decision to widen the Layer buffer to 16 bits per channel (§ 10, decision 1). -That was built, measured, and reversed, and the precision problem it existed to solve was then solved -a different way. This section is the whole arc, because the reversal is the most useful thing the -work produced and a stash is a poor place to keep a lesson. - -### The problem is real, and it is narrower than it looks - -An 8-bit channel multiplied by slightly less than one, many times a second, has nowhere to go. A -decay to a half-life of two seconds at 60 fps wants to multiply by about 0.994 per frame: at 8 bits -a value of 100 either truncates to 99 and keeps truncating, reaching 0 far too fast, or rounds back -to 100 and never fades at all. The tail either vanishes or freezes, and neither is a tail. - -That is the case that motivated the phase. The important observation, which took building the wrong -thing to see, is WHERE it happens: in a plane that DECAYS, frame after frame. The Layer buffer is -written fresh by every effect on every frame, so its precision is a display question, not an -accumulation one. Banding on a buffer is a dither problem. Banding on a decaying plane is a width -problem. - -### Answer A: widen the Layer (built, measured, reversed) - -Phase 2 made `Buffer`, `Canvas` and every `draw::` primitive width-templated, taught every driver and -both preview paths to read at the layer's width, and kept every golden matching through a narrowed -hash. It worked. Then it was measured: - -| | 8-bit | 16-bit | cost | -|---|---|---|---| -| ESP32-S3, 128x128 | 18.25 ms | 28.58 ms | **+56.5%** (effect +50%, driver read +63%) | -| desktop arm64, 128x128 | 0.48 ms | 0.76 ms | +57.9% | - -Two architectures that usually disagree agreed within 1.5 points, which is what makes the number -trustworthy. **On the S3 the DRIVER READ was the larger half.** Reassembling two bytes into a channel -happens `lights x channels` times per frame whatever the effect does, and an in-order core with no -branch predictor punishes it. The 8-bit dispatch itself cost nothing measurable, so the work was -correct; it was aimed one layer too high. - -### Answer B: widen the plane that accumulates (shipped) - -Trails, Nebula, Fluid and the scripted trail each own a 16-bit plane, advect and decay it at full -width, and narrow ONCE on the way out through `draw::blit16` with temporal dithering. The buffer -stays 8 bits, so the driver read is untouched, which is the entire +56% avoided. - -Measured on the desktop at 64x64x10, the same grid for both: - -| | tick | dynamic bytes | -|---|---:|---:| -| Trails (two 16-bit planes + carry) | 1913 us | 614,400 | -| Noise (8-bit, no state) | 920 us | 0 | -| the Layer buffer itself | | 122,880 | - -**The cost moved to memory, and it is larger per effect than the wide buffer would have been**: 5 -bytes per light (two 16-bit RGB planes plus a 1-byte dither carry) against the 3 the Layer holds, so -2.5x what a 16-bit buffer would have cost. Three properties make that acceptable where the buffer was -not: only effects that need it pay, it is `prepare()`-time and PSRAM-preferred, and it is freed when -the effect is removed. A 64-cube would want 3.75 MB for the set (two 1.5 MB planes plus the carry), which is a PSRAM-class effect and the -cards say so. - -### What to learn from it - -**Measure the whole path, not the kernel.** The effect-side cost of a wide buffer was the obvious -half and the smaller one. Nobody predicted the driver read, and no host benchmark would have found it -either: it is an in-order-core property. - -**Precision is not a pipeline property, it is a per-consumer one.** The plan reasoned "the pipeline -should be 16-bit end to end", which is a tidy sentence and an expensive design. What the code needed -was 16 bits in the four places that accumulate. - -**A reversal is worth more than the feature.** Phase 2 cost a day and produced a number that will -stop this being proposed again for years. That is why the numbers live here rather than in a commit -message. - -**Building it was the only way to measure it.** The estimate before the work was "some overhead"; the -answer was +56%. Neither number could have been argued to. - -### Follow-ups, in the order they are worth doing - -1. **Tag the stash, or lose it.** The phase-2 work is 65 files, +1826/-362, in `stash@{0}` and - NOWHERE else: no branch, no tag. A stash does not survive `git stash drop`, a fresh clone, or a - dead disk. `git tag phase2-16bit-layer stash@{0}` costs nothing and makes "recoverable if the wall - says otherwise" true. Product owner's call, since it is a git operation. -2. **Cherry-pick `Correction`'s dithering** (`src/light/drivers/Correction.h`, +158 in the stash). - Decision 10 put quantization in `Correction`, once per driver, at the driver's declared wire - width. That decision followed from the wide buffer and so was never built, but the DRIVER-side - half stands on its own: an 8-bit buffer dithered at the wire is the cheapest remaining win for - banding on a real panel, and it is the one piece of phase 2 whose cost does not scale with lights - x channels. -3. **The five shared-primitive bugs the phase found are still real** under any wide buffer: `fill`, - Canvas `fade`, `blur`, `Layer::extrude` and `effectSetChannel` all assume one byte per channel. - They are correct at 8 bits, so nothing is broken today, but they are a trap for the next wide - thing. Worth a comment at each site rather than a fix. -4. **16-bit LEDs remain unanswered.** Some drivers can carry more than 8 bits to the wire. With the - buffer at 8 that is now a driver-increment question rather than a pipeline one, which is a smaller - and better-shaped problem than the plan assumed. - -## Out of scope - -16-bit-native LED drivers (HD108, UCS7604): enabled by the per-driver quantization, each its own increment starting with the datasheet; a polar modifier on the LUT (modifier-first path, its own increment); the second-core field producer; 3D variants of the velocity rules and the LUT (the dimension audit's "waits for a consumer" rule); a MoonLive per-pixel inline-op program beyond the LUT loads (the roadmap's § 4c work); GPU acceleration on desktop; supersync's protocol (each stateful kernel declares its resync point, no more). diff --git a/docs/work/future/index.md b/docs/work/future/index.md index 9906bdd31..d79e8e383 100644 --- a/docs/work/future/index.md +++ b/docs/work/future/index.md @@ -1,18 +1,8 @@ # Future: what does not exist yet -The to-build list, split along the codebase's own boundary: +Two kinds of file: -- [backlog-core.md](backlog-core.md): core and infrastructure. -- [backlog-light.md](backlog-light.md): the light domain, drivers, effects, sensors. -- [backlog-mixed.md](backlog-mixed.md): items where a core mechanism meets a light one. -- [backlog-moonlive.md](backlog-moonlive.md): the MoonLive language and its engine, in the order the steps are built. +- **`backlog-.md`**: the to-build list, one file per Area of the [Project's](https://github.com/users/MoonModules/projects/5) Backlog view, grouped by module, each item with its fields on one line. +- **`analysis-.md`**: a survey of a problem before it is planned, cited by the backlog items and plans it informs; read when planning, not otherwise. -The analyses beside them survey a problem before it is planned: read when planning, not otherwise. - -- [code-hosting-comparison.md](code-hosting-comparison.md): GitHub, Codeberg, a self-hosted Forgejo and GitLab.com as MoonLight's home, their rules on agent-written code first. -- [processes-inventory.md](processes-inventory.md): every process MoonLight has, by content, with its copies, its gaps and the two step vocabularies; step 1 of the processes plan. -- [moonwalk.md](moonwalk.md): MoonWalk, the model every process is written in, the journey as a story map, a process shaped after Arazzo, a step as Given, When, Then; step 2 of the processes plan. - -`archive_images/moonlight/` stages images captured in MoonLight: each moves to `docs/assets/light//.` and into its module's `@card` when the module ships, or is deleted, so the folder drains to empty. - -An item leaves here when it becomes a plan in [present](../present/index.md), and a shipped item is deleted rather than marked done. Tags in titles: *(investigation)* needs measurement first, *(deferred)* waits on a prerequisite, *(future)* is directional. +How an item arrives, leaves and is titled: [Pick, spec and plan a topic](../../moonwalk.md#pick-spec-plan). diff --git a/docs/work/future/led-driver-psram-ring-analysis.md b/docs/work/future/led-driver-psram-ring-analysis.md deleted file mode 100644 index 36c50f7de..000000000 --- a/docs/work/future/led-driver-psram-ring-analysis.md +++ /dev/null @@ -1,175 +0,0 @@ -# The classic-ESP32 PSRAM ceiling — the refill-ring memory model - -> **Forward-looking research document — exception to the CLAUDE.md present-tense rule.** One question: **can PSRAM lift the classic-ESP32 LED ceiling MoonLight measured at 2048 lights?** Researched 2026-07 against primary sources — hpwit's driver source read line-by-line, plus Espressif's own docs. Every load-bearing claim is cited in § 4. -> -> **Status: PARKED FINDING — not a queued task.** The answer is **yes, via a refill ring** — but that is a **second classic-ESP32 driver** (it cannot be a flag on the shipped i80 driver), and **nothing currently planned needs it**: the shift-register driver builds on the existing i80/Parlio base, S3/P4-first, where the DMA already reaches PSRAM. **§ 3 holds the verdict and the trigger that would un-park it.** Read § 3 before treating anything here as a plan. - -## 1. Verdict - -**Yes — PSRAM can lift the classic-ESP32 ceiling, and the mechanism is a memory-model change, not a shift-register feature.** Our 2048-light wall is a direct consequence of the *whole-frame* DMA model: `esp_lcd`'s i80 backend materialises every light's WS2812 waveform in one contiguous DMA-capable block before the transfer starts, and on the classic ESP32 that block **cannot be PSRAM** — the IDF source rejects it outright (`esp_lcd_panel_io_i2s.c`: `ESP_RETURN_ON_FALSE((caps & MALLOC_CAP_SPIRAM) == 0, NULL, TAG, "external memory is not supported")`), because the classic chip's I2S DMA physically cannot address the external-RAM window. So the DMA buffer scales linearly with the light count against a ~76 KB internal wall. - -hpwit's driver uses a **streaming refill ring** instead: a handful of tiny DMA buffers (144 bytes each for RGB — *one LED slot across all 16 lanes*), refilled on the fly by the I2S completion ISR, which transposes the next pixel row out of the source color array as it goes. **The DMA never touches the source array — the CPU does, inside the ISR** — so the source array is under no DMA constraint and **can live in PSRAM**. Internal RAM becomes **constant (~1.2 KB at the default `nbDmaBuffer = 6`), independent of the light count**. That is the whole ballgame: the ring **decouples LED count from internal RAM**, for **any** driver built on it, expander or not. - -**The ring must be a second classic driver, not a flag on the existing one.** Our i80 driver goes through IDF's `esp_lcd`, which *owns* the DMA and only does whole-frame. **No IDF API can feed PSRAM into classic-ESP32 I2S DMA** — Espressif's own workaround *is* the ring ("add a buffer in internal memory that is DMA'able, and use memcpy to move data from/to that buffer"). So a ring driver means going under `esp_lcd` to the raw I2S registers, exactly as hpwit does, and it lands **alongside** the i80 driver as a user choice: **i80 = simple, WiFi-underrun-immune, ~2 K lights; ring = PSRAM-fed, many-K lights, needs the `nbDmaBuffer` flicker cushion.** - -**A scoping fact that shapes the work: the ring is not available off the shelf.** This is not a matter of adopting a library. The mainstream ESP32 LED libraries use the same **whole-frame DMA** we do and therefore share our internal-RAM ceiling; the refill-ring model is effectively unique to hpwit's drivers, which is why § 2 studies **his source** rather than surveying alternatives. Two consequences: (1) building it is genuinely new capability for a classic-ESP32 LED controller, not catching up; (2) per [*Industry standards, our own code*](../../CLAUDE.md#principles), we study his implementation hard and then **write our own against our architecture** — carry the idea forward, not the code. - -## 2. The PSRAM question - -### 2.1 Two memory models - -**Whole-frame (MoonLight today, via `esp_lcd` i80).** The DMA buffer *is* the frame: every light's expanded WS2812 waveform — 3 slots per bit, one byte (8-lane) or two (16-lane) per slot — materialised in one contiguous DMA-capable block, handed to the peripheral in a single chained transfer. The CPU is out of the loop for the whole frame, which is why this model is **underrun-immune by construction**: nothing remains to refill, so no ISR storm can starve it. The price is linear scaling: - -```text -internalBytes(whole-frame) = maxLaneLights × channels × 3 slots × slotBytes -``` - -**Refill ring (hpwit, under the raw I2S registers).** The DMA buffer holds only a small rolling window: a ring of `nbDmaBuffer` buffers, each **one pixel-row of slots** wide. On each buffer-EOF the I2S ISR calls `loadAndTranspose()`, which reads the next pixel row from the source array and SWAR-transposes it into the buffer the DMA is about to reach. Internal RAM: - -```text -internalBytes(ring) = (nbDmaBuffer + 2) × (nbComponents × 8 bits × 3 slots × 2 bytes/word) - = (nbDmaBuffer + 2) × 144 B [RGB] -``` - -— **no LED-count term.** That is the decoupling, and it is the entire point. - -**Crucially, "ring buffer" names two different things and only one solves the ceiling.** The distinction is the whole decision, and it is easy to lose: - -| | **(A) Refill ring** | **(B) Ping-pong / double buffer** | -|---|---|---| -| What's in a buffer | one pixel row of slots | a **whole frame** | -| Who fills it, when | the **DMA-completion ISR**, mid-transfer | the CPU, **before** the transfer | -| Source array touched by | **CPU only** → **PSRAM-eligible** | DMA → must be internal | -| Internal RAM vs LED count | **constant** | **scales linearly** | -| Buys you | **lifts the memory ceiling** | hides wire time behind DMA | -| In our tree | *(this proposal)* | **`asyncTransmit`** (already shipped) | - -We already have (B). It is genuinely valuable — it is what took the S3 from ~13 to ~34 fps at 16 K — but **it does not and cannot lift the memory ceiling**, which is exactly why halving the DMA buffer by turning `asyncTransmit` off didn't help the WROVER. - -### 2.2 What hpwit's source actually says - -Read directly from [`src/I2SClocklessLedDriver.h`](https://github.com/hpwit/I2SClocklessLedDriver/blob/main/src/I2SClocklessLedDriver.h) / `.cpp` (branch `main`): - -- **A ring of linked `lldesc_t` descriptors.** `struct I2SClocklessLedDriverDMABuffer { lldesc_t descriptor; uint8_t* buffer; };`. `initDMABuffers()` allocates `nbDmaBuffer + 2` buffers of `nbComponents * 8 * 2 * 3` bytes each (**144 B RGB / 192 B RGBW**) plus one 4×-size terminator. Chained `descriptor.qe.stqe_next = &next->descriptor`, last wrapping to `[0]` — a genuine ring. -- **`uint8_t nbDmaBuffer = 6;`** — default 6, and **runtime-tunable** via `updateDriver(..., uint8_t dmaBuffer, ...)` → `nbDmaBuffer = dmaBuffer;`. (This is why the product owner's StarLight-era setting of **75** was reachable: it's a plain count, not a compile-time constant.) -- **The ISR refills and transposes.** `static void IRAM_ATTR interruptHandler(void*)`: on `I2S_OUT_EOF_INT` it increments `ledToDisplay` and calls `loadAndTranspose()`, which reads `driver->leds + ledToDisplay * nbComponents` per strip, applies brightness/gamma maps, and SWAR-transposes (`transpose16x1Noinline2`) into `dmaBuffersTampon[dmaBufferActive]->buffer`. **One buffer = one LED slot, so the ISR fires per LED (~30 µs at 800 kHz).** -- **Ring buffers are internal; the framebuffer is the caller's.** Ring: `heap_caps_malloc(bytes, MALLOC_CAP_DMA)`. Framebuffer: `initled(uint8_t* leds, ...)` takes a raw pointer the driver only ever reads **with the CPU**. Nothing DMA-touches it → **a PSRAM pointer works by construction.** -- **The ISR is deliberately arranged to be allowed to read PSRAM.** `esp_intr_alloc(interruptSource, ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LEVEL3, ...)` with the in-code comment that `ESP_INTR_FLAG_IRAM` was *"removed to avoid Cache Disabled but Cached Memory Region Accessed"* — i.e. the refill ISR is explicitly permitted to touch cache-mapped (PSRAM) memory. That is the design decision that makes the PSRAM framebuffer legal, stated in hpwit's own source. -- **The whole-frame mode exists too, and is the one that scales.** `FULL_DMA_BUFFER` allocates `numLedPerStrip + 2` buffers — and is precisely the mode behind [issue #11, "DMA mode takes up 60k of ram just for a single 300 led strip"](https://github.com/hpwit/I2SClocklessLedDriver/issues/11). **hpwit's library contains both models, and the scaling complaint lands on the whole-frame one — the same wall we hit.** - -**Honest caveat:** hpwit never writes "put `leds[]` in PSRAM" in so many words *for the classic chip* (the explicit PSRAM-framebuffer statements are in the S3 variants). The classic conclusion is drawn from construction — CPU-only reads from a caller-supplied pointer, in a non-IRAM Level-3 ISR — which is sound but is **inference from source, not a vendor promise.** The bench spike in § 5 is what turns it into a measured fact. - -### 2.3 The memory math, concretely (8 lanes, RGB) - -Whole-frame, `slotBytes = 1` (8-lane bus), 3 channels, 3 slots/bit — our i80 model: - -| Lights/lane | Total (8 lanes) | Whole-frame DMA buffer | Fits ~76 KB internal? | -|---|---:|---:|---| -| 256 | 2,048 | ~18 KB | ✅ **measured working** | -| 512 | 4,096 | ~37 KB | ❌ **measured fail** (`i80 bus init failed`) — the real wall sits below the arithmetic: ~76 KB is the *largest free block*, and `asyncTransmit` wants two, plus IDF descriptor overhead | -| 1024 | 8,192 | ~74 KB | ❌ | -| 2048 | 16,384 | ~147 KB | ❌ | - -Ring, same 8 lanes, RGB (hpwit's own sizing, 144 B/buffer): - -| `nbDmaBuffer` | Ring internal RAM | Lights supported | -|---|---:|---| -| 6 (default) | (6+2) × 144 = **1,152 B** | **any** — 2 K, 16 K, 130 K; the count does not appear in the formula | -| 32 | (32+2) × 144 ≈ **4.9 KB** | **any** | -| 75 (StarLight-era) | (75+2) × 144 ≈ **11 KB** | **any** | - -**Read those against each other.** The whole-frame model needs 147 KB of a ~76 KB budget to reach 16 K lights, so it cannot. The ring reaches 16 K — or 130 K — on **1.6–11 KB**, and even a *very* deep 75-buffer cushion costs ~11 KB. The frame itself moves to PSRAM, where 16 K lights × 3 B = 48 KB is a rounding error against 4 MB. - -### 2.4 What the ring gives the WROVER, and what binds next - -With the internal-RAM wall gone, the binding constraints become, in order: - -1. **Wire time — the hard physical floor.** WS2812 is ~30 µs per light per lane (24 bits × 1.25 µs). At 8 lanes × 2048/lane = 16,384 lights: 2048 × 30 µs = **~61 ms → ~16 fps**, before any CPU cost. A protocol constant no driver escapes; it is why "more LEDs" past a point is a *distribution* problem. -2. **Encode CPU in interrupt context — the ring's true price.** Our whole-frame model transposes once with a SWAR kernel on the main path out of internal RAM (measured **~7.6 µs/light** on classic). The ring does comparable work **inside an ISR, per LED, reading cache-mediated PSRAM**. hpwit fires that ISR once per LED (~30 µs apart) — so the refill must reliably complete within the wire time of one pixel. This is the number to measure first. -3. **PSRAM bandwidth / cache stalls** — the same story from the other side: an ISR that stalls on a cache miss is an ISR that is late, which is item 4. -4. **The underrun cushion** (§ 2.5). - -**Honest bottom line for a 4 MB-PSRAM WROVER with a ring driver:** the **memory ceiling disappears** (the frame is kilobytes against megabytes) and the practical ceiling becomes **wire time and ISR headroom** — comfortably into the **8–16 K** range at single-digit-to-low-teens fps. That is a **4–8× lift over our measured 2048**, and the right framing is *"the ceiling stops being about RAM."* **This is not theory:** a prior firmware built on hpwit's ring (the product owner's own MoonLight / WLED-MM lineage) raises its light boundary to **130 K on PSRAM against 4096 without it** — a ~32× gap between the PSRAM and non-PSRAM configuration of the *same* firmware, which is the empirical shape of ring-vs-whole-frame. - -### 2.5 The cost: underrun, and why `nbDmaBuffer` is tunable - -The ring's weakness is the exact mirror of whole-frame's strength. Every buffer completion needs the ISR to refill *before* the DMA consumes the next one. If a higher-priority ISR (WiFi), or a flash-cache-off window, or a PSRAM stall delays it, the DMA runs off the end of valid data and **the strip glitches** — WS2812 latches on a gap of only ~150 µs. - -`nbDmaBuffer` **is** the cushion: it buys `nbDmaBuffer × (one pixel's wire time)` of slack. hpwit says so directly in the shift-register driver's README — *"Sometimes interrupts can disturb the pixel buffer calculations hence making some artifacts. A solution … is to calculate several buffers in advance. By default we have 2 dma buffers. this can be increased"* — and notes the ceiling case: pushing it to `NUM_LEDS_PER_STRIP` is the full frame, "but it requires too much memory" (i.e. it degenerates back into our model). Mitigations visible in his source: a deeper ring, `_DMA_EXTENSTION` idle padding, **core pinning** (`enableShowPixelsOnCore()`), and **Level-3 interrupt priority** so the refill preempts ordinary WiFi ISRs. - -This is not a niche worry — every ESP32 LED library that streams rather than whole-frames fights it, and they all expose the same remedy: a **tunable count of DMA buffers**, raised when interrupt load (typically WiFi) causes flicker. So the cushion depth is a **recognised, standard tuning parameter, not a bespoke hack** — which matters under [*Common patterns first*](../../CLAUDE.md#principles): if we build the ring, `nbDmaBuffer` is the knob users already expect. - -**The trade, plainly:** our i80 driver's underrun-immunity is a real, defensible property we would be giving up on the ring path — which is exactly why the ring should be an *additional* driver, not a replacement. A 2 K-light install on a WiFi-busy board is *better served* by i80. A 16 K-light install cannot use it at all. - -### 2.6 The shift-register (74HC595) driver — and why the ring is NOT its prerequisite - -**In hpwit's lineage the shift-register driver does inherit the ring's PSRAM property.** It multiplies lanes by shifting each ESP32 pin's byte out through a 74HC595 (one per data pin, a 74HC245 level-shifting LATCH/CLOCK): **"8 strips out of one single pin … for 15 esp32 pins which gives you 8x15=120 strips."** That multiplication happens *downstream on the wire*; upstream it is the **same I2S ring, the same ISR-side transpose, the same caller-supplied framebuffer** (`__NB_DMA_BUFFER`, default 2, buffers of `(NUM_VIRT_PINS+1) * nb_components * 8 * 3 * 2` ≈ 1152 B). - -**But that does not make the ring a prerequisite for *our* shift-register driver — the chip does.** Our backlog sets the shift-register driver's acceptance floor at **48 × 256 = 12,288 lights**. Under the whole-frame model that floor is: - -| Chip | Whole-frame DMA buffer for 48 strands | Reachable today? | -|---|---|---| -| **Classic ESP32** | must be **internal** RAM (I2S DMA cannot address PSRAM) → past the ~76 KB wall | ❌ **needs the ring** | -| **ESP32-S3 / P4** | `esp_lcd` allocates it **from PSRAM** (EDMA reaches external RAM in hardware) | ✅ **no ring needed** | - -So the honest conclusion — and the correction to an earlier draft of this document, which asserted a blanket prerequisite — is: **the whole-frame model cannot meet the shift-register driver's floor on the classic chip, and comfortably meets it on the S3 and P4.** The S3 i80 driver *already* drives 16,384 lights on a PSRAM DMA buffer (measured, SE16). A shift-register driver built on the **existing i80/Parlio base**, targeting **S3/P4 first**, is therefore **not memory-bound** and needs no new memory model. The classic chip's ~2,048-light whole-frame ceiling becomes a **documented limit of that chip**, not a blocker for the feature. - -This also resolves — rather than reopens — the loop in our own backlog. `backlog-light.md` records that the chunk-streaming-ring design was **superseded** by the `esp_lcd` whole-frame path, on the reasoning that whole-frame is underrun-immune and reuses the shared `ParallelLedDriver` base. That decision **stands**: it bought immunity at the price of a *classic-chip* ceiling, and the chips that matter for a 12 K-light virtual install already reach PSRAM. The ring is what would lift the **classic** chip specifically — see § 5 for the trigger. - -## 3. Verdict: a parked finding, not a next step - -**This is a finding, not a plan. Do not build the ring driver yet.** The analysis above stands — the mechanism is real, the memory math is right, and it is genuinely new capability for a classic-ESP32 controller. But three things scope it *out* of the near-term order, and this section exists to say so plainly rather than let a research document read as an approved roadmap item. - -**1. It is a whole second classic driver, and it cuts against what just shipped.** `esp_lcd` **owns the DMA** and only does whole-frame, so the ring **cannot be a flag** on `I80LedDriver` (§ 2.1). It means going under `esp_lcd` to the raw classic I2S registers — a second, register-level, ISR-driven, underrun-*prone* classic path standing next to the i80 driver we just hardened and measured across three chips. Forking the classic path on the strength of an analysis, right after proving the simpler path works, is the wrong trade of risk for capability. - -**2. It is NOT a prerequisite for the shift-register driver — that was this document's own error.** An earlier draft of this section claimed the shift-register driver's 12,288-light floor forces the ring first. That is true **only on the classic ESP32**. On the **S3 and P4** the `esp_lcd`/LCD_CAM backend already draws its DMA buffer **from PSRAM** (hardware EDMA — § 3, and the measured 16,384 lights on the SE16), so a shift-register driver built on the existing i80/Parlio base is **not memory-bound there at all**. The correct scoping is therefore: build the shift-register driver **on the drivers we have, S3/P4-first**, and treat the classic chip's ~2,048-light whole-frame ceiling as **a documented limit of that chip**, not a defect blocking the feature. Step 3's shift-register-driver bullet already says "write our own against the shared slots encoder" — that instinct was right; this document was wrong to contradict it. - -**3. Nothing currently planned requires it.** With the shift-register driver targeting S3/P4, no roadmap item is blocked by the classic memory model. The ring buys the classic chip a 4–8× light-count lift and a competitive win — genuinely valuable, but *optional*, and optional work does not preempt work that is on the critical path. - -**The trigger that would un-park it (name it, so this isn't an indefinite maybe):** **classic-ESP32 above ~2,048 lights becomes a shipping requirement** — a board, a user, or a shift-register install that must run on classic silicon at high light counts. Until then this document is the finished homework, not a queued task: when the trigger fires, § 2's memory math, § 3's competitive position, and § 5's bench spike are ready to go without re-deriving anything. - -**Related but distinct — don't conflate.** Two other levers raise the classic chip's *lane* count without touching the memory model: Step 3's **parallel-I2S driver** (hpwit `I2SClocklessLedDriver` lineage — the classic I2S peripheral can clock well past our current 8 lanes) and the **virtual/shift-register fan-out**. Those are lane-count work on the existing whole-frame model. The ring is *memory-model* work. They are independent; either can ship without the other. - -### 3.1 If it is ever built: the shape, and the one thing to measure first - -**It must be a second driver, alongside i80** — a scoping conclusion, not a preference (see reason 1 above). The two would coexist and the user picks: - -| | `I80LedDriver` (shipped) | ring driver (parked) | -|---|---|---| -| DMA model | whole-frame, `esp_lcd` | **refill ring**, raw I2S | -| Framebuffer | internal only | **PSRAM** | -| Ceiling | ~2 K lights | not RAM-bound (wire/CPU-bound) | -| Underrun | **immune by construction** | needs `nbDmaBuffer` cushion | -| Complexity | thin, shares `ParallelLedDriver` | ISR-side transpose, register-level | - -Keeping i80 is **not** legacy baggage: for a ≤2 K install on a WiFi-busy board, underrun-immunity is a real advantage the ring cannot offer. Two drivers, two honest trade-offs, user picks — and the driver docs should say exactly that, rather than presenting the ring as a strict upgrade. - -**The one caution to pin before the first line of code.** The ring moves the transpose *into an ISR* and its source *into PSRAM*. Our classic encode is ~7.6 µs/light with a SWAR transpose on the main path out of internal RAM; the ring must do comparable work in interrupt context, per LED (~30 µs budget), reading cache-mediated PSRAM. **That ISR cost is the ring's true price and the thing most likely to surprise.** Budget a deep cushion from the start — `nbDmaBuffer` of 32–75 costs only ~5–11 KB (§ 2.3); the cushion is cheap and the flicker is not. Note hpwit's two enabling tricks, both of which we'd want: **Level-3 interrupt priority** (so the refill preempts WiFi) and **omitting `ESP_INTR_FLAG_IRAM`** (so the ISR may legally touch cache-mapped PSRAM). - -**When the trigger fires, the first move is a bench spike — not driver code.** On the WROVER, stand up a minimal raw-I2S ring: `nbDmaBuffer` × 144 B `MALLOC_CAP_DMA` buffers, a `MALLOC_CAP_SPIRAM` source array, an ISR that transposes one pixel row per completion, driving **8 × 1024 = 8192 lights** — 4× past our measured wall — on the existing bench pins. That single spike answers all three open questions at once: - -1. **Does internal RAM stay flat?** (It should read ~1.6–11 KB regardless of light count — the claim of § 2.3.) -2. **What does the ISR transpose actually cost per row out of PSRAM?** (The § 2.4 unknown.) -3. **What `nbDmaBuffer` depth holds a clean strip with WiFi associated and hammering?** (The § 2.5 trade, measured rather than assumed.) - -**If that spike lights 8192 lights cleanly, the design is proven and the rest is integration** behind the existing `ParallelLedDriver` seam. If it flickers at every cushion depth, we learn it on a bench rig instead of inside a driver refactor. Either way the spike, not the argument, settles it — and it also converts § 2.2's one honest inference (PSRAM framebuffer on *classic*, which hpwit documents only for the S3) into a measured fact on our own hardware. - -## 4. Sources - -**hpwit (primary — source read line-by-line):** -- [I2SClocklessLedDriver](https://github.com/hpwit/I2SClocklessLedDriver) · [`src/I2SClocklessLedDriver.h`](https://github.com/hpwit/I2SClocklessLedDriver/blob/main/src/I2SClocklessLedDriver.h) — `lldesc_t` ring; `nbDmaBuffer = 6` default, runtime-tunable via `updateDriver()`; `initDMABuffers()` → `nbDmaBuffer + 2` buffers of `nbComponents * 8 * 2 * 3` B (144 RGB / 192 RGBW) + 4× terminator; `IRAM_ATTR interruptHandler()` → `loadAndTranspose()` reading `driver->leds`; ring buffers `heap_caps_malloc(..., MALLOC_CAP_DMA)`; `initled(uint8_t* leds, ...)` caller-supplied framebuffer; `esp_intr_alloc(..., ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_LEVEL3, ...)` with `ESP_INTR_FLAG_IRAM` "removed to avoid Cache Disabled but Cached Memory Region Accessed"; optional `FULL_DMA_BUFFER` whole-frame mode. -- [issue #11 — "DMA mode takes up 60k of ram just for a single 300 led strip"](https://github.com/hpwit/I2SClocklessLedDriver/issues/11) — the whole-frame mode's scaling complaint. -- [PR #47 — `__NB_DMA_BUFFER` as var & PSRAM-prefer](https://github.com/hpwit/I2SClocklessLedDriver/pull/47). -- [I2SClocklessVirtualLedDriver](https://github.com/hpwit/I2SClocklessVirtualLedDriver) — "8 strips out of one single pin … 8x15=120 strips"; 74HC595 per virtual pin + 74HC245 level shifter; `__NB_DMA_BUFFER` default 2, buffers `(NUM_VIRT_PINS+1) * nb_components * 8 * 3 * 2` ≈ 1152 B; "Artifacts due to interrupts" → "calculate several buffers in advance"; `enableShowPixelsOnCore()`; 12,000-LED worked example; 75 → 129 fps second-core figures. -- [I2SClockLessLedDriveresp32s3](https://github.com/hpwit/I2SClockLessLedDriveresp32s3) — the S3 lineage (PSRAM buffers; S3 EDMA can DMA from PSRAM). - -- [The predecessor MoonLight](https://github.com/ewowi/MoonLight) / [WLED-MM](https://github.com/MoonModules/WLED-MM): a prior firmware riding hpwit's ring on PSRAM: LEDs pre-allocated in PSRAM, light boundary **130 K** (vs **4096** non-PSRAM). The empirical demonstration that the ring's ceiling lift is real. - -**Espressif (the load-bearing hardware constraint):** -- [`esp_lcd/i80/esp_lcd_panel_io_i2s.c`](https://github.com/espressif/esp-idf/blob/master/components/esp_lcd/i80/esp_lcd_panel_io_i2s.c) — `ESP_RETURN_ON_FALSE((caps & MALLOC_CAP_SPIRAM) == 0, NULL, TAG, "external memory is not supported");` -- [Support for External RAM — ESP-IDF (ESP32)](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-guides/external-ram.html) — external RAM "cannot be used as a place to store DMA transaction descriptors or as a buffer for a DMA transfer to read from or write into"; prescribed workaround = internal DMA-able buffer + copy. **Confirms: no IDF API feeds PSRAM into classic-ESP32 I2S DMA.** -- [Support for External RAM — ESP-IDF (ESP32-S3)](https://docs.espressif.com/projects/esp-idf/en/stable/esp32s3/api-guides/external-ram.html) — the S3 *does* have hardware DMA-to-PSRAM (descriptors still internal): why our S3 i80 reaches 16 K and classic cannot. - -**MoonLight's own measurements:** -- [performance.md § Multi-pin LED driving](../../reference/performance.md#multi-pin-led-driving-all-three-peripherals-128128-grid) — classic i80 2048-light ceiling + `esp_lcd_i80_alloc_draw_buffer` rejecting `MALLOC_CAP_SPIRAM`; S3 16,384 @ ~34 fps; P4 Parlio 4096, 139 fps @ 1024; the `multicore` +44 % table. -- [backlog-light.md](backlog-light.md) — the superseded chunk-streaming-ring decision; the shift-register driver's **48 × 256 = 12,288** acceptance floor. diff --git a/docs/work/future/leddriver-analysis-bottom-up.md b/docs/work/future/leddriver-analysis-bottom-up.md deleted file mode 100644 index b521479d4..000000000 --- a/docs/work/future/leddriver-analysis-bottom-up.md +++ /dev/null @@ -1,631 +0,0 @@ -# LED driver landscape analysis - -> **Forward-looking research document — exception to CLAUDE.md present-tense rule.** This is a Stage-1 landscape survey that grew into an architectural sketch + a recommendation. It maps the field of LED-driver libraries across ESP32, Teensy, Raspberry Pi, and desktop; proposes a two-axis reference architecture (Backend × Multiplex); and recommends a path (build everything ourselves, see "Recommendation — walk Scenario B" near the end). Stage 2 deep-dives validate the architecture's load-bearing assumption (that hpwit's shift-register multiplex can be factored out of the I2S-LCD peripheral code without losing performance). All repos read at **main/master HEAD** on **2026-05-25**, not at last release tag. Source citations use file paths and line numbers where available; inferred claims are marked. - -## TL;DR - -- **What this is.** Stage-1 of a two-stage investigation. Maps the LED-driver landscape, proposes a reference architecture, names which path to walk in Stage 2. -- **Field surveyed.** WLED / WLED-MM (NeoPixelBus under the hood, ESP32-family). hpwit's I2SClocklessLedDriver + I2SClocklessVirtualLedDriver (I2S-LCD parallel; the virtual one adds 74HC595 shift-register multiplex). FastLED master — note: master diverges sharply from the September 2025 release; a modular driver refactor + runtime-driver-switch API landed in May 2026. NightDriverStrip (FastLED legacy + core-pinning patterns). ESP-IDF's `led_strip` (managed component, reference only). Teensy via OctoWS2811 / ObjectFLED. Raspberry Pi via rpi-ws281x (broken on Pi 5 today). Desktop via ESP32-over-USB bridge as the dominant pattern. -- **Reference architecture.** One narrow boundary line: `LedDriver::push(std::span)`. Below the line is **two orthogonal axes**: Backend (RMT, I2S-LCD, LCD-CAM, parlio, FlexIO, RP1 PIO, SPI MOSI, BareMetal) × Multiplex (None vs. ShiftReg). The virtual driver is *not* a sibling backend — it's the ShiftReg multiplex applied on top of any parallel-clocked backend. Composition by member, switchable at runtime; no templates, no CRTP. -- **Identity-mapping fast path preserved.** When `MappingLUT::hasLUT()` is false, drivers receive a span pointing directly at `layer_->buffer()` — zero copy through the wire. This optimisation drove a major part of the architecture's shape. -- **Feasibility on hpwit's code (read at HEAD, 2026-05-25).** Two-target paths interleaved through one file (30 `#ifdef CONFIG_IDF_TARGET_ESP32S3` blocks). The multiplex code isn't cleanly factored out today — refactoring it into a backend-agnostic `ShiftRegMultiplex` layer is real engineering work, not free composition. `Backend × ShiftReg` is high-confidence on classic ESP32 + S3 (hpwit's lib is the reference), medium-confidence on parlio + FlexIO (no reference, plausibly feasible), low-confidence on RP1 PIO (research-grade). -- **What ESP-IDF gives us.** Peripheral plumbing (RMT5, parlio_tx_unit, esp_lcd_panel_io_i80, SPI master DMA, GDMA, GPIO LL) — yes. Anything LED-specific (WS2812 encoder, transposition helpers, multi-protocol abstraction) — no. The `led_strip` component is community, not first-party. Per-backend we'd write ~500-700 lines of new code; ~300-500 lines for the multiplex layer. -- **Recommendation: walk Scenario B (build ourselves).** Reasons in priority order: the multiplex axis is our MoonLight-defining value-add and doesn't compose cleanly onto FastLED; runtime driver switching aligns with our own contract; the identity-mapping fast path needs the driver reading `layer_->buffer()` directly; IDF gives us the plumbing for free; license + binary-size tax shrinks Scenario A's surplus. **Cost accepted:** 3 weeks → 3 months to first working driver, mitigated by spike-ordering. -- **First two Stage-2 spikes (load-bearing).** (1) `LcdCamLedDriver (None)` on ESP32-S3 against the existing `Drivers::loop()` shape — ~1 week, proves the contract. (2) Refactor hpwit's S3 path into `LcdCamLedDriver (ShiftReg)` — ~2-3 weeks, validates that the multiplex can be factored out without losing performance. **If spike 2 fails, fall back to hybrid** (Scenario A for non-multiplex backends + Scenario B for multiplex). The architecture supports both. -- **Out of scope for Stage 1.** Per-driver benchmarking on real hardware; PR-to-FastLED to expose its DMA buffer; final IDF version pin decision; license pick. Stage 2. - -## Why this document exists - -MoonLight's `src/light/drivers/` ships an ArtNet send driver and a WebSocket preview driver. There is **no LED-strip driver yet** — that's the gap this survey serves. The pipeline expects a `DriverBase` (`src/light/drivers/Drivers.h:11`) child that reads from the Drivers container's shared output buffer and pushes bytes to hardware. The requirements (1-10K LEDs must, 10-30K should, 30K+ interesting, 50 FPS, hot-reconfigurable pin/protocol/count, WiFi-coexistent) narrow but don't pick a library. This document characterises the candidates. - -## ESP32 — primary depth - -### WLED — https://github.com/wled/WLED - -- **HEAD**: `0f34973` (2026-05-23), `main` branch, 18,115 stars, EUPL-1.2. -- **Abstraction**: delegates to `NeoPixelBus` (Makuna) for WS281x output via the `PolyBus` wrapper. `BusDigital` (`wled00/bus_manager.cpp:299`, `_driverType` 0=RMT / 1=I2S) selects between RMT and I2S backends at runtime. SPI (APA102/SK9822) goes through NeoPixelBus's two-pin protocol path (`bus_manager.cpp:322`). -- **Platforms**: classic ESP32, S2, S3, C3, C6, H2, P4 — variant-specific paths in `bus_wrapper.h` (e.g. lines 253-258 note that S3 uses NeoPixelBus's LCD parallel `X8` method). RMT5 vs legacy RMT is selected by NeoPixelBus, not WLED directly. -- **Protocols**: WS281x family, SK6812 RGBW, APA102, SK9822, TM1814, WS2801, plus PWM analog, on/off relay, HUB75 (S3 via `BusHub75Matrix`), and network buses (ArtNet, DDP). -- **Parallel pins**: up to 8 parallel channels on S3 via NeoPixelBus's I2S/LCD path; RMT channels divided per variant. `OUTPUT_MAX_PINS` caps it. -- **Threading**: `show()` is synchronous; NeoPixelBus handles DMA/ISR underneath. WLED runs a single-task render loop. -- **Hot reconfig**: `BusManager::add()` (`bus_manager.cpp:1478`) and `removeAll()` (`:1559`) let buses be created/destroyed at runtime, but no per-bus pin/count mutation in place — full reallocation is the pattern. UI "save & reboot" is the common path; on-the-fly is possible but unusual. -- **WiFi coexistence**: nothing explicit beyond what NeoPixelBus provides. Long-standing glitch reports on large installations — historically the reason WLED-MM forked. -- **Maintenance**: very active (commit yesterday, 501 open issues — high but reflects scale). -- **Field notes**: by far the most widely tested LED firmware. Carries baggage as a full firmware, not a library — extracting the bus layer for embedding is non-trivial. - -### WLED-MM — https://github.com/MoonModules/WLED-MM - -- **HEAD**: `e66e478` (2026-05-23), default branch is `mdev`, 395 stars, EUPL-1.2. -- **Delta from upstream**: focused on large installations and HUB75. WLED-MM adds inline-fast-path pixel writes (`unGamma8_bus`, `lastBus`/`laststart`/`lastlen` caching in `bus_manager.cpp` around line 1081), heavy `IRAM_ATTR`/`__attribute__((hot))` annotations, and a substantially extended `BusHub75Matrix` with dirty-bit tracking. Adds board-specific pinout presets for MOONHUB, MatrixPortal S3, HD-WF1/WF2. -- **Notably absent**: WLED-MM does **not** integrate hpwit's I2SClocklessLedDriver or the virtual driver — its HUB75 path uses `ESP32-HUB75-MatrixPanel-DMA` directly; its WS281x path is still NeoPixelBus via PolyBus. The "more pins for big installs" benefit comes from the upstream LCD-parallel I2S, not from a swap-in driver. -- **Channel allocation tweaks**: `bus_wrapper.h:776-786` shifts RMT/I2S channel assignment to accommodate the I2S0 split. Adds `WLEDMM_SLOWPATH` / `WLEDMM_TWOPATH` build options. -- **Maintenance**: active, 102 open issues. Smaller maintainer pool than upstream; product owner is co-maintainer here. -- **Field notes**: this fork's value-add is empirical tuning for installations beyond ~5K LEDs, not a fundamentally different driver architecture. If MoonLight wants the WLED bus layer at all, the question is whether to take it from upstream or from MM. - -### I2SClocklessLedDriver (hpwit) — https://github.com/hpwit/I2SClocklessLedDriver - -- **HEAD**: `a736f5e` (2026-04-06), `main`, 72 stars, MIT. -- **Abstraction**: ESP32 I2S peripheral in parallel-LCD mode (classic ESP32, `i2sInit()` at lines 1093-1167); on S3 it uses **LCD_CAM + GDMA** despite the "I2S" name (`#ifdef CONFIG_IDF_TARGET_ESP32S3`, lines 47-87). One peripheral, many GPIOs. -- **Platforms**: ESP32 and ESP32-S3 only. **No S2, C3, P4** support in the source (no conditional blocks for those). Sweet spot: S3 with PSRAM. -- **Protocols**: WS2812/13/15 and SK6812 (RGB and RGBW). No APA102 / SPI strips. Up to 16 parallel strips per peripheral. -- **Threading**: ISR-driven DMA chain with `nbDmaBuffer + 2` buffers (default 6), `loadAndTranspose()` runs inside the ISR (`:1654`, `:1741`) interleaving transposition with transmission. FreeRTOS semaphores (`sem`, `semSync`, `semDisp`, `waitDisp`) coordinate caller-side waits. `showPixels(NO_WAIT, ...)` is non-blocking. -- **Hot reconfig**: `initled()` is the (re-)init path. Pins and per-strip lengths can be changed by calling `initled()` again; no public API for live pin migration mid-frame. -- **WiFi coexistence**: not explicitly addressed in source; in practice users pin the driver task to core 1 and run WiFi on core 0. The DMA-driven design means the WiFi stack stealing CPU has little effect on bit timing — this is hpwit's core architectural advantage. -- **Field notes**: the standard go-to when RMT runs out of channels. Excellent for fixed installs; less ergonomic for hot reconfiguration. - -### I2SClocklessVirtualLedDriver (hpwit) — https://github.com/hpwit/I2SClocklessVirtualLedDriver - -- **HEAD**: `e7b2f0e` (2024-11-20), `main`, 47 stars, MIT. **Less active than the non-virtual driver** — last release "Titus" Jan 2024 — but stable; the technique doesn't need churn. -- **Abstraction**: same I2S parallel-LCD trick as the non-virtual driver (S3 = LCD_CAM + GDMA; classic = I2S0 LCD mode). Innovation is the **per-physical-pin time-multiplexing** through external 74HC595 shift registers + 74HC245 level shifters. `NUM_VIRT_PINS` (default 7-8) parallel data lines × up to 8 strips per shifted output = **up to ~120 strips total** from one ESP32. Source: project README + `I2SClocklessVirtualLedDriver.h` GDMA setup around line 430. -- **Platforms**: ESP32 + ESP32-S3. Hpwit overclocks the S3 LCD clock up to ~1.125 MHz, giving ~40% bandwidth headroom over baseline 800 kHz WS2812 timing. -- **Protocols**: WS2812/13/15, SK6812. No SPI strips, no DMX. -- **Threading**: optional dedicated display task (`I2SClocklessVirtualLedDriver_dispTaskHandle`), pinnable to core via `enableShowPixelsOnCore(corenum)`. Same semaphore model as the non-virtual driver. WAIT / NO_WAIT / LOOP display modes. -- **Hot reconfig**: init-time pin assignment; the shift-register topology is physical, so "hot pin change" doesn't really apply — the wiring is the wiring. -- **WiFi coexistence**: same story as the non-virtual driver — pin the display task to one core, WiFi on the other. Field-proven on installations approaching 100K LEDs. -- **Field notes**: this is the heavy-iron option for installations beyond 30K LEDs. Requires PCB design / hand-wired shift registers; not plug-and-play. **Fork-and-vendor is a low-risk option** given the product owner's relationship with the author and the technique's stability. - -### FastLED — https://github.com/FastLED/FastLED - -- **HEAD**: `f7d4b0e` (2026-05-24), `master`, 7,399 stars, MIT. -- **Critical: HEAD diverges sharply from the September 2025 release (3.10.3).** Master has a **modular driver refactor** under `src/platforms/esp/32/drivers/` with subdirectories `rmt/`, `rmt_rx/`, `parlio/`, `lcd_cam/`, `lcd_spi/`, `i2s/`, `i2s_spi/`, `spi/`, `uart/`, `gpio_isr_rx/`, `ble/`. Each is an OO peripheral abstraction with "automatic initialization and fallback handling" and "thread-safe operations" per the in-tree README. The pre-overhaul code lived as monolithic `clockless_rmt_esp32.h` / `clockless_i2s_esp32.h`. -- **Runtime driver switching** is the headline new API: `FastLED.add()`, `Channel::create()` (templated, since ~May 2026); non-template overload also lands. Diagnostic: "one-shot affinity-miss" + normalised driver names. Source: commit log on `master` around 2026-05-11 to 2026-05-12. -- **Removed**: `FASTLED_DISABLE_LEGACY_DRIVER_REGISTRY` macro (legacy registry is now opt-out-by-default). -- **Platforms**: classic ESP32, S2, S3, C3, C6, P4 (parlio is the P4 backend). Teensy 4.x via ObjectFLED (default since 3.10.x). ARM M0/M0+ for UCS7604 RGBW (beta). -- **Protocols**: WS281x family, SK6812 RGBW, APA102, SK9822, HD107S, LPD8806, WS2801, UCS7604 (16-bit RGBW, beta), and ~20 others. -- **Parallel pins**: up to 8 via RMT, up to 16 via I80/LCD parallel (S3/P4), up to 50 on Teensy 4 via ObjectFLED. -- **Threading**: per-driver; the LCD/I80 path is DMA + GDMA on S3, zero CPU during transmit. -- **Hot reconfig**: the new runtime-driver API is explicitly designed for it. This is the largest architectural delta from the September release. -- **WiFi coexistence**: better than legacy FastLED because the LCD/parlio paths are DMA-driven. Historical RMT bugs (`taskYIELD()` during WiFi storms) are mitigated in the new path. -- **Field notes**: master is in flux; the API surface above is fresh as of May 2026 and could shift before the next tagged release. Anyone building on it should pin a commit SHA, not the version. - -### NightDriverStrip — https://github.com/PlummersSoftwareLLC/NightDriverStrip - -- **HEAD**: `53edb35` (2026-05-16), `main`, 1,551 stars, GPL-3.0. -- **Abstraction**: built on FastLED (the legacy monolithic ESP32 RMT/I2S path, not the master refactor — release-version-pinned in `platformio.ini`). -- **Platforms**: ESP32 (classic, S2, S3) on a variety of dev boards (M5Stick, Heltec, LilyGo, plain DevKit). HUB75 via custom integration. -- **Threading**: explicit multi-task design — render task pinned to core 1, networking and effects on core 0. The README's "core affinity" discipline is what makes the project recognisable. Documentation here is thin; the patterns live in the source. -- **WiFi coexistence**: addressed by core pinning and explicit FreeRTOS task priorities, not by disabling WiFi. -- **GPL-3.0** is the key gotcha — copyleft constraints make code reuse in MoonLight (under whatever final licence) a deal-breaker for direct copy. Read it for **patterns**, not lines. -- **Field notes**: the canonical example of "how to keep a multi-thousand-LED ESP32 install stable while WiFi is on". Worth studying, not vendoring. - -### ESP-IDF `led_strip` — https://github.com/espressif/idf-extra-components/tree/master/led_strip - -- **Abstraction**: official Espressif component. Two backends — RMT (RMT5 in IDF 5.x) and SPI clockless (single-data-pin via SPI MOSI rate-matching). -- **Platforms**: every IDF-supported ESP32 variant. -- **Protocols**: WS2812/SK6812 generic single-wire. -- **Hot reconfig**: handle-based, init/deinit per strip. Multi-strip via multiple handles; no built-in parallel multiplexing. -- **License**: Apache-2.0. -- **Field notes**: the lowest-common-denominator reference. Worth knowing for the SPI-MOSI trick alone (one-pin WS2812 with zero CPU). Not a contender for large installs. - -### ESP32 driver summary - -| Driver | Abstraction | Platforms | Protocols | Max LEDs envelope | Hot reconfig | Licence | Maintenance | One-line | -|---|---|---|---|---|---|---|---|---| -| WLED bus layer | NeoPixelBus (RMT + I2S) | ESP32 / S2 / S3 / C3 / C6 / P4 | WS281x, APA102, SK9822, TM1814, DMX, HUB75 | ~10K typical, more via I2S-parallel | add/removeAll, full re-init | EUPL-1.2 | very active | full firmware, hard to extract | -| WLED-MM bus layer | same + hot-path tuning + HUB75 | as upstream | as upstream | ~16K demonstrated on Olimex per WLED-MM users | as upstream | EUPL-1.2 | active | hardened upstream, no virtual driver | -| I2SClocklessLedDriver | ESP32 I2S-LCD / S3 LCD_CAM+GDMA | ESP32, S3 | WS281x, SK6812 | 16 pins × strip length; 8K-30K | init-time pin change | MIT | active (2026-04) | DMA, ISR, 16 parallel pins, no WiFi issues | -| I2SClocklessVirtualLedDriver | same + 74HC595 shift-reg | ESP32, S3 | WS281x, SK6812 | up to ~120 strips, 50K-100K+ field-proven | init-time | MIT | stable (2024-11) | the heavy-iron option | -| FastLED master | modular per-peripheral drivers | ESP32 / S2 / S3 / C3 / C6 / P4 | WS281x + ~20 others | 50 pins on Teensy; 16 via LCD on S3 | **runtime driver-switch API new** | MIT | flux on master, stable releases | major overhaul — read HEAD not Sep release | -| NightDriverStrip | FastLED (legacy path) | ESP32 family | WS281x, HUB75 | demos with several thousand | restart | GPL-3.0 | active | study the task-pinning patterns | -| ESP-IDF led_strip | RMT5 / SPI clockless | every ESP32 variant | WS281x | per-handle, modest | per-handle | Apache-2.0 | active | reference, not a contender | - -## Teensy - -Three options for Teensy 4.x (NXP i.MX RT1062). License notes per project; all permissive. - -- **OctoWS2811** — https://github.com/PaulStoffregen/OctoWS2811 — Paul Stoffregen's reference. 8 parallel WS281x outputs via DMA on Teensy 3.x; on Teensy 4.x uses the `_imxrt.cpp` variant which exploits the i.MX RT's DMA + FlexIO. Stable but slow release cadence (last release March 2022). The historical Teensy reference. -- **ObjectFLED** — https://github.com/zackees/ObjectFLED — up to **40 parallel pins on Teensy 4.0, 55 on Teensy 4.1**. Uses OctoWS2811's DMA core underneath, layered to add per-object independent timing. Non-blocking show — returns in ~6% of transmission time. Teensy 4.x only; FastLED-compatible. This is FastLED's default Teensy backend since 3.10.x. -- **FastLED on Teensy** — same library, ObjectFLED backend by default. The master-branch refactor (above) consolidates Teensy paths but doesn't reshape them as drastically as the ESP32 layer. - -Teensy is a strong fit when the LED count exceeds what an ESP32 can drive cleanly and Ethernet (built-in on 4.1) supplants WiFi entirely. No WiFi-coexistence problem, period. - -## Raspberry Pi - -Three patterns, all known but each with caveats in 2026. - -- **rpi-ws281x** — https://github.com/jgarff/rpi_ws281x — BSD-2. The historical reference. Supports Pi 1-4 via PWM (2 channels), PCM (1 channel), or SPI MOSI (1 channel). Up to ~5,400 LEDs via PCM, ~2,700 per string via PWM. **Pi 5 (RP1 chip) is broken** — issues #554 (2025-04, build fails when `PAGE_SIZE != 16384`), #555 (2025-05, "HW revision not supported"), #564 (2025-09, 2 GB variant). Last release v1.0.0 in March 2023. Community-maintained; no active Pi 5 PR landed at HEAD read time. -- **Pi-via-SPI bit-bang** — using the Pi's SPI MOSI as a one-pin WS281x driver at ~3.2 MHz, encoding each WS281x bit as 3-4 SPI bits. Works on Pi 5 because SPI is still exposed via RP1. Single-pin, but reliable. -- **OPC server pattern** — Pi as a render node feeding LEDs via Open Pixel Control over USB to a downstream ESP32/Teensy, or via E1.31/ArtNet over Ethernet. Doesn't address the Pi's own GPIO, but is the most-deployed Pi pattern in 2026 for large installs. -- **Userspace GPIO libraries** (2026-10-11): WiringPi, now a community fork after its author stopped, and the kernel's GPIO character device through libgpiod make pins portable across Pi-compatible boards. Both go through a call per pin change, far too slow and jittery for WS2812's ±150 ns or HUB75's refresh, so LED output on any Pi takes a peripheral with DMA (PWM, PCM, SPI), PIO, or direct register writes. -- **HUB75 on a Pi** (2026-10-11): hzeller's rpi-rgb-led-matrix (https://github.com/hzeller/rpi-rgb-led-matrix) drives panels on a Pi 1 to 4 by writing the GPIO registers directly. Its options are a tested catalog of the panel variety a HUB75 backend has to cover: multiplexing schemes (`--led-multiplexing`), row-address types (`--led-row-addr-type`), driver-chip init sequences such as FM6126A (`--led-panel-type`), chained and parallel panels, PWM depth, a pixel mapper. MoonLight writes its own drivers, so it is study material, implemented from the chip datasheets with credit. -- **Pi 5 / RP1** (updated 2026-10-11): feasible through RP1's PIO. Raspberry Pi publishes piolib (https://www.raspberrypi.com/news/piolib-a-userspace-library-for-pio-control/, BSD-3) over a kernel driver: the PIO registers are not reachable over RP1's PCIe link, only the state machines' FIFOs through DMA, so userspace goes through that driver. RP1 has one PIO block, 4 state machines and 32 instruction slots. HUB75 on the Pi 5 is proven by Adafruit's PioMatter (https://docs.circuitpython.org/projects/piomatter/en/latest/adafruit_raspberry_pi5_piomatter.html), alpha, up to 3 panels, written because the register writes of earlier Pis stopped working. No worked WS2812-over-PIO example on RP1 was found; the technique is the RP2040's, and parallel lanes would feed pre-encoded slots through a FIFO by DMA, as the Teensy driver feeds its port. WS2812 on a Pi 5 today goes over SPI, one lane on MOSI (the rpi5-ws2812 package). - -## Desktop - -Product owner's question: "Is there any way to do something with GPIO pins? E.g. connect an ESP32 via USB, high-speed access via USB to its pins?" Honest answer: - -- **USB-CDC to ESP32 bridge** — by far the most common pattern. ESP32 acts as a smart pixel pusher; the desktop sends RGB data over USB serial. ESP32-S3's native USB-Serial-JTAG bus sustains ~12 Mbps (1.5 MB/s); UART-bridge boards (CP210x, CH340) cap at ~1 Mbps. At 12 Mbps, 30K RGB LEDs at 50 FPS needs 4.5 MB/s — over the wire's limit, so this caps somewhere around 10-15K LEDs. **The ESP32 is the driver; the desktop is the renderer.** -- **OPC over USB-CDC** — same idea with a defined protocol on top. Open Pixel Control (Zestyping/openpixelcontrol) is widely supported by visualisers (Processing, TouchDesigner). Maintenance dormant — superseded by E1.31/DDP in professional lighting — but still works. -- **FT232H bit-bang** — FTDI's MPSSE mode runs at up to 30 MHz. Libraries (pyftdi, libFTDI) can produce WS281x-timed pulses directly from the desktop, no microcontroller. Realistic for ~500-2000 LEDs; not for the project's targets. Niche but it does exist. -- **USB-to-SPI dongles** — Bus Pirate / FT2232H / Cypress CY7C65211. Same envelope as FT232H, marginally easier API. Same scaling ceiling. -- **Native desktop GPIO** — parallel-port WS281x bit-bang was a thing in 2015. Effectively extinct in 2026. - -**Direct answer to the question**: yes, "ESP32 over USB" is real, well-trodden, and the right model for desktop-driven installations up to ~10K LEDs. Beyond that, the desktop should output E1.31/DDP/ArtNet over Ethernet to one or more ESP32/Teensy nodes — the network protocols are designed for exactly this scale. - -## Architectural primitives observed - -**Output paths.** Five clusters across the field: - -- *RMT* (ESP32 legacy + RMT5) — per-bit timing precision, but channel count is 4-8 depending on variant, and the IDF 5 RMT5 API is structurally different from RMT4. Good for ≤8 strips. -- *I2S parallel* (classic ESP32) — one peripheral, 8-16 GPIOs in lockstep via DMA. Pixel data must be *transposed* (interleaved across strips bit-by-bit) before DMA reads it — this is the work hpwit's driver does in its ISR. -- *LCD-CAM / I80* (ESP32-S3) — successor to I2S parallel; faster, cleaner DMA via GDMA. FastLED master's `lcd_cam` driver is here; hpwit's S3 path is also here. -- *parlio* (ESP32-P4) — new parallel I/O peripheral, ESP-IDF 5.3+ only. FastLED master has a backend; ecosystem otherwise sparse. -- *SPI MOSI* — encode each WS281x bit as 3-4 SPI bits at ~3 MHz. One pin, but DMA-driven and zero CPU. The Pi and ESP-IDF `led_strip` both use this. -- *FlexIO/DMA* (Teensy 4.x) — OctoWS2811's territory. Functionally equivalent to ESP32 I2S-parallel. - -**Buffer strategies.** Single-buffer + busy-wait (small installs, RMT4) gives way to double/N-buffer + DMA chain at scale. hpwit uses `nbDmaBuffer + 2` (default 6) circular descriptors with the ISR doing on-the-fly transposition — this is the most sophisticated pattern in the field. NeoPixelBus and ESP-IDF `led_strip` use simpler double-buffering. Per-strip vs mega-buffer trade-off: per-strip is more flexible (independent strip lengths), one mega-buffer is faster to DMA. hpwit supports both via overloaded `initled()` with per-strip `sizes[]`. - -**WiFi-coexistence patterns.** Three approaches: - -1. *Core pinning* — driver on core 1, WiFi on core 0. NightDriverStrip is the canonical example; hpwit's drivers also support this via `enableShowPixelsOnCore()`. -2. *DMA-driven output* — once data is in DMA, WiFi can't steal it. This is the real reason I2S/LCD-CAM drivers don't flicker: it's not core affinity, it's that the CPU isn't in the timing-critical path at all. -3. *Disable WiFi during transmit* — never used in serious projects; defeats the point of a networked controller. Mentioned only to dismiss. - -Most libraries (NeoPixelBus, FastLED legacy RMT, Adafruit_NeoPixel) don't address this and rely on the CPU staying responsive — which is exactly when WiFi storms cause visible glitches. - -**Hot reconfiguration.** A genuine differentiator. WLED's `BusManager::add()`/`removeAll()` supports runtime bus mutation but at the cost of full reallocation. hpwit's drivers require an `initled()` re-init for pin/count change. **FastLED master's runtime-driver-switch API** (May 2026) is the only library where switching the entire driver (RMT ↔ LCD) at runtime is a first-class supported operation. For MoonLight, the requirement is GPIO/protocol/count via UI controls — that's "re-init on change" pattern, achievable with all of these but ergonomically cleanest with FastLED's new API. - -## Research question — can the virtual driver be reused on Teensy / Pi? - -The product owner's explicit question. Honest answer from reading the source: - -The technique has **two components**: (a) parallel-clocked GPIO output via a peripheral that can drive N pins in lockstep from DMA-fed memory, and (b) external 74HC595 shift registers that demultiplex one clocked output into 8 strip signals via a clock/latch protocol. (b) is hardware — protocol-agnostic, reusable anywhere. (a) is platform-specific. - -**Teensy 4.x (NXP i.MX RT1062)**: built (2026-10-11) as GPIO-DMA rather than FlexIO: a QTimer paces one eDMA channel writing pre-encoded slots into a GPIO port, up to 16 lanes, because no 8 contiguous FlexIO pins stay free beside a W5500. The original FlexIO assessment follows. It has **FlexIO**, a programmable parallel I/O peripheral with DMA support. FlexIO can do parallel-clocked output of 8-32 GPIOs with timer-driven clocking. The architecture matches what ESP32's I2S-LCD provides. **Conceptually portable**, but the rewrite is non-trivial: FlexIO's programming model (shifters + timers + pin muxing) doesn't map 1:1 to I2S DMA descriptors. The `loadAndTranspose()` ISR pattern needs adapting to FlexIO's interrupt model. **Estimate: 1-2 weeks of focused work**; existing OctoWS2811 i.mx_rt code is the reference for FlexIO + DMA, but it doesn't do the shift-register multiplexing. **Needs Stage-2 verification** to confirm FlexIO clock can reach the 800 kHz × 8-slot = 6.4 MHz effective rate needed. - -**Raspberry Pi 4 (BCM2711)** — has PWM and SMI (Secondary Memory Interface) for parallel output, plus DMA. Less programmable than FlexIO; the timing precision is the question. **Likely portable but with effort**; not a clean fit. - -**Raspberry Pi 5 (RP1)**: RP1 contains PIO blocks (the same architecture as RP2040). PIO is a near-perfect fit for the virtual driver pattern: programmable state machines with deterministic timing, DMA-fed FIFO, parallel pin output up to 32 GPIOs. **Conceptually the best target outside ESP32.** The userspace access path is answered (2026-10-11): the kernel's PIO driver, which piolib wraps, and Adafruit's PioMatter drives HUB75 through it. See the Raspberry Pi section above. - -**Summary**: the technique generalises. ESP32 I2S-LCD and RP1 PIO are the natural fits; Teensy FlexIO is workable; BCM2711 is the weakest. The shift-register hardware is reusable across all platforms. A cross-platform virtual driver is a real engineering target, but each port is 1-3 weeks, not a weekend. - -## Stage-2 candidates - -For the product owner to pick 2-3 from: - -1. **FastLED master — modular driver layer** — what we'd learn: whether the new runtime driver-switching API and per-peripheral driver subdirectories are mature enough to use directly, or if vendoring a snapshot makes more sense. Estimated cost: 1.5 days. -2. **I2SClocklessVirtualLedDriver — fork-and-vendor scope** — what we'd learn: minimal API surface to wrap as a MoonLight driver MoonModule; what config controls (pin map, brightness, gamma, virtual-pin count) need to be hot-reconfigurable; whether the init-time-only constraint is acceptable. Estimated cost: 1 day. -3. **WLED-MM bus layer extraction** — what we'd learn: whether the bus layer can be lifted out of WLED-MM and embedded in MoonLight cleanly (or if it's too entangled with WLED's segment/effect plumbing). The EUPL-1.2 licence interaction with MoonLight's licence is part of this. Estimated cost: 1 day. -4. **NeoPixelBus directly** — what we'd learn: bypass WLED entirely, use the underlying library as MoonLight's WS281x backend. NeoPixelBus is what every WLED bus class wraps anyway. Estimated cost: 0.5 day. -5. **Hybrid: hpwit non-virtual + FastLED for non-WS281x** — what we'd learn: covers the 1-30K WS281x case with hpwit's I2S-LCD driver (no shift registers needed) and falls back to FastLED for APA102/SK9822/HD107S. Estimated cost: 1 day. -6. **ObjectFLED + FastLED Teensy path** — what we'd learn: feasibility of Teensy 4.1 as a parallel target alongside ESP32, especially for installations where WiFi is replaced by 4.1's built-in Ethernet. Estimated cost: 1 day. - -## Answers — product-owner direction (2026-05-25) - -These were the open questions; the answers now drive the architecture below. - -1. **Teensy 4.1 — near-term target.** The architecture must host a Teensy backend from day one, not as "later work". ObjectFLED (FlexIO + DMA) is the reference; FastLED-on-Teensy is the implementation path for the "use libraries" scenario. -2. **Licence — GPL-3.0 default; MIT under consideration.** Either way the WLED-MM bus-layer extraction is licence-compatible (EUPL-1.2 is compatible with GPL-3.0; MIT is compatible with both). Neither rules out any Stage-2 candidate. Decision deferred but stops being a blocker. -3. **Custom PCBs are acceptable.** The virtual-driver shape (74HC595 shift-register multiplex) is in-house already; the architecture must natively support `pinCount × stripsPerPin` topology, not bolt it on as a special case. -4. **Runtime driver switching is a hard requirement.** Confirmed: switch RMT ↔ LCD-CAM ↔ I2S ↔ virtual from the UI. FastLED master's new `Channel::create()` proves the field is converging here. This is the architectural axis everything else hangs off. -5. **Raspberry Pi 5 — stretch goal.** RP1 PIO stays in the architecture's future-target list (the abstraction must not assume Xtensa or NXP). Doesn't drive Stage-1 sketch choices, but the boundary line should respect it. - -## Reference architecture - -The architecture splits at one place: **what the LED driver receives** vs **what the LED driver does**. Above the line is platform-independent; below the line is platform-dependent. Both Stage-2 scenarios (build vs. borrow) sit on the **same** above-the-line shape — they differ only in who provides the below-the-line implementations. - -Below the line is **two orthogonal axes**, not a flat list of backends: - -- **Backend axis** — which parallel-clocked peripheral feeds the wire (RMT, I2S-LCD, LCD-CAM, parlio, FlexIO, RP1 PIO, …). -- **Multiplex axis** — `None` (one peripheral pin = one strip) or `ShiftReg` (one peripheral pin → N strips via 74HC595 cascade; the "virtual driver" technique). - -The virtual-driver technique is *not* a sibling backend alongside LCD-CAM — it's a **modifier on top of** any parallel-clocked backend. The shift-register protocol on the wire is identical regardless of which peripheral is driving the parallel pins underneath. hpwit's library uses I2S-LCD on classic ESP32 and LCD-CAM+GDMA on the S3, but the technique generalises: anywhere there's a parallel-clocked DMA-fed pin block (parlio on P4, FlexIO on Teensy, RP1 PIO on Pi 5), the multiplex can hang off it. - -```text - hardware-independent (above the line) -+--------------------------------------------------------------------+ -| Layouts → Layers → BlendMap → Drivers (container) → Layer buffer | -| OR | -| outputBuffer_ | -| ↓ | -| std::span + metadata | -| ↓ | -| LedDriver::push(span) | -+----------------------------------|---------------------------------+ - hardware-dependent (below the line) - ↓ -+--------------------------------------------------------------------+ -| LedDriver = composition of a Backend + a Multiplex | -| | -| Multiplex axis → | -| None (direct) ShiftReg (virtual) | -| ┌────────────────────┬────────────────────────────┐ | -| Backend Rmt│ RmtLedDriver │ (RMT can't drive shift-reg │ | -| axis │ (ESP32 family, │ cleanly: no parallel-clock │ | -| ↓ │ ≤8 strips/chip) │ pin block — N/A) │ | -| ├────────────────────┼────────────────────────────┤ | -| I2sLcd│ I2sLcdLedDriver │ I2sLcdVirtualLedDriver │ | -| │ (classic ESP32, │ (classic ESP32, hpwit's │ | -| │ 16 parallel pins) │ current real-world deploy)│ | -| ├────────────────────┼────────────────────────────┤ | -| LcdCam│ LcdCamLedDriver │ LcdCamVirtualLedDriver │ | -| │ (S3, 16 pins, │ (S3, hpwit's modern path; │ | -| │ GDMA) │ S3-overclocked for headroom)│ | -| ├────────────────────┼────────────────────────────┤ | -| Parlio│ ParlioLedDriver │ ParlioVirtualLedDriver │ | -| │ (P4, 16 pins, new) │ (P4 future port — needs │ | -| │ │ Stage-2 verification) │ | -| ├────────────────────┼────────────────────────────┤ | -| FlexIo│ FlexIoLedDriver │ FlexIoVirtualLedDriver │ | -| │ (Teensy 4.x via │ (Teensy future port — │ | -| │ ObjectFLED-like) │ needs Stage-2 verification)│ | -| ├────────────────────┼────────────────────────────┤ | -| Rp1Pio│ Rp1PioLedDriver │ Rp1PioVirtualLedDriver │ | -| │ (Pi 5 stretch) │ (Pi 5 stretch + virtual) │ | -| ├────────────────────┼────────────────────────────┤ | -| SpiMosi│ SpiMosiLedDriver │ (single pin: no multiplex │ | -| │ (one pin, any chip,│ benefit — N/A) │ | -| │ small installs) │ │ | -| └────────────────────┴────────────────────────────┘ | -| | -| BareMetalBackend — escape hatch for exotic chips; same two-axis | -| shape, just owns its register pokes instead of using a HAL. | -| | -| Each LedDriver instance owns: peripheral handle, DMA descriptors, | -| transposition buffer, optional ShiftRegMultiplex member, ISR. | -| None of this leaks above the line. | -+--------------------------------------------------------------------+ -``` - -The multiplex axis is **composition, not inheritance**: a `LcdCamLedDriver` instance has a `Multiplex multiplex_` member (defaulting to `Multiplex::none()`); setting it to `Multiplex::shiftReg(virtualPins, cascadeDepth)` enables the virtual driver mode at runtime. The peripheral backend stays the same — only the inner-loop transposition function changes, and only at topology-change time, not per-frame. This preserves the runtime driver-switch requirement (axis 1) *and* the runtime virtual-on-or-off switch (axis 2) without templates. - -### The line itself — `LedDriver::push(std::span)` - -The boundary is a single virtual call per frame per driver. Inside `push()`, the driver uses its own typed peripheral handle with no further virtual dispatch — hot-path-clean. The line is intentionally narrow: just bytes and metadata, no buffer ownership crossing it. The driver does not store the span beyond the call's scope (the caller's buffer may be reused for the next frame). - -Metadata carried by the driver as bound MoonModule controls, **not** through `push()`: - -- `pinMap[]` — which GPIO each strip lives on (or, for virtual driver: which shift-register output). -- `stripLengths[]` — per-strip light counts, sums to `bufferBytes / channelsPerLight`. -- `protocol` — WS2812 / WS2815 / SK6812 / APA102 / SK9822 / HD107S / etc. -- `colorOrder` — GRB / RGB / GRBW / etc. -- `gamma` (1.0-3.0). -- `globalBrightness` (0-255 uint8). -- *Backend-specific*: each backend may expose its own controls (RMT clock divider, LCD overclock factor, FlexIO timer divider). These live on the backend, not the base. - -Per-frame: just the span + an implicit "size matches the bound metadata". - -> **Superseded: `colorOrder` / `gamma` / `globalBrightness` shipped differently.** These three are now the **output correction** (`src/light/drivers/Correction.h`): the `Drivers` container holds the shared global `brightness`, and each driver owns its own `Correction`, built from that brightness and the driver's own `fixture` (channel order *and* RGBW), rather than binding its own `colorOrder`/`gamma`/`brightness`. One brightness across all physical drivers, one wiring per driver; ArtNet already uses it. The future LED driver consumes the same `Correction`: see [leddriver-analysis-top-down.md § 4.6](leddriver-analysis-top-down.md) and [MoonLight, drivers](../../explanation/architecture/moonlight.md#drivers). Gamma is not implemented yet (the LUT is brightness-only; gamma folds in later as a per-channel R/G/B split). - -### Identity-mapping fast path preserved - -The existing optimisation at `Drivers.h:90` — when `layer_->lut().hasLUT() == false`, drivers point at `layer_->buffer()` directly, no copy — survives unchanged. The architecture sketches the `outputBuffer_` path because it's the common case, but the identity case is the *fast* case: - -```text -Identity case (no MappingLUT): - Layer.buffer ────────► LedDriver::push(span) (zero-copy) - -Composed case (one or more Layers with non-identity LUT): - Layer1.buffer ─┐ - Layer2.buffer ─┼─► BlendMap → Drivers.outputBuffer_ ─► LedDriver::push(span) - ... ─┘ -``` - -The driver doesn't know which path produced the span. That's the point. - -### Scenario A — Use existing libraries - -Below the line is **FastLED master** (the modular driver subdirectories `src/platforms/esp/32/drivers/` and FastLED's Teensy ObjectFLED integration) for the backend axis, **plus a vendored fork of hpwit's library for the multiplex axis only**. - -```text -+--------------------------------+--------------------------------------+ -| MoonLight LedDriver (backend×Multiplex)| External implementation | -+--------------------------------+--------------------------------------+ -| RmtLedDriver (None) → FastLED::add | -| LcdCamLedDriver (None) → FastLED::add | -| FlexIoLedDriver (None) (Teensy)→ FastLED::add | -| ParlioLedDriver (None) (P4) → FastLED parlio backend | -| | -| LcdCamLedDriver (ShiftReg) → FastLED lcd_cam DMA path + our | -| ShiftRegMultiplex transposition. | -| hpwit's lib provides the transposition | -| algorithm reference; we vendor that | -| function only, not the peripheral init | -| (FastLED owns that). | -| I2sLcdLedDriver (ShiftReg) → same shape on classic ESP32 (FastLED's | -| legacy I2S backend hosts our multiplex)| -+--------------------------------+--------------------------------------+ -``` - -Pros: ride FastLED's runtime driver-switch API and per-backend maturity (matches requirement 4). FastLED owns the peripheral churn (RMT4 → RMT5 → parlio API revisions); we own the multiplex transposition and the MoonLight-side wiring. Teensy support comes essentially free via ObjectFLED. The multiplex code we vendor is small (~200 lines from hpwit's lib — just the transposition + cascade-shift protocol, not the peripheral init). ~2-3 weeks to a working ESP32 + Teensy hybrid; +1 week for the multiplex layer on top of FastLED lcd_cam. - -Cons: FastLED master is in flux (May 2026 surface could shift), so we pin a commit SHA, not a version. The multiplex-on-top-of-FastLED-lcd_cam path needs FastLED to expose its DMA buffer for us to write into — its current API doesn't do that cleanly, so we either bypass FastLED for that one specific backend (effectively becoming Scenario B for it) or upstream a PR to FastLED adding the hook. **This is the brittle seam of Scenario A.** - -### Scenario B — Build everything ourselves - -Below the line is **our own peripheral backends + our own multiplex layer**, all talking to ESP-IDF / NXP HALs (the floor is HAL-level, not register-level: `esp_lcd_panel_io`, `parlio_tx_unit`, `rmt_tx_channel`; NXP FlexIO HAL on Teensy; not register pokes — that's the `BareMetalBackend` escape hatch). - -```text -+--------------------------------------+--------------------------------+ -| MoonLight LedDriver (backend×Multiplex)| ESP-IDF / NXP HAL | -+--------------------------------------+--------------------------------+ -| RmtLedDriver (None) → rmt_tx_channel + encoder | -| I2sLcdLedDriver (None) → legacy I2S in LCD mode + DMA | -| I2sLcdLedDriver (ShiftReg) → same + our ShiftRegMultiplex | -| LcdCamLedDriver (None) → esp_lcd_panel_io_i80 + GDMA | -| LcdCamLedDriver (ShiftReg) → same + our ShiftRegMultiplex | -| ParlioLedDriver (None) → parlio_tx_unit (IDF 5.3+) | -| ParlioLedDriver (ShiftReg) (future) → same + our ShiftRegMultiplex | -| FlexIoLedDriver (None) (Teensy) → NXP FlexIO HAL + eDMA | -| FlexIoLedDriver (ShiftReg) (future) → same + our ShiftRegMultiplex | -| (Rp1PioLedDriver — stretch) → /dev/pio + ioctl | -| SpiMosiLedDriver (None) (single pin) → SPI MOSI bit-rate encoder | -| BareMetalBackend (exotic) → register pokes for one-offs | -+--------------------------------------+--------------------------------+ -``` - -Notice that **the ShiftReg row is the same class** across all the parallel-clocked backends. `ShiftRegMultiplex` is one ~300-line class parameterised by virtual-pin count and cascade depth, sharing the transposition algorithm. The peripheral backend doesn't know it's being multiplexed — its `pinCount` is the physical pin count; the multiplex layer expands that to `physicalPins × stripsPerPin` upstream of `push()`. - -Pros: everything under our roof, no upstream churn surprises. The two-axis composition is *natural* in our tree because there's no library boundary to fight: the multiplex layer cleanly sees the backend's DMA buffer. Virtual driver as a first-class deployment target everywhere (S3 today, P4 / Teensy / Pi5 when those backends land). Hot-reconfigure semantics are exactly what we want. No upstream API stability dependency. - -Cons: 2-4 weeks per backend (HAL-floor; longer for parlio because of its newness). 5 backends × ~3 weeks + multiplex layer + integration = ~4-5 months before parity with what FastLED + vendored multiplex would give us in 3-4 weeks. Carries ongoing maintenance cost for every IDF API churn. Teensy backend needs separate test hardware. **The cost is mostly in the backend axis, not the multiplex axis** — the multiplex layer is small and stable. - -### Feasibility check — is the two-axis split actually reachable? - -This section is **hope-and-gotchas**, written after one pass through hpwit's `I2SClocklessVirtualLedDriver/src/I2SClocklessVirtualLedDriver.h` (HEAD, ~3820 lines, 2026-05-25). The architectural picture above is the goal; what follows is what stands between us and it. - -**What I found in hpwit's source:** - -- **Two distinct code paths interleaved through one file.** Classic ESP32 uses I2S0 in LCD mode (`soc/i2s_reg.h`, direct register pokes into `(&I2S0)->lc_conf.val`). S3 uses LCD-CAM + GDMA (`soc/lcd_cam_struct.h`, `hal/gdma_types.h`, `esp_private/gdma.h`). 30 `#ifdef CONFIG_IDF_TARGET_ESP32S3` blocks across the file; the two paths share the `loadAndTranspose()` inner-loop algorithm but the peripheral init, DMA setup, and ISR are entirely separate per target. -- **The multiplex code is not factored out.** `NUM_VIRT_PINS` and `NBIS2SERIALPINS` are macros baked at compile time. The transposition function (`loadAndTranspose` at lines 3533 + 3713 — itself duplicated under different `I2S_MAPPING_MODE` paths) intermixes the shift-register cascade math with the peripheral's DMA buffer layout (`DMABuffersTampon[dmaBufferActive]->buffer`). The shift-register protocol and the peripheral's DMA descriptor format are entangled in the same memory layout calculations. -- **No P4 / parlio code.** Zero references to `parlio`, `P4`, or `esp32p4` in the source tree. The S3 path was added relatively recently (the GDMA+LCD_CAM code is the newest large addition); P4 is a similar amount of work, not yet done. - -**What that means for the two-axis split:** - -- **The classic-ESP32 ↔ S3 split as two backends** — already exists *de facto* in hpwit's code via the `#ifdef`, just not factored as separate classes. Extracting `I2sLcdBackend` (classic) and `LcdCamBackend` (S3) as siblings is a real refactor (a few days of careful surgery) but it's reading what's there, not inventing it. **Feasible.** -- **The shift-register multiplex as a reusable layer** — the harder one. The transposition function `loadAndTranspose` knows the DMA buffer's pixel-encoding layout (per-bit interleaving × WS2812 timing × the cascade-shift signalling). Pulling that into a backend-agnostic `ShiftRegMultiplex` class requires the backend to expose its DMA buffer's *encoding contract* (where bit N for output O lives in memory). Different peripherals encode parallel-clocked output differently — I2S-LCD has a specific FIFO layout; LCD-CAM's GDMA descriptors are different again; parlio uses a different bit-packing scheme (per its `parlio_bit_pack_order_t` enum). **The multiplex isn't peripheral-agnostic in the way I wrote earlier; it needs per-backend specialisation of its bit-layout math.** What stays shared is the algorithm shape (cascade-shift signal generation, latch protocol, transposition strategy); what changes per backend is the memory-write pattern. -- **ESP32-P4 + parlio support for the multiplex** — ESP-IDF's `parlio_tx_unit` exposes a 16-pin parallel-clocked DMA-fed output (`SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH = 16` per `esp32p4/include/soc/soc_caps.h:462`). Same architectural shape as LCD-CAM. **The technique conceptually fits**; the work is writing the parlio-specific transposition function that matches parlio's bit-packing layout. Estimated 1-2 weeks of focused implementation + bench-verification on real hardware. **Feasible but unproven.** No one has shipped this; we'd be first. Stage-2 verification required. - -**Net honest read on the two-axis split:** - -The composition I drew is the *architectural target* and is achievable. The reality is: - -1. **`Backend × None`** (RMT, LCD-CAM direct, parlio direct, FlexIO direct) — all feasible; each is a standard peripheral wrapper. -2. **`I2sLcd × ShiftReg`** (classic ESP32 virtual) — hpwit's working code is the reference; lifting it into our architecture is a refactor not a port. **High confidence.** -3. **`LcdCam × ShiftReg`** (S3 virtual) — same, the S3 path in hpwit's lib is the reference. **High confidence.** -4. **`Parlio × ShiftReg`** (P4 virtual) — no existing reference; needs original engineering. The parlio API supports it in principle (DMA-fed parallel-clocked output); the multiplex transposition has to be re-derived for parlio's bit-pack layout. **Medium confidence, Stage-2 verification needed.** -5. **`FlexIo × ShiftReg`** (Teensy virtual) — likewise no existing reference; OctoWS2811 doesn't multiplex. Each port is a multi-week engineering project, not free composition. **Medium confidence.** -6. **`Rp1Pio × ShiftReg`** (Pi 5 virtual) — most speculative. RP1 PIO has the right shape but no LED-driving reference exists. **Low confidence, true research.** - -So the architecture's promise — "the same `ShiftRegMultiplex` class works across backends" — is **aspirational**. What's realistic is "the same `ShiftRegMultiplex` *protocol* (cascade-shift signalling, transposition strategy) generalises, but each `Backend × ShiftReg` pair needs its own per-backend transposition implementation matching that backend's DMA layout". The class hierarchy can express it; the inner-loop code is per-pair work. - -**Possible stoppers that Stage 2 needs to verify:** - -- Does hpwit's algorithm survive being refactored out of one file into a Backend + Multiplex pair without losing performance? The current code's tight coupling is partly a performance optimisation (the transposition writes directly to known DMA-buffer slots). Splitting it adds an abstraction layer that we'd need to prove is zero-cost. -- Does parlio's `parlio_tx_unit_transmit` API support the cascade-shift signalling pattern? The hpwit S3 trick uses LCD-CAM in a slightly unusual way; whether parlio is flexible enough in the same direction is unproven. -- Does the bit-overlap technique (using one peripheral's clock edges to clock shift registers *and* deliver pixel data on other pins) work on parlio? The S3 LCD-CAM lets you commandeer the LCD output clock for this; parlio's clock generation is structurally similar but the pin-routing model differs. - -These are the "hope vs. confirmed" gaps. Stage 2 deep-dive into option #2 (virtual driver vendor) is where they get resolved — and resolves them on real hardware, not by reading source. - -### What does ESP-IDF give us if we build everything ourselves? - -Short answer: **a lot of low-level peripheral plumbing, almost nothing LED-specific.** ESP-IDF treats LED driving as a community concern, not a first-party concern. - -What IDF provides natively (relevant to LED driving): - -- **`driver/rmt_tx.h`** (`esp_driver_rmt`) — the RMT TX peripheral. Generic per-bit pulse-train generator with timing precision. **Useful** for WS281x single-pin output up to 8 channels; programmable encoder support for any clockless protocol. RMT5 API (IDF 5.x) is what we'd target. -- **`driver/parlio_tx.h`** (`esp_driver_parlio`) — parallel I/O TX. Up to 16 GPIOs in lockstep, DMA-fed. **Useful** as the host for our `ParlioBackend` (None and ShiftReg). New (IDF 5.3+), P4-only today, API still stabilising. -- **`esp_lcd_panel_io_i80.h`** (`esp_lcd`) — i80 LCD interface, ESP32-S3. 16-pin parallel data + clock, DMA-fed via GDMA. **Useful** as the host for our `LcdCamBackend`. This is what hpwit's S3 code uses underneath, just exposed via the public IDF API rather than direct register pokes. -- **`driver/spi_master.h`** — DMA-fed SPI MOSI at MHz rates. **Useful** for the `SpiMosiBackend` (one-pin WS281x via bit-rate encoding, the same trick the official `led_strip` component uses for its non-RMT mode). -- **`hal/gpio_ll.h`** — low-level GPIO ops, IRAM-safe. **Useful** for the latch-pin signalling in our shift-register multiplex. -- **GDMA / `esp_private/gdma.h`** — the underlying DMA engine on S3/C-series/P4 chips. **Useful** for advanced backends that need to chain DMA descriptors manually (the way hpwit does on S3). - -What IDF does **not** provide: - -- **No LED-strip driver in core IDF.** No `esp_ws2812.h`, no built-in WS281x bit encoder, no APA102 helper. The `led_strip` community component (`espressif/idf-extra-components`, Apache-2.0) gives a basic single-strip handle on top of RMT or SPI MOSI, but it's a managed-component dependency we'd pull in, not built into IDF. -- **No transposition helpers.** If you want to drive 16 parallel WS281x strips through I2S-LCD or LCD-CAM, you write the bit-interleaving yourself. That's the work hpwit's lib does. -- **No multi-protocol abstraction.** "Drive WS2812 here, APA102 there, DMX over there" is your problem; IDF gives you the peripherals, not the protocol mapping. -- **No color-order / gamma / brightness layer.** Up to us. -- **No hot-reconfigure pattern.** Each peripheral driver's API is "create_unit → enable → transmit → delete"; toggling pin maps at runtime means delete + recreate, and the discipline of doing that without dropping a frame is on us. - -**What that means for Scenario B effort estimate:** - -Per backend, we're writing: - -- ~200 lines of peripheral setup (config struct, handle ownership, GPIO routing) -- ~50-100 lines of DMA buffer management (descriptor chain, double-buffering) -- ~100-300 lines of encoding (WS2812 timing-bit pattern, APA102 SPI clocking, etc.) -- ~50-100 lines of ISR / completion-callback handling -- ~100 lines of hot-reconfigure logic -- ~50 lines of MoonModule control surface - -Per backend: ~500-700 lines of new code. For ESP32 (RMT) + S3 (LCD-CAM) + P4 (parlio) + Teensy (FlexIO) that's 2000-2800 lines just for backend basics. The shift-reg multiplex layer is another ~300-500 lines on top, with per-backend transposition specialisations. - -**IDF helps a lot at the peripheral layer; nothing at the LED-protocol layer.** That's the honest weight of Scenario B. The 2-4-weeks-per-backend estimate stands; it's not naïve, but it does require that the IDF peripheral APIs *don't have surprises*. RMT5 has had three breaking changes between IDF 5.0 and 5.4; parlio is still pre-stabilisation. Expect to pin an IDF version and re-validate on every bump. - -### What we gain by building everything ourselves - -Importing a library means you get a lot that wasn't built for your case. Specifically, what we'd give up by adopting FastLED master under the line: - -- **The pixel buffer layout is FastLED's, not ours.** Their CRGB struct, their channel ordering decisions, their gamma/brightness application points. We bridge — every `push()` call has to translate from MoonLight's flat `std::span` (the existing `Buffer::data()` shape) into whatever FastLED's bus class expects. That bridge is a per-frame copy or a per-frame view rewrite; in either case it's hot-path overhead that disappears in Scenario B because we control both sides. -- **Hot-reconfigure is FastLED-paced.** Their new `Channel::create()` API is the closest the field has to runtime driver-switching, but it's young (May 2026), the API surface is in flux, and "switch RMT to LCD-CAM mid-flight without dropping a frame" is something we'd have to verify works the way we need — not assume. In Scenario B the contract is ours; if a frame drop on driver switch is unacceptable, we engineer around it. -- **No leverage on inner-loop optimisations specific to MoonLight's MappingLUT / identity-mapping fast path.** FastLED knows nothing about our identity-mapping optimisation (`!hasLUT()` → direct Layer-buffer push). To use FastLED's bus path we either pre-compose into FastLED's CRGB buffer (defeating the identity path) or write a thin wrapper that exposes our raw buffer (possible but adds a coupling point we don't control). Scenario B keeps the identity path zero-copy through to the wire. -- **The multiplex seam is brittle on FastLED.** This is the strongest finding from the feasibility check above. FastLED has no shift-register multiplex backend. To use FastLED's lcd_cam DMA path *with* our shift-register multiplex, we need FastLED to hand us its DMA buffer for direct writes — its current API doesn't, and either (a) we bypass FastLED for that backend (effectively Scenario B for the most MoonLight-defining backend), (b) we upstream a PR (slow, library-author dependent), or (c) we accept a per-frame copy from FastLED's buffer into our transposed buffer (hot-path cost). -- **No control over IDF version pinning.** FastLED master targets whatever IDF FastLED master targets. If our MoonLight IDF version pin disagrees with FastLED's, we either bump them both in lockstep or carry a divergent fork. In Scenario B we own the IDF compatibility matrix. -- **Carrying FastLED is binary size we don't fully use.** FastLED master ships drivers for ~20 protocols, ~6 ESP32 variants, Teensy, ARM M0/M0+, and AVR; gamma tables, palette helpers, FX layer, blur kernels. We use the bus driver and the bus driver only. Dead code elimination at link time helps, but FastLED's runtime driver-registry is the opposite of dead-code-eliminable (the whole point is that drivers are reachable from a string lookup). Realistic estimate: 80-150 KB of binary we'd carry without using. -- **Profile-guided tuning is harder.** MoonLight's hot path is unusual — most LED firmwares don't have a Layer/MappingLUT composition layer above the driver. Tuning the inner loop for MoonLight's specific access patterns is straightforward if we own the inner loop; in Scenario A it's a sequence of conversations with the library maintainer. - -What we **lose by building everything ourselves**: speed-to-first-working-driver. Scenario A puts an ESP32 + Teensy hybrid in your hands in ~3 weeks. Scenario B is multi-month before parity, and during those months we own every IDF API surprise. That cost is real. - -What we **gain by building everything ourselves**: every byte of the hot path is ours, the multiplex axis is symmetric across backends without library-author cooperation, the binary stays focused on MoonLight's actual needs, and the architecture stops being a negotiation between MoonLight's design and FastLED's. **The architecture becomes a contract we own**, not a translation layer between two contracts. - -### Recommendation — walk Scenario B - -Reading the whole document honestly: **build everything ourselves.** Reasons, in priority order: - -1. **The multiplex axis is the MoonLight-defining value-add for the >30K-LED case, and it doesn't compose cleanly onto FastLED.** Scenario A's "ride FastLED + vendor hpwit for the multiplex" framing was the first thing I wrote, and the feasibility check made it visibly worse. The cleanest path for the virtual driver is Scenario B; that's our heaviest single dependency on Scenario A, and it's the spot where Scenario A is structurally weakest. -2. **Runtime driver switching as a first-class requirement aligns naturally with our own contract.** FastLED's API is moving toward this in May 2026 master, but it's young and we'd be downstream of their choices. Owning the contract means owning the semantics of "switch backend without dropping a frame" — the user-facing requirement. -3. **The identity-mapping fast path needs the driver to read directly from `layer_->buffer()`.** That's a MoonLight-specific optimisation that the rest of the architecture is built around (Drivers.h:90 already implements it for ArtNetSend / Preview). FastLED has no incentive to support it; we'd lose the optimisation or carry a coupling layer. -4. **ESP-IDF gives us the peripheral plumbing for free.** As the previous section showed, `parlio_tx_unit`, `esp_lcd_panel_io_i80`, `rmt_tx_channel` are all production-ready. The LED-specific layer on top is small (300-700 lines per backend); we're not writing a peripheral driver from scratch. -5. **License + binary-size tax shrink the surplus from Scenario A.** EUPL-1.2 / MIT / GPL-3.0 mixing is solvable but adds review burden; 80-150 KB of unused FastLED code is real on a 4 MB partition table. - -**Costs accepted by this recommendation:** - -- **Time to first working driver shifts from ~3 weeks to ~3 months.** This is the biggest concrete cost. Mitigated by ordering: ship `LcdCamLedDriver (None)` on ESP32-S3 first (highest-value single backend), then `LcdCamLedDriver (ShiftReg)` on the same chip (validates the multiplex layer against an in-house reference), then `RmtLedDriver (None)` for classic ESP32 fallback, then `FlexIoLedDriver (None)` for Teensy, then expand. By month 4 we have parity with what Scenario A would give in month 1; by month 6+ we have everything Scenario A could never give us (parlio multiplex, RP1 PIO if Pi 5 is ready, custom inner-loop optimisations). -- **Per-backend IDF API surface changes are our problem.** Mitigated by IDF-version pinning + a documented compatibility matrix. Plan-17 already established the v6.1-dev pin discipline; this extends it. -- **Teensy backend needs separate test hardware.** Mitigated by the fact that Teensy 4.1 was named as a near-term target; the hardware purchase is in scope regardless of which scenario. - -**Suggested order of Stage-2 work** (informed by the recommendation): - -1. **Spike: `LcdCamLedDriver (None)` on ESP32-S3 against the existing `Drivers::loop()` shape.** ~1 week. Goal: prove the `LedDriver::push(span)` contract works end-to-end on real hardware (16 parallel pins, 8 KB strip each, 50 FPS). If this spike fails, the rest of the recommendation fails too — so do it first, fastest signal. -2. **Refactor hpwit's S3 path into `LcdCamLedDriver (ShiftReg)` against the same contract.** ~2-3 weeks. This is the highest-information-density spike: validates the two-axis split, validates the multiplex transposition refactor, gives us a working virtual driver on our own architecture. **If this spike fails, the recommendation needs revising — the multiplex-can-be-factored-out claim is the single load-bearing architectural assumption.** -3. **`RmtLedDriver (None)`** for classic ESP32 small-install support. ~1 week (RMT5 is the simplest backend). -4. **Continue per the architecture diagram**, prioritising by user-visible value: FlexIo (Teensy) > Parlio (P4) > Rp1Pio (Pi 5 stretch). - -**When to revisit this recommendation:** - -If the spike at step 2 fails — i.e. the multiplex refactor turns out to be infeasible without ruining performance, or hpwit's S3 path turns out to be too register-poke-specific to extract cleanly — **we fall back to a hybrid**: Scenario A for the non-multiplex backends (RMT, LCD-CAM direct, FlexIO direct), Scenario B for the multiplex layer specifically, accepting that the multiplex bypass-FastLED-DMA path is the brittle seam. The architecture supports this; the diagram remains accurate; the recommendation is just "we shipped less of Scenario B than we wanted." - -## Concrete DriverBase API sketch - -The header that Stage 2 starts from (illustrative, not final). Lives at `src/light/drivers/LedDriver.h`: - -```cpp -#pragma once - -#include "light/drivers/DriverBase.h" -#include -#include - -namespace mm { - -enum class LedProtocol : uint8_t { - WS2812, WS2815, SK6812, SK6812RGBW, - APA102, SK9822, HD107S, TM1814, P9813, - Custom, // backend-specific timing in its own controls -}; - -enum class ColorOrder : uint8_t { - RGB, RBG, GRB, GBR, BRG, BGR, - RGBW, GRBW, // … -}; - -// Per-strip descriptor — one of these per physical strip on the driver's -// peripheral. Stored as a bound MoonModule control array. -struct StripSpec { - uint16_t lightCount; // logical lights on this strip - uint8_t pin; // GPIO or shift-register output index - LedProtocol protocol; - ColorOrder order; -}; - -// LedDriver — abstract base for hardware-specific LED-strip output. -// -// Inherits the existing DriverBase (src/light/drivers/Drivers.h) shape, so -// it composes with ArtNetSendDriver and PreviewDriver in the Drivers -// container without changes to the container. Lifecycle (setup / loop / -// teardown) is MoonModule's; the LED-specific contract is push() + -// onTopologyChange(). -// -// Hot path: push() is called once per render frame per driver instance. -// Inside push(), the implementation uses its own typed members with no -// further virtual dispatch. push() must: -// - not block longer than the per-driver budget (see performance section) -// - not allocate -// - not call any platform API that holds the WiFi stack -class LedDriver : public DriverBase { -public: - // Per-frame entry point. `pixels` is the source buffer (either the - // shared outputBuffer_ or a Layer's own buffer in the identity-mapping - // fast path); the driver does NOT store this span past push() return. - // Span length matches strips_ × channelsPerLight at all times — caller - // (Drivers container) guarantees this; driver may assert in debug. - virtual void push(std::span pixels) = 0; - - // Called when strip count, lengths, pin map, or protocol changes via - // UI. Backends use this to (re-)allocate DMA buffers, re-bind GPIOs, - // and recompute transposition tables. May block (it's not the render - // loop). Returns false if the new topology can't be honoured (e.g. - // RMT channel exhaustion, too many parallel pins for the peripheral). - virtual bool onTopologyChange(std::span strips) = 0; - - // Diagnostic: name of the backend ("rmt", "lcd_cam", "virtual_i2s", - // "flexio", …). Used by the UI's driver-switch dropdown. - virtual const char* backendName() const = 0; - - // Optional: maximum strips/lights this backend can hold given the - // current chip. Used to grey-out impossible UI configurations. - virtual uint16_t maxStrips() const { return 16; } - virtual uint16_t maxLightsPerStrip() const { return UINT16_MAX; } -}; - -} // namespace mm -``` - -Notes on the shape: - -- `std::span` for `pixels` — caller-owned, zero-copy, lifetime tied to the call. ESP-IDF allows `std::span` (C++20 is the project baseline). -- No `color_order` / `gamma` / `brightness` argument to `push()` — those are controls on the driver and applied during push by the implementation. Keeps the boundary narrow. -- `StripSpec.pin` is a uint8 because the virtual driver indexes shift-register outputs (0..63), not real GPIOs. Real-GPIO backends just use it as a GPIO number. -- `onTopologyChange()` returns bool so the UI can surface "this combination isn't possible on this chip" instead of silently truncating. -- No CRTP, no templates — straightforward virtual dispatch, one call per frame. Runtime driver switch is `delete oldDriver; oldDriver = makeDriverByName(...)` — trivially supported. - -## Performance budget (16K LEDs × 50 FPS, ESP32-S3 + PSRAM) - -| Component | Memory | Time per frame | Notes | -|---|---|---|---| -| Frame budget total | — | **20 ms** | 50 FPS = 20 ms wall clock per tick | -| Effect compute (per Layer) | n/a | 8-12 ms | Empirical: Metaballs at 16K = ~12 ms today | -| Layer buffer (one Layer, RGB) | 48 KB | n/a | 16384 × 3 | -| MappingLUT (identity case) | 0 KB | 0 ms | Direct hand-off to driver, the *fast* path | -| MappingLUT (sparse) | 32-64 KB | n/a | (lightCount+1) × sizeof(idx) + dest count × sizeof(idx) | -| BlendMap (composed) | n/a | 1-3 ms | One memcpy in identity case; per-light loop in LUT case | -| Driver `push()` budget | n/a | **3-5 ms** | What's left after effect + blend | -| DMA transmission (background) | per-backend | 16-20 ms | WS2812 timing × strips; runs *after* push returns | -| Output buffer (RGB → wire bytes) | 48-96 KB | n/a | Pre-encoded vs runtime-encoded | -| Free heap headroom on no-PSRAM ESP32 | ≥ 40 KB | n/a | Otherwise WiFi / Ethernet can't start | -| Free heap headroom on PSRAM S3 | ≥ 100 KB | n/a | More breathing room; multi-driver becomes viable | - -Two real numbers worth noting: - -- **DMA transmission is asynchronous to `push()`.** Wire-level WS2812 transmission at 800 kHz takes ~30 µs per LED × 16K = ~500 ms in serial; on parallel hardware (16 pins) it's ~30 ms. Both of these are larger than the frame budget — which is why double-buffering + DMA chains exist. The driver's `push()` returns immediately after handing the buffer to DMA; the next frame's `push()` blocks (or yields) until DMA is free. -- **`push()` budget shrinks fast at higher counts.** At 30K LEDs the effect compute alone is 20+ ms (linear in light count for most effects); the frame budget either grows (drop to 30 FPS) or moves to multi-core (effects on core 1, network on core 0). MoonLight does not pin tasks today — the render runs on whichever core the scheduler task lands on. - -## Hot-path do-and-don't checklist - -Specific to the LED-driver hot path (inside `push()` and anything it calls per-frame). General-purpose render-loop rules from `architecture.md` § Hot path discipline apply too. - -### Don't -- `std::function`, `std::bind`, `std::variant`'s `std::visit` with non-trivial visitor — all heap-allocate or have hidden vtable surface. -- `new`, `malloc`, `push_back`, `std::string`, `std::vector` growth — anything that can hit the heap. -- Virtual calls inside per-light or per-byte inner loops. One virtual call per `push()` for `LedDriver::push()` itself is fine; per-byte virtual dispatch in transposition is not. -- `mutex.lock()`, `semaphore.take()` with non-zero timeout — block the render loop. `try_lock` returning fast is OK. -- `printf` / `ESP_LOGI` — they take a global lock and write to UART. Use `ESP_DRAM_LOGE` if absolutely needed; better, log from `loop1s()` not from `push()`. -- PSRAM for the *output staging buffer* that DMA reads — PSRAM is ~12 MB/s sequential vs SRAM's 80+ MB/s; DMA from PSRAM works but is slow. -- Float math per-light if integer suffices. The existing code is already integer-first; preserve that in any new backend. - -### Do -- Bound the inner loop count statically where possible (`uint16_t` for strip lengths up to 65 535). -- Place ISR helpers in IRAM via `IRAM_ATTR` — IDF-level discipline. -- For DMA buffers on ESP32, prefer `heap_caps_malloc(size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL)` to ensure SRAM, not PSRAM. -- Document each `IRAM_ATTR` placement; the iram budget is tight on the classic ESP32 (~70 KB total). -- Use `std::span` over pointer+size pairs where C++20 syntax allows — keeps the hot path readable without abandoning zero-cost. (Note: this is the opposite of what `platform.h` does today across the broader platform API — there it's pointer+size for consistency with C-style ESP-IDF APIs. Inside the LED driver layer where everything is C++, `std::span` is the right call.) -- For runtime driver switching: free the old backend (DMA stopped, peripheral released, ISR detached) before allocating the new one. Avoid the heap-fragmentation pattern that bit us in plan-18 (Drivers null-deref). - -## Risks and unknowns - -Specific to the LED-driver architecture; ranked by potential blast radius. - -1. **RMT5 API churn.** ESP-IDF's RMT v5 (`driver/rmt_tx.h`) landed in IDF 5.0 and has had non-trivial revisions in 5.1, 5.2, 5.3, and 5.4 — encoder API, callback signatures, queue semantics. Our backend has to pin to one IDF version and re-validate on every IDF bump. FastLED's master takes the same pinning approach; we'd inherit the same risk in scenario A. -2. **parlio is too new.** `parlio_tx_unit` is IDF 5.3+ only, ESP32-P4 only, and the API surface has rough edges (especially around chained DMA and idle-state behaviour). Worth treating as "Stage-2 verification needed" rather than "ready to use". -3. **Virtual driver portability beyond ESP32.** The `loadAndTranspose()` ISR pattern hpwit uses is conceptually portable to FlexIO and RP1 PIO but neither has a published reference implementation. Each port is its own multi-week engineering project; cross-platform "free" doesn't exist here. -4. **LCD overclock non-portability.** hpwit overclocks the S3 LCD clock to ~1.125 MHz for WS2812 timing headroom. This is undocumented behaviour; Espressif could change it without warning. The non-overclocked path works; "overclock for headroom" is performance-only and falls back cleanly if removed. -5. **Hot-reconfigure during DMA in-flight.** What happens if the user changes `pinMap` via the UI while the *current* frame's DMA is mid-transmission? The architecture must define this: most likely "topology changes are taken into account on the next frame's `push()`; the in-flight frame completes on the old topology". `onTopologyChange()` blocks until DMA-quiescent before re-allocating peripheral resources. -6. **WiFi-coexistence empirically variable.** Different routers, different switches, different cable conditions all interact with the WiFi stack's CPU-burst patterns. Our two tracks (DMA-driven + core pinning) cover the known modes; there's an unknown unknown around very-large installs (50K+) that no public library has fully solved. -7. **Identity-mapping path subtlety.** The fast path requires the Layer's buffer layout to *exactly* match the wire layout (no gamma, no brightness, no color-order swap on the way out). If the driver wants to apply gamma/brightness in `push()`, the identity path is gone — we re-introduce the copy. The control-binding shape should make this trade-off explicit per backend. **Resolved for clockless LEDs:** a WS2812-class driver must encode RGB into a pulse pattern anyway, so it already allocates a separate DMA buffer — the `Correction` (brightness/reorder/white) fuses into that encode pass at no extra buffer cost. The true zero-copy-to-wire identity path only ever applied to byte-identical protocols (ArtNet/DMX, APA102-SPI), where there's no encode and correction is the only reason to copy. So "identity → no copy" is a property of the *protocol*, not the mapping. -8. **Build vs borrow lock-in.** Whichever scenario Stage 2 picks, switching later is expensive (months, not weeks). The hybrid escape hatch (Scenario A for some backends, Scenario B for others) reduces this but doesn't eliminate it. - -## Stage-2 candidates (revised) - -The original list still stands; the answers above narrow it: - -1. **FastLED master — modular driver layer** (Scenario A backbone). Confirmed in scope. -2. **I2SClocklessVirtualLedDriver — fork-and-vendor** for the virtual-driver shape. Confirmed in scope (custom PCBs OK). -3. **WLED-MM bus layer extraction** (Scenario A alternative for ESP32-only). Confirmed: license compatible either way. -4. **NeoPixelBus directly** — superseded by 1+3; deprioritise unless 1 and 3 both fall through. -5. **Hybrid hpwit non-virtual + FastLED** — covers the 1-30K WS281x case without shift registers. Still a strong middle option. -6. **ObjectFLED + FastLED Teensy path** — confirmed in scope (Teensy 4.1 is near-term). - -Pick 2-3 for Stage 2. **See the "Recommendation — walk Scenario B" section above** for which to pick under that recommendation (the answer is the two spikes at the head of "Suggested order of Stage-2 work" — `LcdCamLedDriver (None)` on S3 first as a contract-shape spike, then `LcdCamLedDriver (ShiftReg)` to validate the multiplex refactor against hpwit's existing S3 implementation as the reference). - -If you reject the Scenario-B recommendation and want to evaluate Scenario A or a hybrid instead: **#1 (FastLED master) + #2 (virtual driver vendor) + #6 (Teensy via ObjectFLED)** is the trio that exposes the most surface for Stage-2 to characterise. Be aware that this is the trio that produces *Scenario A + a vendored multiplex bypass for the virtual driver*, which is the hybrid path the recommendation explicitly names as the fallback if the Scenario-B multiplex spike fails. diff --git a/docs/work/future/leddriver-analysis-top-down.md b/docs/work/future/leddriver-analysis-top-down.md deleted file mode 100644 index 68a31bfe9..000000000 --- a/docs/work/future/leddriver-analysis-top-down.md +++ /dev/null @@ -1,543 +0,0 @@ -# LED driver — top-down analysis - -A second-attempt design study, deliberately inverted: instead of cataloguing the LED-driver libraries that exist and choosing among them, start from the end goal — *driving WS2812-class LEDs from a GPIO pin* — and reason about what each supported MCU can actually do, how to defend the timing against the rest of the system (WiFi, the OS, the cache), and how to *prove* the output is correct without trusting the code. - -The output of this study is the next-iteration plan: a generic driver architecture, the per-platform implementation that wires into it, the testing strategy that verifies it, and a "hello world" path that starts with WS2812 on ESP32 classic and walks out to Teensy 4.1 and Raspberry Pi 5. - -The earlier study at [leddriver-analysis-bottom-up.md](leddriver-analysis-bottom-up.md) is intentionally not consulted while writing this one; a short reconciliation section at the bottom compares the two after the fact. - -## TL;DR - -- WS2812 is a strict-timing 1-wire NRZ protocol at 800 kHz. Every working driver does the same thing at the bottom: a DMA engine clocks out a pre-encoded bit pattern with sub-150 ns edge accuracy, while the CPU stays out of the timing loop. -- The peripheral that hosts that DMA engine is what changes per device: RMT on ESP32, I2S/LCD_CAM/PARLIO on the newer ESPs, FlexIO on Teensy 4.x, PIO (via RP1) on Raspberry Pi 5. -- The WiFi-glitch problem is, almost without exception, an ISR-latency problem: a refill ISR that misses a deadline by > ~5 µs corrupts one bit and shifts every downstream pixel. The standard fix is to take the CPU out of the refill entirely (DMA-only ping-pong) and pin everything else to the other core. -- Architecture: one tiny abstract `LedDriver` interface; per-platform implementations live in `src/platform//` and are picked by `if constexpr (platform::…)` switches at compile time. No `#ifdef` outside `src/platform/`. -- Testing: on-board GPIO loopback is the cheap CI workhorse (RMT-RX on ESP32, FlexPWM-capture on Teensy). A $12 fx2lafw clone (the project uses a NanoDLA v1.3) + sigrok-cli adds an independent-clock cross-check. A $149 DSLogic Plus is the right "later" upgrade when timing-margin tests start to matter. -- Hello-world order: ESP32 classic → ESP32-S3 → ESP32-P4 → Teensy 4.x → Raspberry Pi 5 (via PIO/RP1 — and seriously consider just bridging to an MCU on the Pi 5). - -## 1. The protocol, in detail - -WS2812 (and the SK6812 RGBW variant) is a 1-wire NRZ protocol clocked at 800 kHz: every bit is a 1.25 µs cell with a high-then-low pulse whose duty cycle encodes 0 or 1. There is no clock line. Receivers latch on idle-low > 50 µs (original WS2812) or > 280 µs (current WS2812B silicon). - -### 1.1 Datasheet numbers and real-silicon tolerances - -| Chip | T0H | T0L | T1H | T1L | Period | Reset (current silicon) | -| ---------- | ------ | ------ | ------ | ------ | ------- | ----------------------- | -| WS2812B | 0.40 µs | 0.85 µs | 0.80 µs | 0.45 µs | 1.25 µs | > 280 µs | -| WS2812 (orig.) | 0.35 µs | 0.80 µs | 0.70 µs | 0.60 µs | 1.25 µs | > 50 µs | -| SK6812 RGB | 0.30 µs | 0.90 µs | 0.60 µs | 0.60 µs | 1.20 µs | > 80 µs | -| SK6812 RGBW | 0.30 µs | 0.90 µs | 0.60 µs | 0.60 µs | 1.20 µs | > 80 µs | - -Sources: [WS2812B datasheet](https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf), [WS2812 datasheet](https://cdn-shop.adafruit.com/datasheets/WS2812.pdf), [SK6812 datasheet](https://cdn-shop.adafruit.com/product-files/1138/SK6812+LED+datasheet+.pdf), [SK6812 RGBW datasheet](https://cdn-shop.adafruit.com/product-files/2757/p2757_SK6812RGBW_REV01.pdf). - -Tim "cpldcpu" Böscke's [reverse engineering of the real WS2812 silicon](https://cpldcpu.com/2014/01/14/light_ws2812-library-v2-0-part-i-understanding-the-ws2812/) shows that the actual decode threshold sits around 600 ns of high time — far more forgiving than the datasheet implies. That is why every library you can name uses slightly different numbers and they all work. The numbers *we* use should be: - -- **Bit timing**: target T0H = 350 ns, T1H = 700 ns, period = 1250 ns, with a ±150 ns budget. These satisfy WS2812, WS2812B, and SK6812 simultaneously. -- **Reset / latch**: ≥ 300 µs idle low. This covers WS2812B current silicon, WS2813, and SK6812. The 50 µs value from the older datasheet is dead — [WorldSemi silently changed it ca. 2017–2018](https://tomverbeure.github.io/2019/10/13/WS2812B_Reset_Old_and_New.html) and code that assumed 50 µs randomly fails on new strips. - -### 1.2 Bit order, byte order, frame order - -- **Pixel order**: GRB (G7..G0, R7..R0, B7..B0), MSB first within each byte. SK6812 RGBW: GRBW, 32 bits, MSB first. Every other chip-specific quirk (BGR, RBG) is a transposition the driver applies on the *logical* color before encoding. -- **Frame order**: the first 24/32 bits go to the *first* pixel (the one closest to the controller). Subsequent pixels read the data after a per-pixel shift register inside the previous chip — there is no addressing. - -### 1.3 Refresh-rate math - -Per-pixel time = bits/pixel × 1.25 µs. Frame time = N × per-pixel + reset. - -| N pixels (WS2812B) | Frame (µs) | Max FPS | -| ------------------ | --------------- | ------- | -| 100 | 3000 + 300 = 3300 | 303 | -| 300 | 9000 + 300 = 9300 | 107 | -| 1000 | 30000 + 300 = 30300 | 33 | -| 4096 | 122880 + 300 | 8.1 | - -Driving *K* strands in parallel from one peripheral does not multiply wall-clock time — the per-frame ceiling is set by the longest strand. The DMA *buffer* scales with K. SK6812 RGBW costs ~33% more time per frame than WS2812 at equal pixel counts because of the extra white byte. - -This refresh ceiling is a property of the LED protocol, not of any MCU. No driver, however clever, breaks 33 FPS at 1000 pixels per strand — that is *the* number that drives every parallel-strand design decision below. - -## 2. Per-platform peripheral inventory - -### 2.1 ESP32 classic (Xtensa LX6, dual-core, with WiFi/BT) - -| Peripheral | Status | -| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| RMT v1 / v2 | 8 channels, 64 symbols per memory block, no DMA. Each channel timing-independent. The historical FastLED `clockless_rmt_esp32` driver (Sam Guyer's) does manual ping-pong refill at the half-buffer interrupt — that explicit double-buffering is what makes RMT v1 *more* WiFi-resilient than v2 ([FastLED #2082](https://github.com/FastLED/FastLED/issues/2082)). | -| I2S0 in LCD mode | Up to 16 parallel chains via the LCD sub-mode of I2S0; this is what hpwit's [I2SClocklessLedDriver](https://github.com/hpwit/I2SClocklessLedDriver) uses. DMA-driven, ping-pong buffers, CPU out of the loop. The hard cost is WiFi coexistence — both peripherals contend for the SPI/DMA bus arbiter. Mitigations covered below. | -| SPI | Right peripheral for APA102 / SK9822 (clocked, two-wire). Not for WS2812 unless you tolerate 4× bit-stuffing (NeoPixelBus does this as a fallback). | -| LCD_CAM, PARLIO | Absent. | -| MCPWM, PCNT | Never used in production for LEDs. Ignore. | - -**Recommendation**: RMT (v1 API where possible) for 1–8 chains, mixed chipsets, with WiFi on. I2S parallel for 9–16 uniform-timing chains when you can tame WiFi. - -### 2.2 ESP32-S3 (Xtensa LX7, vector instructions, USB-OTG) - -| Peripheral | Status | -| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| RMT v2 | 4 TX + 4 RX channels, 48 symbols/block, **DMA optional** (`with_dma = true`). On a typical board with USB-CDC console, you have room for one DMA channel plus one non-DMA channel. | -| I2S | Still works for parallel LED output (16 lanes), but the S3's I2S no longer contains the LCD sub-module — that's been promoted to its own peripheral. The legacy I2S-parallel drivers still compile and run. | -| LCD_CAM | **Present, and this is the right peripheral for parallel LEDs on S3.** Originally a HUB75 / 8-bit-parallel-display peripheral; hpwit and Adafruit (Phil Burgess) [discovered](https://blog.adafruit.com/2022/06/14/esp32uesday-hacking-the-esp32-s3-lcd-peripheral/) it can be repurposed for 8- or 16-lane WS2812 output. It has its own GDMA channel and does not share the legacy DMA arbiter with WiFi. | -| SPI | Same role as classic ESP32 — APA102 / SK9822. | - -**Origin of the "LCD_CAM discovery" the brief refers to**: the Adafruit "ESP32uesday" posts in June 2022 ([1](https://blog.adafruit.com/2022/06/14/esp32uesday-hacking-the-esp32-s3-lcd-peripheral/), [2](https://blog.adafruit.com/2022/06/21/esp32uesday-more-s3-lcd-peripheral-hacking-with-code/)) introduced the technique for HUB75 RGB matrices. hpwit ([I2SClockLessLedDriveresp32s3](https://github.com/hpwit/I2SClockLessLedDriveresp32s3)) carried it across to addressable WS2812-class chains shortly after. FastLED's WIP `clockless_lcd_*_esp32.h` is the public-library equivalent ([#1779](https://github.com/FastLED/FastLED/issues/1779), [#2113](https://github.com/FastLED/FastLED/issues/2113)). - -**Recommendation**: LCD_CAM (I80 or RGB mode) for ≥ 4 parallel chains, especially with WiFi active. RMT v2 with DMA for ≤ 4 chains and mixed chipsets. - -### 2.3 ESP32-P4 (RISC-V, no built-in WiFi) - -| Peripheral | Status | -| ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| RMT v2 | 4 TX + 4 RX, 48 sym/block, DMA standard. Without WiFi contention, RMT5 finally behaves as advertised. | -| PARLIO TX | **The headline P4 peripheral for LEDs.** Dedicated parallel TX block, 8 or 16 bits wide, hardware bit timing, GDMA. FastLED ships an alpha PARLIO driver ([#2095](https://github.com/FastLED/FastLED/issues/2095), [#2178](https://github.com/FastLED/FastLED/issues/2178)). Auto-detection is the current sore spot; force the driver via build flag for now. | -| LCD_CAM | Also present on P4. FastLED's LCD driver targets P4 alongside S3 ([#2113](https://github.com/FastLED/FastLED/issues/2113)) but community testing is the bottleneck because P4 hardware is still scarce. | -| SPI | Adds Octal SPI (8 data lines) — useful for big APA102 banks. Not for WS2812. | - -**WiFi coexistence**: a non-issue on P4. If WiFi is needed it's via an ESP32-C6 over SDIO, fully decoupled. - -**Recommendation**: PARLIO for any parallel layout, RMT5+DMA for ≤ 4 mixed-chipset chains. - -### 2.4 Teensy 4.x (i.MX RT1062, ARM Cortex-M7, no WiFi) - -The "trivially great" platform. The relevant peripheral is **FlexIO** — a configurable shifter/timer/state-machine fabric (essentially a smaller PIO before PIO was cool), DMA-fed, runs at the i.MX bus clock. - -| Library | Use | -| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| [OctoWS2811](https://www.pjrc.com/teensy/td_libs_OctoWS2811.html) | Paul Stoffregen's reference, FlexIO + DMA. The Teensy-3 "8 channel" limit is gone on Teensy 4 — supports an arbitrary number of digital pins, zero CPU cost during refresh. | -| [TriantaduoWS2811](https://github.com/wramsdell/TriantaduoWS2811) | 32 channels from 3 Teensy pins via FlexIO clocking external 74HC595 shift registers, still zero CPU cost. Headline number: ~1.065 M LED updates/second across 32 channels. | -| WS2812Serial | UART-based, blocking-free, single pin. "Best for <600 LEDs." The fallback when you genuinely just want one strip and don't care about parallelism. | - -Teensy 4.1 has **1 MB tightly-coupled RAM** and supports up to **16 MB external PSRAM** via under-board solder pads, accessed via the `EXTMEM` attribute. PSRAM is the natural home for large frame buffers; DMA can read from it. No WiFi means no peripheral-vs-radio contention. - -**Recommendation**: OctoWS2811 for ≤ 8 strands; Triantaduo/Duotrigesimal for 16–32 parallel; WS2812Serial for one-strip toys. PSRAM via `EXTMEM` for anything past a few hundred KB of frame buffer. - -### 2.5 Raspberry Pi 5 (BCM2712 + RP1 southbridge) - -Pi 5 deliberately moved GPIO, PWM, DMA, SPI, I2C off the SoC onto the in-house **RP1** chip. That broke every WS2812 driver that worked by mapping BCM283x PWM+DMA registers directly from userspace — [jgarff/rpi_ws281x #528](https://github.com/jgarff/rpi_ws281x/issues/528) tracks the fallout. - -State of the art on Pi 5 in 2026: - -| Path | Status | -| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [rpi_ws281x Pi 5 branch](https://github.com/jgarff/rpi_ws281x/wiki/Raspberry-Pi-5-Support) (jgarff) | Experimental: custom kernel module `rp1_ws281x_pwm.ko` + device-tree overlay. Single-channel works, concurrent channels not yet. | -| [PIOLib](https://www.raspberrypi.com/news/piolib-a-userspace-library-for-pio-control/) + [Adafruit_Blinka_Raspberry_Pi5_Neopixel](https://github.com/adafruit/Adafruit_Blinka_Raspberry_Pi5_Neopixel) | **The forward-looking path.** RP1 contains PIO blocks like the RP2040; PIOLib is a userspace ioctl wrapper over an `rp1-pio` kernel driver. WS2812 is the canonical PIO demo and lives at [`utils/piolib/examples/ws2812.c`](https://github.com/raspberrypi/utils/blob/master/piolib/examples/ws2812.c). | -| [niklasr22/rpi5-ws2812](https://github.com/niklasr22/rpi5-ws2812) | Bit-bang WS2812 timing through SPI MOSI at 2.4 MHz. Single channel, no kernel module needed, well-trodden hack. | -| Bridge to an MCU | Use the Pi 5 as the brains (HTTP, preview, scenes) and hand the strand timing to an ESP32 / Teensy / Pico over USB or SPI. **Cleanest path today.** | - -**Recommendation**: in MoonLight, treat the Pi 5 as a desktop-class platform that delegates strand timing to an attached MCU. If single-strand-from-Pi-5 ever becomes a requirement, wrap Adafruit's PIO library behind a `WS2812Rp1Driver` and accept the single-strand limit. - -## 3. WiFi vs the driver — the failure that defines ESP32 driver design - -The defining failure mode on ESP32 is: it works perfectly at boot, then it flickers — wrong colors, shifted pixels — the moment WiFi associates. This is not RF interference. Measured root cause ([FastLED #2082](https://github.com/FastLED/FastLED/issues/2082)): an ISR that should fire every ~35 µs is delayed **40–50 µs under light WiFi load** — i.e. the deadline is missed outright. One missed deadline corrupts one bit, which shifts every downstream pixel of that frame, until the next reset. - -Contributing factors, ranked: - -1. **WiFi task priority on core 0.** WiFi stack runs at a priority above user tasks and cannot be preempted. If the LED render task is also on core 0, it gets starved. -2. **Flash-cache stalls.** Any ISR or hot function not marked `IRAM_ATTR` stalls during cache-disabled windows (NVS writes, OTA, WiFi calibration). The interrupt must also be registered with `ESP_INTR_FLAG_IRAM`. -3. **Dynamic frequency scaling.** If the CPU drops from 160 MHz to 40 MHz, the APB divider feeding RMT/I2S can't keep up. Force `pm_config.min_freq_mhz = 80`. -4. **RF coupling** — the *inverse* problem, where I2S parallel clock lines next to the WiFi front-end degrade WiFi RX. Rare, board-layout-dependent. -5. **DMA bus contention** — rarely the dominant factor. The AHB matrix arbitrates per-peripheral. - -**Mitigations checklist**, in priority order — these are what every production driver does: - -- Pin the LED render task to **core 1**, leave WiFi tasks on core 0 (`xTaskCreatePinnedToCore(..., 1)`). -- `WiFi.setSleep(false)` — modem sleep introduces periodic ~100 ms blackouts. -- Mark every ISR and hot function `IRAM_ATTR`; register interrupts with `ESP_INTR_FLAG_IRAM`; constants in `DRAM_ATTR`. -- Raise interrupt priority (level 3 via `esp_intr_alloc`). -- Set `pm_config.min_freq_mhz` ≥ 80. -- Prefer DMA-fed peripherals (RMT-with-DMA, I2S, LCD_CAM, PARLIO) so the CPU is out of the timing loop. - -Diagnostic signature for WiFi-induced corruption (vs power or signal-integrity faults): first frames after boot look perfect; symptoms appear only after WiFi associates; pixels are shifted/stuck *from a position outward* (one bit slipped, everything downstream inherits it); color flashes correlate with traffic bursts. Compare against power-fault signature: brownout, whole-strip dim, first-pixel corruption. - -For MoonLight, this means: **the LED driver task lives on core 1** (the quiet core, away from the WiFi stack and its interrupts) and **the effects / network path lives on core 0** (it can tolerate latency spikes — a late effect frame is invisible, a late driver bit is a corrupted pixel). This is the inverse of the WLED render-on-core-1 pattern, applied to a different observation about which task has the harder deadline; see § 7.2 for the full rationale and the MoonLight-specific per-module core-affinity story. - -## 4. Generic driver architecture - -A driver does five things, in order: - -1. **Configure** peripheral timing for a chipset variant (WS2812B vs SK6812 RGBW vs APA102) at setup. -2. **Correct + encode** the logical `Buffer` into a peripheral-native bit pattern. The source buffer is linear RGB; the driver applies the shared **output correction** (brightness, channel reorder, RGBW white — see § 4.6) per light, then encodes the corrected, wire-ordered bytes into the bit pattern. Correction and encode fuse into one pass. -3. **Start** the DMA / peripheral, point it at the encoded buffer. -4. **Wait** for completion (or signal completion via callback / semaphore). -5. **Enforce** the inter-frame reset gap (≥ 300 µs). - -That's it. The five steps are the same on every MCU; what changes is *who* does step 2 (CPU vs DMA encoder) and what the peripheral in step 3 looks like. - -### 4.1 The interface - -```cpp -// src/light/drivers/LedDriverBase.h -namespace mm { - -struct LedFrame { - std::span bytes; // logical RGB (3 ch); driver applies Correction (brightness, - // reorder to GRB/…, RGBW white) per light during encode - uint16_t lightCount; // pixels (not bytes) - uint8_t channelsPerLight; // source channels (3 = RGB today); output channels come - // from the Correction's fixture-profile (3 or 4) -}; - -struct LedDriverConfig { - uint32_t t0h_ns = 350; - uint32_t t1h_ns = 700; - uint32_t period_ns = 1250; - uint32_t reset_us = 300; - uint8_t invert = 0; // for level-shifters that swap polarity -}; - -class LedDriverBase { -public: - virtual ~LedDriverBase() = default; - virtual bool begin(const LedDriverConfig&) = 0; - virtual void show(const LedFrame&) = 0; // synchronous: returns after DMA done + reset - virtual void end() = 0; -}; - -} -``` - -`show()` is synchronous because the per-frame budget is small enough that there is nothing useful to do on the same core while a frame is in flight. The caller decides whether to call `show()` from the render task (simple) or from a dedicated task pinned to a different core than the network stack (the WiFi mitigation pattern). The driver itself doesn't pin tasks — that's a wiring decision in `main.cpp`. - -### 4.2 Per-platform implementations - -``` -src/platform/esp32/ Esp32RmtWs2812Driver // 1-8 chains, mixed chipsets, WiFi-safe -src/platform/esp32/ Esp32I2sParallelDriver // 9-16 chains, uniform timing -src/platform/esp32s3/ Esp32S3LcdCamDriver // 8/16 chains, DMA, post-WiFi-clean -src/platform/esp32p4/ Esp32P4ParlioDriver // 8/16 chains, hardware-clocked, no WiFi -src/platform/teensy/ TeensyFlexIoDriver // OctoWS2811-equivalent on FlexIO -src/platform/rpi5/ Rpi5PioDriver // PIOLib via Adafruit Pi5 NeoPixel -src/platform/desktop/ DesktopNullDriver // writes frames to a file/socket for preview -``` - -Each platform header is gated by `if constexpr (platform::isEsp32)` etc. at the call site — no `#ifdef` outside `src/platform/`. The selection logic lives in one `main.cpp` factory that returns a `std::unique_ptr` chosen by `platform_config.h` flags. - -### 4.3 The encoding step (step 2 above) - -For RMT, the encoded form is an array of `(level, duration)` symbols — one per bit, with the duration set from the chipset's T0H/T0L/T1H/T1L. RMT v2 also supports a *callback encoder* that produces symbols on-the-fly, but for a fixed chipset the symbol cost is small (~2 KB for 1000 WS2812 pixels with one strand) and pre-encoding is simpler. - -For I2S parallel / LCD_CAM / PARLIO, the encoded form is a *transposed* frame: the buffer is bit-sliced so that one DMA "tick" emits one bit-of-the-same-pixel across all K lanes. Concretely, for K=16 lanes: - -``` -DMA word at sample N = ((lane0_bitN) << 0) | ((lane1_bitN) << 1) | ... | ((lane15_bitN) << 15) -``` - -Each WS2812 bit becomes 3 sub-bits at 2.4 MHz (high → high-or-low depending on 1/0 → low), producing the per-bit pulse pattern. This is the same encoding hpwit's I2S driver and `jgarff/rpi_ws281x` use. - -The transpose costs O(N×24) per frame on the CPU. On ESP32 classic that's ~50 µs for 1000 pixels — well inside the frame budget. On S3/P4 it's cheaper (vector instructions on LX7 / hardware barrel shifter on RISC-V). Doing the transpose on the *render side* (writing logical pixels into a pre-transposed shape) is faster but couples the buffer layout to the driver — keep it in the driver until profiling proves otherwise. - -### 4.4 What goes in shared code vs platform code - -Shared, in `src/light/`: - -- `LedDriverBase` interface. -- `Buffer` (already exists), `MappingLUT` (already exists). -- Chipset descriptors (WS2812B, SK6812 RGB, SK6812 RGBW, APA102) — pure data: GRB order, channels, default timing. -- Output correction — already exists: `Correction` (`src/light/drivers/Correction.h`), owned by the `Drivers` container, applies brightness / channel reorder / RGBW white per light. The LED driver applies it in step 2 (§ 4.6), the same `const Correction*` ArtNet already uses. Gamma / dither fold into the same LUT later. - -Platform-specific, in `src/platform//`: - -- Peripheral setup (clock, DMA channel, pins, interrupt registration). -- Encoder for that peripheral's bit format. -- ISR / DMA callback. - -### 4.5 Shift-register expanders - -Putting 74HC595 (or similar) on the data lines is **not** a new driver layer — it's a hardware multiplexer that lets one peripheral-controlled clock + data drive N strands. The driver still emits the same bit pattern; the shift register fans it out in space rather than time. The Teensy TriantaduoWS2811 design is the canonical example: 3 Teensy pins (clock, data, latch) drive 32 strands via external '595s. - -In the architecture above, an expander variant is a configuration of an existing driver (e.g. `TeensyFlexIoDriver` with `expanderChains = 32`), not a new class. The transpose step gets a wider stride; everything else is unchanged. - -For MoonLight this is **third-priority** behind WS2812 and SK6812: design the interface so it could host it (the K-lane abstraction is already general enough), but don't ship it day one. - -### 4.6 Output correction (already shipped) - -The brightness / channel-reorder / RGBW-white stage is **not** specific to LED drivers and is **already implemented** — ArtNet uses it today. It is a per-driver stage shared by every *physical* driver, with the shared state owned by the `Drivers` container: - -- `Correction` (`src/light/drivers/Correction.h`) holds a 256-entry brightness LUT, a channel-order table (the `fixture`: RGB / GRB / … / RGBW / GRBW), an output-channel count (3 or 4), and a derive-white flag. `Correction::apply(src, out)` transforms one logical RGB light into the wire form: brightness via LUT, then reorder, then (for RGBW presets) `W = min(r,g,b)` of the brightness-scaled channels. -- `Drivers` exposes the shared `brightness`, and each driver exposes its own `fixture`; a change to either rebuilds only the affected corrections (`Drivers::rebuildAllCorrections`, `DriverBase::rebuildCorrection`) without a pipeline rebuild, so the slider stays fluent. Each driver owns its `Correction`; Preview ignores it (shows the raw logical buffer). -- An LED driver consumes the *same* `const Correction*`. In the fused single-pass case (§ 4.3, identity / shuffle, no blend) it calls `correction_->apply(...)` per light and encodes the result straight into the DMA buffer — F/G/H and the WS2812 encode are one pass, never a second sweep over encoded bytes. - -So the LED driver does **not** re-invent brightness/reorder/white — it reuses the shipped `Correction`. Gamma and white-balance fold into the same LUT later as a per-channel R/G/B split (the field is `briLut`, not `gammaLut`, so that's a fill change, not a rename). See [MoonLight, drivers](../../explanation/architecture/moonlight.md#drivers) for the cross-driver picture. - -## 5. Testing architecture - -Goal: prove the driver produced the right bits at the right times, without trusting the code that produced them. The methods, ranked by what to actually do: - -### 5.1 Software unit tests of the encoding pipeline (table stakes) - -In `test/test_led_encoder.cpp`: feed a known logical `Buffer` into the encoder, assert the resulting bit pattern (GRB order, MSB first, T0H/T1H pulse widths in *symbol counts*). This catches color-order bugs, gamma bugs, off-by-one strip length, RGBW-vs-RGB confusion. Runs in CI on every push, zero hardware. - -### 5.2 On-board GPIO loopback (the cheap CI workhorse — pick this) - -The ESP32 RMT peripheral is a *transceiver*: the RX side captures pulse durations into 15-bit `(level, duration)` symbols at up to 12.5 ns resolution. Wire `GPIO_TX → GPIO_RX` with a jumper, run the driver, decode the RX symbol stream back into bytes. - -- Cost: $0 (a jumper). -- Resolution: 12.5 ns native, 100 ns is plenty for WS2812. -- Coverage: bit-level + per-edge timing margin. -- Headless / CI: runs entirely on-device, host harness reads pass/fail over USB. -- Setup: write the RX-symbol → byte decoder. ESP-IDF ships an [RMTLoopback example](https://github.com/espressif/arduino-esp32/blob/master/libraries/ESP32/examples/RMT/RMTLoopback/RMTLoopback.ino) to fork. - -On Teensy 4, FlexPWM dual-input capture gives 6.7 ns resolution; Paul Stoffregen's [WS2812Capture](https://github.com/PaulStoffregen/WS2812Capture) library is purpose-built for exactly this — it captures pulse widths via DMA and decodes bytes per byte. Use it directly. - -This is the **primary CI signal-verification method** for the project. It runs on the device under test, on every push, with zero added hardware. - -### 5.3 fx2lafw + sigrok-cli (the $12 independent cross-check) - -A $10–15 fx2lafw clone gives 24 MS/s on 8 channels, sigrok-compatible. The project's reference device is the **NanoDLA v1.3** (CY7C68013A-based, 24 MHz / 8 channels, fx2lafw firmware), but any board the sigrok `fx2lafw` driver enumerates works the same way. The libsigrokdecode tree has a WS2812 protocol decoder ([logic-ws2812](https://github.com/dustin/logic-ws2812)) that emits RGB triples; [sigrok-cli](https://sigrok.org/wiki/Sigrok-cli) runs it headless: - -```bash -sigrok-cli -d fx2lafw -c samplerate=24m --samples 100000 \ - -P ws2812:data=D0 -A ws2812=rgb -``` - -Why bother when 5.2 exists: the loopback method trusts the *MCU's own clock*. An independent analyzer with its own clock crosses the verification — if loopback and analyzer disagree, you have a real bug. 24 MS/s = 41 ns/sample, enough to *correctness*-check WS2812 (350 vs 700 ns) but marginal for *timing-margin* tests (320 vs 380 ns). - -#### 5.3.1 Wiring the NanoDLA v1.3 to an ESP32 and capturing one frame - -**Wiring** (any fx2lafw clone follows the same pattern; only the labels differ): - -- **Common ground.** Tie the analyzer's `GND` pin to an ESP32 `GND` pin. Without this the analyzer reads garbage — every other concern is downstream of this one. Use the shortest jumper you have. -- **Signal.** Tie analyzer channel `D0` (NanoDLA labels them `CH0..CH7`) to the ESP32 GPIO that drives WS2812 data. For the on-board loopback rig from §5.2 that's the `GPIO_TX` side of the loopback jumper — keep the loopback in place and tap the same line with a second jumper into `D0`. The analyzer is high-impedance; it doesn't load the line. -- **Do NOT power the ESP32 from the analyzer's 5 V / 3.3 V rail.** The CY7C68013A's regulator can't carry an ESP32 under WiFi load. Power the ESP32 over its own USB cable; the analyzer's USB is data-only as far as the ESP32 is concerned. -- **Strand power.** If a real WS2812 strand is on the line too, give it its own 5 V supply with the supply ground also tied to the common ground above. The analyzer taps the signal; it does not drive the strand. - -**Voltage levels.** ESP32 GPIO is 3.3 V CMOS. fx2lafw boards specify 3.3 V logic (5 V tolerant inputs). 3.3 V → 3.3 V is a direct connection — no level shifter, no series resistor. Skip every "is it safe?" worry; this is the well-understood case. - -**Sample-rate vs strand length.** At 24 MS/s, 8 channels (the NanoDLA's max), the host-side buffer fills at ~24 MB/s. A 30-LED strand is one full frame ≈ 0.9 ms wire-time + reset — well inside one sigrok-cli `--samples` capture window. For larger strands either bump `--samples` proportionally or trigger on the first edge so you only capture the active wire-time. - -**Capture + decode (single frame, one strand):** - -```bash -# 1. Plug in: NanoDLA via USB, ESP32 via its own USB cable, jumpers as above. -# 2. Confirm sigrok sees the device. Should print one fx2lafw entry. -sigrok-cli --scan - -# 3. Capture ~4 ms at 24 MS/s — enough for any reasonable WS2812 frame -# plus the 50–280 µs idle that latches it. -sigrok-cli -d fx2lafw -c samplerate=24m --samples 100000 \ - -P ws2812:data=D0 -A ws2812=rgb - -# 4. The output is one line per pixel, with RGB triples in transmit -# order. Compare against the byte stream the firmware claims to have -# sent: that's the cross-check the doc above describes. -``` - -**Common gotchas, in the order you'll hit them:** - -1. **No common ground** — capture looks like random toggling. Re-seat the GND jumper. -2. **Wrong channel** — `-P ws2812:data=D0` vs the channel you actually wired. Re-check the NanoDLA label; CH0 = D0 in the sigrok driver. -3. **Sample rate too low** — at 8 MS/s a 350 ns "0" pulse becomes 3 samples and the decoder will start mistaking 0s for 1s. Always pass `samplerate=24m` explicitly; the driver's default is lower. -4. **PulseView for debugging.** When `sigrok-cli` says "no frames decoded," open the same capture in [PulseView](https://sigrok.org/wiki/PulseView) and look at the raw waveform — usually it's an idle-low gap missing or a stuck bit, both obvious by eye. - -### 5.4 DSLogic Plus ($149) — the "later" upgrade - -[DSLogic Plus](https://www.dreamsourcelab.com/shop/logic-analyzer/dslogic-plus/): 16 channels @ 100 MS/s (10 ns/sample). sigrok-compatible — the CI scripts from 5.3 reuse without change. 16 channels means you can capture multiple parallel strands at once. The right purchase when a timing-margin bug appears that 24 MS/s can't see. - -### 5.5 Saleae, oscilloscope, camera, photodiode - -Skip for CI: - -- **Saleae Logic 8 / Pro 8** — gold standard, but the [Logic 2 Automation gRPC API](https://saleae.github.io/logic2-automation/) is the only feature that justifies the price over DSLogic for a CI rig, and that only at high test volume. -- **Oscilloscope** (Rigol DS1054Z and similar) — useful for development, fragile for CI (screen-scraping or vendor-specific scripting). -- **Webcam + OpenCV** — end-to-end ("a real LED lit up"), but rolling shutter, exposure noise, and refresh-vs-frame-rate beat make it low-SNR. -- **Photodiode array** — niche; interesting only when a *driver → LED* (vs driver-output) bug is suspected. - -### 5.6 Recommended layered test suite - -| Layer | Method | Cost | What it catches | When | -| ----- | ------ | ---- | --------------- | ---- | -| Unit | Encoder tests, host-side | $0 | Encoding bugs, color order, RGBW, off-by-one | Every push, CI | -| Module | Loopback (RMT-RX / FlexPWM-capture) | $0 | Wrong bits emitted, wrong timing on real silicon | Every device-test push | -| Scenario | NanoDLA v1.3 (or any fx2lafw clone) + sigrok-cli | $12 | Cross-check vs MCU clock; reset gap; bus traffic | Per release | -| Margin | DSLogic Plus + sigrok-cli | $149 | Sub-50 ns timing drift, jitter under WiFi load | When a specific failure motivates it | - -Layers 1–2 ship day one. Layer 3 ships when the first non-trivial driver lands. Layer 4 is reactive — buy it when you have a bug that 24 MS/s can't see. - -## 6. Hello-world plan - -The goal of hello-world is: end-to-end, one solid-color frame on a real WS2812 strip, with the test rig confirming it was correct, on each platform in turn. - -### 6.1 ESP32 classic — first - -1. Promote `src/platform/esp32/Esp32RmtWs2812Driver.{h,cpp}` (RMT v1 path; one channel; one strand of 100 WS2812B; pin 4). -2. Encoder: `LedFrame.bytes` → `rmt_item32_t[]` with `T0H/T0L/T1H/T1L` from `LedDriverConfig`. -3. `show()`: configure RMT TX, point at item buffer, start, wait for done, sleep ≥ 300 µs for reset. -4. Wiring in `main.cpp`: pick `Esp32RmtWs2812Driver` via `if constexpr (platform::isEsp32 && !platform::isEsp32S3 && !platform::isEsp32P4)`. -5. Pin the render task to core 1, WiFi/HTTP to core 0. -6. Verification: jumper GPIO 4 → GPIO 5, run RMT-RX decoder, assert decoded byte stream matches what was sent. Plus fx2lafw on the wire as cross-check. - -This is the first commit. Everything that follows builds on this. - -### 6.2 ESP32-S3 — second - -Add `Esp32S3LcdCamDriver` with 8 parallel chains via LCD_CAM (I80 mode). Reuse the encoder logic; the difference is the transposed bit-slice and the GDMA setup. Verification: same loopback (RMT-RX still exists on S3) plus fx2lafw on lane 0. - -### 6.3 ESP32-P4 — third - -Add `Esp32P4ParlioDriver`. Sane API surface (Espressif provides PARLIO TX driver in ESP-IDF), no WiFi to defeat. The encoded form is the same transposed bit-slice as LCD_CAM, but PARLIO is a cleaner peripheral and the GDMA story is mature. Watch for [#2178](https://github.com/FastLED/FastLED/issues/2178) — auto-detection regression in FastLED; we're not using FastLED, so we don't inherit it, but it's a marker that the IDF API has been moving. - -### 6.4 Teensy 4.1 — fourth - -Add `TeensyFlexIoDriver`. Easiest of the lot because OctoWS2811 is mature and well-documented. Either link OctoWS2811 directly or re-implement the FlexIO setup; given that the project is C++20 / CMake and OctoWS2811 is Arduino-flavoured, a thin re-implementation is probably cleaner. Verification: `WS2812Capture` library handles loopback directly. - -### 6.5 Raspberry Pi 5 — last, and probably as a bridge - -Treat the Pi 5 as a desktop platform. Two paths: - -- **Bridge** (recommended day one): connect a Pico or ESP32 over USB-serial; the Pi 5 sends frames; the MCU drives strands. Re-uses any of the drivers above. The Pi 5 implementation is just a serial protocol on top of `DesktopNullDriver`. -- **Direct** (only if needed): wrap Adafruit's PIO library behind `Rpi5PioDriver`, accept single-strand limit. - -### 6.6 Acceptance bar for each step - -Each platform reaches hello-world when: - -1. A 100-pixel solid-color test frame renders on a real strip. -2. The on-board loopback test (5.2) passes on real hardware. -3. The fx2lafw cross-check (5.3) passes — same bytes, same reset gap. -4. The fps/jitter KPI line (per [collect_kpi.py](../../../moondeck/check/collect_kpi.py)) is captured and stored. -5. (ESP32 only) the test still passes with WiFi associated and a packet flood running. - -## 7. Product-owner decisions - -These were the open questions raised by §1–§6; each is now either settled or explicitly deferred with the shape it will take when revisited. - -### 7.1 Network protocol — settled - -Live LED data over **DDP / ArtNet / E1.31 / WebSocket**. No other protocols in scope for now. - -### 7.2 Core affinity — settled, with a generalisation - -Default direction is **inverted from §3 of this doc**: the LED driver task runs on **core 1**, the effects / network task runs on **core 0**. The reasoning is the same as §3 (WiFi-stack-and-radio interrupts land on core 0), but applied to a different observation about which side suffers more from disruption: - -- If the **driver task is interrupted**, the LED signal corrupts → visible flicker. The driver must be on the quiet core. -- If the **effects task is interrupted**, the next frame is a few ms late → invisible at 30+ FPS. Effects can tolerate core 0. - -That is the inverse of what §3 of this doc proposes (which followed the WLED pattern of network-on-core-0, render-on-core-1 — same conclusion, applied to the *render* loop rather than the *driver* loop). In MoonLight the driver and the render-of-effects are separate tasks, so the choice is about which one sits on the quiet core. The driver wins because its deadline is per-bit, not per-frame. - -This is also how MoonLight (v2 → MoonLight's predecessor) is wired today: it works, the precedent matters. - -**Generalisation**: a single fixed default is ESP-classic-and-S3 sensible. P4 has no built-in WiFi so the choice is moot there. **Because MoonLight already supports per-module core affinity** (the wider plan is to expose this as a control), the driver and effects task affinities become MoonModule-level controls with the defaults above. Other devices (Teensy, Pi 5) ignore the control. - -Practical wiring rule for hello-world: in `main.cpp`, pin the LED driver task to core 1 and the effects/render task to core 0 on classic ESP32 + S3; leave it unset on P4 / Teensy / Pi 5. Make the values overridable via the existing core-affinity control. - -**Single-purpose-device assumption**: in practice a device runs *either* live LEDs (network-driven art frames) *or* GPIO LEDs (effects engine driving local strands), not both heavy duty at once. The architecture must let either side own the quiet core; for hello-world we pick GPIO-LED-driver-on-core-1 as the default because the LED-driver-task path is what carries hard-realtime requirements regardless of which side is generating the frames. - -### 7.3 Color depth at the wire — flexible-from-day-one, default 8-bit - -No single answer; the design must be flexible enough to add the other paths later. The architecture supports three modes, with **default 8-bit** for the first commit: - -| Mode | Layer buffers | BlendMap | Driver input | Wire | Notes | -| ---- | ------------- | -------- | ------------ | ---- | ----- | -| 8-bit (default) | 8-bit | 8-bit | 8-bit | 8-bit | Simplest, smallest RAM, WS2812 native. The starting point. | -| 8-bit + driver-side dither | 8-bit | 8-bit | 8-bit | 8-bit with temporal dither applied in the driver | Cheaper than full 16-bit; gradient quality between modes 1 and 3. WLED-style. | -| 16-bit pipeline (incl. dither) | 16-bit | 16-bit | 16-bit | 8-bit (driver downsamples + dithers) | Doubles RAM; best gradient quality; required for 16-bit-native LEDs (UCS7604, HD108) when those are added. | - -Per-driver configurable; each driver instance declares which mode it operates in via a bound control. The shared upstream pipeline (effects, BlendMap) reads the driver's declared input width and produces buffers accordingly — i.e. switching the driver to 16-bit mode rebuilds the Layer buffers as 16-bit, not just the driver's encoding step. - -For the first commit: 8-bit only. Mode 2 and Mode 3 are stubbed in the interface (e.g. `LedDriverBase::inputBitsPerChannel()` returning 8, with the override hook for later modes) but not implemented. This is the "stub code now, full implementation later" pattern from §7.5. - -### 7.4 Frame buffering policy / DMA buffers — auto-from-heap, with override - -Both single-buffer (write-then-show) and double/N-buffer (render-while-show) need to work; the framework decides per driver at setup based on available DMA-capable RAM. Concretely: - -- The driver declares its per-buffer DMA size at setup (computed from strip count × pixels × encoding overhead). -- At `begin()`, the driver allocates `N` buffers where `N = clamp(min_for_target_fps, max(2, available_dma_ram / per_buffer_size), platform_ceiling)`. -- The auto-computed `N` is **exposed as a read-only diagnostic field on the UI**, and **can be overridden** by a bound control (default = `auto`, otherwise = explicit integer). - -Per-platform expected ranges (the numbers you cited from MoonLight observation): - -- Classic ESP32, no PSRAM: ~7 buffers. -- ESP32-S3, no PSRAM: ~30 buffers. -- ESP32-S3 + PSRAM: ~75 buffers. - -These are the *anti-flicker* values — fewer buffers and the refill ISR can't tolerate a WiFi-induced latency spike. The auto-derive logic uses these as upper bounds; the override is the safety valve when the auto-derive is wrong for a given install. - -### 7.5 Shift-register expander — yes, with stubs now, full implementation later - -Confirmed in scope. The architecture must accommodate `pinCount × stripsPerPin` topology from day one. Concretely: - -- `LedDriverBase` exposes `maxStrips()` / `maxLightsPerStrip()` (per the bottom-up doc's API sketch). -- A `ShiftRegMultiplex` configuration object is part of `LedDriverConfig` from the first commit (defaulting to `none()`), even when no driver actually implements it yet. -- Each parallel-clocked driver (`I2sLcdLedDriver`, `Esp32S3LcdCamDriver`, `Esp32P4ParlioDriver`, `TeensyFlexIoDriver`) has stub code that *rejects* a non-`none()` multiplex config with a clear "not implemented yet" return — not a crash, not silent acceptance. -- Full implementation of the multiplex layer is its own later plan, scheduled after the first non-multiplex driver lands and passes the testing layers (§5). - -This is the pattern the bottom-up doc calls "Backend × Multiplex" — confirmed as the architectural target. - -### 7.6 Pi 5 — accept "Pi 5 + attached MCU", defer firmware-shape decision - -Confirmed: MoonLight does **not** commit to driving WS2812 directly from the Pi 5's own GPIO pins. Bridge to an MCU. - -The shape of the firmware that runs on the bridged MCU is **deferred** until Pi 5 work actually starts. Two paths are on the table, both viable, both with trade-offs worth weighing at decision time rather than now: - -#### Path A — Stripped MoonLight on the bridge MCU - -The bridge MCU runs a thin MoonLight build: no UI, no effects engine, no full network listener — just the `Drivers` + `LedDriver` pipeline plus a USB-serial input parser that pushes incoming RGB byte frames into the driver. Estimate: 50–150 KB build target. - -Pros: -- Code reuse with the main MoonLight tree. Driver fixes flow into the bridge automatically. -- A bridge MCU is a regular MoonLight build with three modules disabled — no second codebase. -- The bridge can locally expose status/heartbeat/diagnostics via the existing MoonModule control surface. - -Cons: -- Larger image. Slower to boot. More to flash. -- Couples the bridge to MoonLight's release cadence and IDF version pin — bridge upgrades become MoonLight upgrades. - -#### Path B — Dedicated minimal firmware - -Standalone firmware, no MoonLight code reuse: `read N RGB bytes from USB serial → push to a vendored LedDriver implementation → repeat`. Estimate: under 30 KB. - -Pros: -- Tiny, fast to boot, easy to flash from any laptop. -- Independent release cadence; the bridge stays stable while MoonLight iterates. -- No build-system overhead — single-file possibility. - -Cons: -- Driver code diverges from MoonLight's tree over time unless we vendor + sync deliberately. -- No diagnostic surface beyond what the protocol carries. -- Two codebases to maintain in the longer run. - -**Decision deferred** until the Pi 5 bridge becomes a real task. When it does, the deciding factor is likely: how often will the bridge's driver be touched? If rarely, Path B. If it tracks MoonLight's main-line driver work, Path A. - -For *this* analysis it's enough to note: the `LedDriver` interface designed in §4 is the same in both paths — both reuse it. The choice between A and B is a *build target* decision, not an *interface* decision. - -### 7.7 Carry-over from this doc's TL;DR - -These were not in the original "open questions" list but are decisions that need to stay visible: - -- **Hello-world platform order remains ESP32 classic → S3 → P4 → Teensy → Pi 5 bridge.** -- **CI signal-verification stack remains layered**: software unit tests, RMT-RX (or equivalent) loopback, fx2lafw cross-check, DSLogic Plus when timing-margin testing becomes necessary. - -## 8. Reconciliation with the bottom-up analysis - -Read [leddriver-analysis-bottom-up.md](leddriver-analysis-bottom-up.md) at the end, after this analysis was complete. Below are the points of strong agreement, where the two analyses diverge, and where the bottom-up document is meaningfully deeper than this one. - -### 8.1 Strong agreement - -- **Peripherals per chip.** Same list, same rankings. Classic ESP32 → RMT + I2S-LCD; S3 → LCD_CAM + GDMA; P4 → PARLIO + LCD_CAM; Teensy → FlexIO; Pi 5 → RP1 PIO. Both analyses identify the same "what to drive WS2812 from" answer per platform. -- **WiFi-glitch root cause and fix.** Both converge on the same triple: ISR latency / cache stalls / CPU starvation, mitigated by core pinning + DMA-only data path + IRAM discipline. The bottom-up doc states "the real reason I2S/LCD-CAM drivers don't flicker is that the CPU isn't in the timing-critical path at all" — exactly the same finding as this doc's WiFi section. -- **Pi 5 strategy.** Both reach the same conclusion: bridge to an MCU; PIO-via-RP1 exists but is research-grade today. The bottom-up doc adds the SPI MOSI bit-bang option ([rpi5-ws2812](https://github.com/niklasr22/rpi5-ws2812)) which works through RP1 because SPI is still exposed there. -- **Shift-register expanders as a value-add, not a separate backend.** The bottom-up doc names this explicitly as the "Multiplex axis" — a modifier on top of any parallel-clocked backend, not a sibling of LCD_CAM. This doc reached the same shape ("an expander variant is a configuration of an existing driver, not a new class") but the bottom-up doc has the cleaner vocabulary. -- **Hot path discipline.** Both arrive at the same do/don't list: no allocations, `IRAM_ATTR`, integer math, DMA-from-SRAM-not-PSRAM, no `printf` in `push()`. -- **WS2812 protocol fundamentals.** Same 800 kHz, same per-pixel time math, same ≥ 300 µs reset. - -### 8.2 Genuine divergences - -- **Build-vs-borrow recommendation.** The bottom-up doc recommends **Scenario B — build everything ourselves**, with five reasons in priority order (multiplex axis, runtime driver switching, identity-mapping fast path, IDF gives plumbing, license/binary-size tax). **This top-down doc does not make a build-vs-borrow recommendation.** It implicitly assumes we write our own drivers on top of ESP-IDF / NXP HALs, never even considering FastLED-as-dependency, because the top-down approach starts from "what is the simplest direct path from buffer to wire" and library overhead drops out. - Reconciled: the bottom-up doc's recommendation stands. This top-down doc is consistent with Scenario B — same direction, more concrete implementation detail, less analysis of the choice itself. - -- **Recommended Stage-2 spike order.** The bottom-up doc starts with **LCD_CAM on S3** (highest-value single backend). This doc proposes starting with **RMT on classic ESP32** (smallest, cheapest, most-likely-to-work first contract). Reconciled: starting with classic-ESP32-RMT is the right "hello world" — the goal there is to prove the `LedDriver` interface and the loopback test harness end-to-end on the simplest peripheral. Once that exists, the LCD_CAM-on-S3 spike from the bottom-up doc becomes the *second* commit and the contract is already known to fit. - -- **Buffering policy.** This doc originally treated double-buffering as an open question. The bottom-up doc had already done the math (§ "Performance budget") and concluded: double-buffer is required at any non-trivial light count because DMA transmission dominates the frame budget. **Now resolved** in § 7.4 — buffer count is auto-derived from heap budget with explicit override, expected ranges 7 (classic ESP32) / 30 (S3 no PSRAM) / 75 (S3 + PSRAM). Both docs land in the same place. - -- **Identity-mapping fast path.** The bottom-up doc treats this as a load-bearing architectural constraint (see `Drivers.h:90`, "drivers point at `layer_->buffer()` directly, no copy"). This top-down doc mentions zero-copy in passing but doesn't carry it as a constraint. Reconciled: it *is* a constraint. The `LedDriverBase::show(LedFrame&)` interface above already supports it — `LedFrame::bytes` is a `std::span` — but the wiring in `main.cpp` has to be explicit about handing the Layer's buffer pointer to the driver in the identity case rather than copying through `outputBuffer_`. - -### 8.3 Where the bottom-up doc goes meaningfully deeper - -- **API surface for the driver.** The bottom-up doc's `LedDriver` interface in § "Concrete DriverBase API sketch" (§ 477-549) is fully specified: `push(span)` + `onTopologyChange(strips)` + `backendName()` + `maxStrips()` / `maxLightsPerStrip()` for UI greying. This is more concrete than my `LedDriverBase::begin/show/end` and should be the starting point for the actual header. -- **Performance budget table.** The 16K × 50 FPS budget breakdown (§ 559-578) is the kind of math this top-down doc gestures at but does not actually do. It shows `push()` has 3-5 ms; DMA transmission runs 16-20 ms in the background; effects are 8-12 ms; the frame budget is 20 ms total. That table should be carried forward into the actual implementation plan. -- **Library landscape characterisation.** WLED, WLED-MM, NightDriverStrip, FastLED master vs release, NeoPixelBus, ESP-IDF `led_strip` — the bottom-up doc has read each one at a specific HEAD commit and characterised the abstractions, license, maintenance, and gotchas. Even if we walk Scenario B, that landscape map is the documentation we point at when someone asks "why didn't you just use WLED?" -- **Risks and unknowns.** The eight-item risk list (§ 601-612) names specific failure modes — RMT5 API churn, parlio newness, shift-register driver portability, LCD overclock non-portability, hot-reconfigure-during-DMA, WiFi-coexistence empirical variability, identity-mapping path subtlety, build-vs-borrow lock-in. These are real and we will hit them. This top-down doc's § 7 "Open questions" is shallower. -- **Hpwit's source read.** The bottom-up doc has actually read `I2SClocklessVirtualLedDriver.h` (3820 lines, HEAD 2026-05-25) and located the specific entanglements (30 `#ifdef CONFIG_IDF_TARGET_ESP32S3` blocks, duplicated `loadAndTranspose` under different `I2S_MAPPING_MODE` paths, multiplex math intertwined with DMA buffer layout). This is the kind of detail that only comes from reading the source, and it changes the cost estimate for the multiplex-as-reusable-layer claim. Both docs accept the same conclusion ("multiplex code needs per-backend specialisation, not free composition"), but the bottom-up doc has the receipts. - -### 8.4 Net effect on the next plan - -The two analyses are compatible — neither contradicts the other on any load-bearing decision. The right next plan reads the top-down doc for the architectural shape, the protocol math, the platform-by-platform peripheral choice, and the testing strategy; and reads the bottom-up doc for the build-vs-borrow recommendation, the concrete `LedDriver` interface, the performance budget, and the library landscape map. Hello-world is RMT-on-classic-ESP32 → LCD_CAM-on-S3 → PARLIO-on-P4 → FlexIO-on-Teensy → Pi-5-via-bridge. Both docs agree. - diff --git a/docs/work/future/livescripts-analysis-bottom-up.md b/docs/work/future/livescripts-analysis-bottom-up.md deleted file mode 100644 index 9c60bb1f5..000000000 --- a/docs/work/future/livescripts-analysis-bottom-up.md +++ /dev/null @@ -1,237 +0,0 @@ -# MoonLive — live-script engine landscape analysis - -> **Forward-looking research document — exception to CLAUDE.md present-tense rule.** This is a Stage-1 bottom-up survey of *live scripting* for MoonLight: running user-authored scripts (LED effects, layouts, modifiers, drivers, sensor logic) on a running device without a recompile-and-flash cycle. It deep-reads one reference implementation — the [ewowi/ESPLiveScript `fix-warnings` fork](https://github.com/ewowi/ESPLiveScript/tree/fix-warnings) of [hpwit/ESPLiveScript](https://github.com/hpwit/ESPLiveScript) — at HEAD on **2026-06-25**, surveys the comparable field (WLED ARTI-FX, embedded VMs, WASM), and extracts the architectural primitives a clean MoonLight redesign must decide. Companion to the monthly digest [friend-repos/hpwit-ESPLiveScript.md](../../friend-repos/hpwit-ESPLiveScript.md) (credits + activity log). The **top-down** redesign document ([livescripts-analysis-top-down.md](livescripts-analysis-top-down.md)) expands the decisions recorded here into the build spec. Source citations use `file:line` against the cloned fork; inferred claims are marked *(inferred)*. Modelled on [leddriver-analysis-bottom-up.md](../../work/future/leddriver-analysis-bottom-up.md). - -## TL;DR - -- **What live scripting is, and why MoonLight wants it.** The same itch that produced WLED ARTI-FX and ESPLiveScript: author an effect (or layout, modifier, driver, sensor rule) *as text*, push it to a running device, see it run **on the next tick** — no toolchain, no flash, no reboot. It turns MoonLight from "the effects we compiled in" into "any effect a user can write," and it's the natural home for a web-based pattern editor. -- **The design space has three corners, set by *how* a script becomes execution.** (1) **AST-walking interpreter** — parse to a tree, walk it every frame (WLED **ARTI-FX**: everything stored as `double`, flexible, slow). (2) **Bytecode VM** — compile to a compact opcode stream, run a dispatch loop (**PixelBlaze**, most embedded Lua/JS). (3) **Native JIT** — emit real machine code, call it as a function (**ESPLiveScript**). Speed climbs (1)→(3); portability and simplicity fall (1)→(3). MoonLight's "blazingly fast like ESPLiveScript" requirement points at (3), but (3) is exactly where portability dies — see below. -- **ESPLiveScript is a from-scratch C-like compiler that JIT-emits native Xtensa machine code** (confirmed, not bytecode): `tokenizer.h` → `asm_parser.h` (AST of `NodeToken`) → visitor methods emit Xtensa assembly *strings* → `asm_parser_LMbin.h` encodes them to 32-bit opcodes → the binary is copied to executable RAM (`heap_caps_malloc(..., MALLOC_CAP_EXEC)`) and **called as a function pointer** via inline `callx8` (`execute_asm.h:386-399`). That direct-call, no-dispatch-loop design is the entire "85 fps C++ vs 10 fps Gravity vs 3 fps Lua" speed story from its README. -- **The portability finding that reshapes everything: ESPLiveScript is Xtensa-only.** The agent deep-read confirmed **no RISC-V backend** — all codegen is Tensilica Xtensa LX (`asm_parser_LMbin.h`, the inline-asm executor). This matters enormously for MoonLight's target order: classic ESP32 + S3 are Xtensa (✅ ESPLiveScript runs), but **ESP32-P4 is RISC-V** (❌), as are Teensy 4.x (ARM Cortex-M7) and desktop (x86-64 / ARM64). So ESPLiveScript as-is covers exactly the *first two* targets on MoonLight's list and **none** of the rest. A native-codegen engine needs **one backend per ISA** (Xtensa, RISC-V, ARM Thumb-2, x86-64, ARM64) — that's the real cost of "blazingly fast everywhere." -- **The front-end is portable; the back-end is not.** Tokenizer + parser + AST (`NodeToken`) are CPU-agnostic; only the *visitor → opcode* tier and the *load-and-execute* tier are ISA-bound. But today they're **deeply interleaved** — visitor methods emit Xtensa strings inline, there is **no intermediate representation (IR)** between AST and machine code. A clean redesign's load-bearing decision is whether to introduce that IR seam so one front-end feeds many back-ends (the LLVM shape, scaled down). -- **The "compatible with MoonModule" requirement is the MoonLight-specific value-add.** ESPLiveScript binds to the host via `addExternalFunction(name, ret, sig, fnptr)` / `addExternalVariable(name, type, _, ptr)` (`asm_external.h`) — a flat C-pointer registry. MoonLight needs scripts to read/write **controls**, consume the **producer/consumer data structures** (a `Buffer`, an `AudioFrame`), and slot into the **module tree** as a scripted effect/layout/modifier/driver/peripheral. That binding layer — script ⇄ MoonModule — is ours to design; no surveyed engine has it. -- **Memory + sync are already partly modelled in ESPLiveScript** and align with MoonLight's constraints: compiled code lands in IRAM/PSRAM by target (`execute.h:10-15` gates PSRAM stack on S3/P4), a **save/load compiled-binary path** exists (`savebinary`/`executebinary` examples → compile once, ship the binary, skip re-compile on boot), and a `sync()` primitive coordinates concurrent script tasks. These are the right *ideas*; the redesign carries them forward against our `platform::` seam and `Scheduler`. -- **⚠️ Superseded upstream (noted 2026-08-06): hpwit has rewritten it as [ESPLiveScript2](https://github.com/hpwit/new-parser).** A from-scratch C++ reimplementation whose stated goal is precisely the gap this analysis identified below — a compiler you can *verify*: the whole toolchain builds and runs as a host program, and its tests execute the actual compiled Xtensa bytes under QEMU against v1's own example corpus. The rewrite landed in the first days of August 2026 (the repo was dormant May 2025 → August 2026), so this document's reading of v1 stands as written but is no longer a reading of hpwit's *current* work. **Before Stage 2 acts on any v1 conclusion, re-read v2** — the portability finding in particular (is codegen still Xtensa-only, or did the rewrite introduce the IR seam we concluded was missing?). Digest: [friend-repos/hpwit-new-parser.md](../../friend-repos/hpwit-new-parser.md). -- **Code-quality reality (for the redesign).** Header-only, ~18K lines across 11 headers, **pervasive global state** (`string signature; Token __t;` and dozens of file-scope compiler counters), no IR, no unit tests, a 4,100-line `Parser` and a 5,824-line `NodeToken`. It works and it's fast, but it is **not** a base to extend in place — it's the reference to learn from and rewrite against our architecture (exactly the *Industry standards, our own code* method we used for LED drivers). -- **Recommendation: build our own native engine, Xtensa-first, behind an IR seam — start small, start beautiful, no dead-ends.** Take the ESPLiveScript *approach* (native machine-code execution, near-100% speed — the standout, never-done-before-in-this-space when bound to a module system) and add the one thing our multi-target goal needs that a single-ISA engine doesn't: put an **IR seam** between a platform-independent front-end (tokenizer→parser→AST) and the code generator. **Ship one backend first — Xtensa (classic ESP32 + S3)** — exactly where ESPLiveScript already proves native speed; that's the small, beautiful, blazingly-fast first deliverable. The IR seam is the **no-dead-end guarantee**: adding RISC-V (P4), ARM (Teensy), or x86/ARM64 (desktop) later is "write another backend behind the same IR," never "go back to the drawing board." ESPLiveScript's real dead-end isn't *Xtensa-first* — it's *Xtensa-welded-in, no IR*; we start at the same fast place but with the seam it lacks. **WASM/WAMR is the named fallback, per target**: a target without a native backend yet can run the portable path through the same IR, so we're never blocked — but the *flagship* experience is native. (Detail + why-this-over-WASM-wholesale in § Recommendation.) -- **Safety the same way — climb the tiers, don't pay upfront.** A user-facing script editor means a bad script must degrade, not brick. Start with the **cheap** safety (array **bounds-checking** = a compare-branch per indexed access, low single-digit %, and removable in a trusted/fast mode; **watchdog / instruction budget** to kill a runaway loop = near-free, the task WDT already does most of it) — these catch the common bad-script cases at low cost (the kind the `fix-warnings` null-deref was). The **expensive** tier — a true memory sandbox where a script *cannot* touch memory outside its arena — is exactly what WASM gives for free and native can't cheaply; leave it as a tier we *can* climb via the IR→WASM fallback if field experience demands it, not a wall we hit. So safety is staged, not a foregone full-sandbox cost. -- **Ruled out (with reasons, so the top-down doesn't relitigate).** **FastLED's WASM** is browser *preview* (Emscripten-compiled FastLED in a Web Worker), not on-device scripting — adjacent, not it. **MicroPython/Python** is the right *edit* ergonomics but far too slow for the hot path (the Lua/Gravity wall that birthed ESPLiveScript). **Rust/TinyGo** are near-native but **AOT-compiled-and-flashed** — they remove no edit loop. **Adopting WASM/WAMR *wholesale* as the only engine** is the considered alternative, not a dead end — it wins portability + free sandbox but tops out at ~50% native (WAMR-AOT) and carries a 200KB+ runtime; we keep it as the per-target fallback rather than the flagship, because *native speed is the differentiator we're chasing*. -- **Out of scope for Stage 1.** Final VM-vs-JIT decision; IR design; the web editor; benchmarking on real hardware; the language grammar spec. All Stage 2 (top-down). - -## Why this document exists - -MoonLight compiles its effects, layouts, modifiers, and drivers into the firmware. Adding one means writing C++, building, and flashing. **Live scripting removes that loop**: a user writes an effect as text in a browser, the device compiles/loads it, and it runs as a first-class module on the next tick — the same leap WLED took with ARTI-FX. - -The product owner's requirements (verbatim intent): - -- **General in core, specific in light.** Scripts must work for domain-neutral core jobs (e.g. read/transform sensor data) *and* the light domain: a scripted **layout** (coordinate iterator), **effect** (writes the buffer), **modifier** (remaps positions), **driver** (consumes the buffer). One engine, many module roles. -- **Target order.** ESP32 classic + S3 **first**; then P4 and other ESP32 flavours; then Teensy; then macOS / Linux / Windows. -- **Blazingly fast, like ESPLiveScript** — near-native per-pixel throughput, because a script runs in the render hot path at up to 16K+ lights × 50 FPS. -- **Smart memory management** — IRAM/PSRAM-aware, no hot-path allocation, compile-once/run-many. -- **Infinitely scalable** — run *as many* live scripts concurrently as memory allows, exploiting PSRAM (each script is an independent compiled unit; the only ceiling is free heap, not a fixed slot count). Many small scripted modules — several effects across layers, a scripted modifier, a couple of core sensor rules — coexist; the device hosts what fits and degrades gracefully when it doesn't, the same way the light pipeline already scales to available memory. -- **Sync with the rest of the system** — live reconfig (no reboot), `Scheduler`-driven, robust to add/delete/replace in any order, controls and producer/consumer data wired in. -- **Compatible with the MoonModule class** — a scripted module is a MoonModule: it has controls, a `loop()`, a role, and renders generically in the UI with zero per-script UI code. - -This document characterises the one reference that already hits "blazingly fast" (ESPLiveScript), maps it against the field, and surfaces every decision the redesign must make. It does **not** pick the design — that's the top-down doc. - -## ESP32 — primary depth: ESPLiveScript (the reference) - -Read at `ewowi/ESPLiveScript@fix-warnings`, cloned 2026-06-25. ~18,358 lines across 11 header-only files in `src/`. - -### What it is, in one sentence - -A hand-written **C-like language with a from-scratch tokenizer → parser → AST → native-Xtensa code generator**, where the compiled script is loaded into executable RAM and **called directly as a function** — a JIT compiler, not an interpreter. Yves Bazin (hpwit) built it because Lua (3 fps) and Gravity (10 fps) couldn't drive his 12,288-LED panel where hand-written C++ hit 85 fps (README intro). - -### The pipeline (the load-bearing structure) - -Source text flows through five stages; the data structure between them is `Token` → `NodeToken` (the AST node) → assembly text → binary: - -1. **Tokenize** — `src/tokenizer.h` (2,394 lines). Lexes source into `Token`s with a `tokenType` enum. Also owns the user-defined-type registry (`_userDefinedTypes`, global). -2. **Parse → AST** — `src/asm_parser.h` (1,929 lines) builds a tree of `NodeToken` (`src/NodeToken.h`, 5,824 lines). `NodeToken`'s `nodeType` enum has ~47 kinds (`binOpNode`, `defFunctionNode`, `forNode`, `ifNode`, `callFunctionNode`, `returnNode`, `defAsmFunctionNode`, …). A for-loop becomes a `forNode` with init/cond/incr/body children. -3. **Generate code (visitor)** — `NodeToken::visitNode()` (`NodeToken.h:818-1001`) dispatches to `_visitbinOpNode()`, `_visitcallFunctionNode()`, etc., each of which **emits Xtensa assembly strings** into output buffers (e.g. `NodeToken.h:1858` emits `movi a%d,%d`). There is **no IR** — visitors know Xtensa directly. -4. **Encode to binary** — `src/asm_parser_LMbin.h` (592 lines) turns assembly text into 32-bit Xtensa opcodes (`bin_add`, `bin_l32i`, `bin_movi`; e.g. `bin_add` at `:97-100` emits `0x800000 | …`). -5. **Load + execute** — `src/execute_asm.h` (876 lines). `_createExcutablefromBinary()` (`:224-275`) copies the binary into executable RAM with `heap_caps_malloc(size, MALLOC_CAP_EXEC)` (`:232`), patches external references (relocation, `:44-223`), and `executeBinaryAsm()` (`:386-399`) runs it via inline asm: `l32i a15,%0,0 ; callx8 a15` — **a direct indirect call to the generated code.** No dispatch loop. That is the speed. - -### Why it's blazingly fast (confirmed) - -It is **native machine code called as a function** — the CPU fetches the script's own instructions from IRAM, exactly like a compiled C function. There is no per-opcode interpreter overhead (the cost a bytecode VM or AST-walker pays every operation). The README's benchmark (85 fps C++ ≈ ESPLiveScript ≫ 10 fps Gravity ≫ 3 fps Lua) is the direct consequence. Scripts can even drop to **inline Xtensa** for the hottest paths (`__ASM__ uint32_t millis() { "entry a1,32" … "retw.n" }`, `sc_examples/animwle.sc`). - -### What ties it to ESP32 — the portability barrier - -The agent's deep-read is unambiguous: **Xtensa-only, ESP-IDF-coupled.** - -- **Codegen is 100% Xtensa LX** (`asm_parser_LMbin.h`, all visitor emission). **No RISC-V, no ARM, no x86 backend exists.** Only ~1 arch `#ifdef` in the codegen — and it's a *memory-caps* choice (S3/P4 PSRAM stack vs classic internal, `execute.h:10-15`), not a second ISA. (Note the irony: that one `#ifdef` *mentions* ESP32-P4, but only for stack allocation — P4 is RISC-V, so the **generated code wouldn't run on it.** *(inferred from "Xtensa-only codegen" + P4 being RISC-V)*.) -- **Execution is ESP-IDF-specific**: `MALLOC_CAP_EXEC` IRAM allocation, inline `callx8`, `rsr a14,234` cycle-counter reads, `xTaskCreatePinnedToCoreWithCaps` (`execute.h:590`). -- **Front-end is portable, back-end is not**: tokenizer + parser + AST are CPU-agnostic; tiers 2-4 (visit→opcode, encode, load-execute) are ISA/platform-bound and **interleaved** with no seam between them. - -So on MoonLight's target list, ESPLiveScript as-is runs on **classic ESP32 + S3** and stops there. P4 (RISC-V), Teensy (ARM), and desktop (x86-64/ARM64) each need a *new code generator* — 2-3K lines per ISA *(inferred, agent estimate)* — or a different execution strategy entirely. - -### Host integration — the binding model - -A host C program drives it through `class Parser` (`ESPLiveScript.h:79`): - -- **Compile + run**: `parseScript(&str)` → `Executable`; `Executable::execute("fn", args)` runs a function; `executeAsTask("fn", core, args)` runs it pinned to a FreeRTOS core; `suspend()/restart()/kill()/free()` manage its lifetime (`execute.h:352+`). -- **Expose a C function to scripts**: `addExternalFunction("calc","float","int",(void*)calcul)` → script calls `float h = calc(52);` (`asm_external.h`, README example). -- **Expose a C variable**: `addExternalVariable("value","int","",(void*)&v)` and `("array","int *","",(void*)arr)` → script does `value = value + 2; array[i] = 10;`. -- **Arguments**: `int` and `float` only (`ESPLivescriptRuntime.h:150-176`). -- **JSON path** (`__JSON__OPTION__`): scripts exchange JSON with the host (`execute_asm.h:400-471`) — the `enjoy json` feature from the digest; the bridge a web editor would lean on. -- **Precompiled binaries**: `parseScriptBinary()` → `saveBinary()/loadBinary()` → `createExecutableFromBinary()` (`execute_asm.h:276-384`). Compile once (on a desktop or web service), persist the `ESPLiveScript1.0.1`-format binary, load it on the device — but **external pointers are not serialized**, they re-bind at load (README). This is the seed of a smart compile-once memory strategy. - -This binding is a **flat C-pointer registry** — exactly what MoonLight must *replace* with a MoonModule-aware layer (controls, producer/consumer structs, the module tree). - -### Memory model - -- Generated code → executable RAM via `MALLOC_CAP_EXEC` (IRAM on classic; PSRAM-capable on S3/P4 via `__LS_STACK_CAPS`, `execute.h:10-15`). -- Script globals → a malloc'd `data` buffer; locals → the Xtensa ABI stack frame (`entry a1, size`); params → registers a2-a7. -- Precompiled-binary persistence (above) = compile-once. -- `Executable::free()` releases both code and data. -- No PSRAM is *forced* for the data section; that's a knob the redesign would make policy. - -### The language (what a user writes) - -C-like, LED-oriented. From `sc_examples/*.sc` + README: - -- **Types**: `int`/`s_int` (32/16-bit), `uint8_t..uint32_t`, `float`, `char`, `bool`, plus **`CRGB`/`CRGBW`** (LED color) as first-class; user `struct`s with fields, methods, constructors; multi-dimensional arrays (`int g[z][y][x]`). -- **Control flow**: `if/else`, ternary, `while`, C-style `for`, `break`/`continue`, `return`; recursion (`sc_examples/fibonacci.sc`). -- **Built-ins**: `printf`/`printfln` (int only), `millis()`, `rand`/`copy`/`memset`/`fill` (inline-asm in `functionlib.h`), `hsv()` and FastLED math when `USE_FASTLED` is set. -- **Escape hatch**: `__ASM__ … @` blocks for hand-written Xtensa. -- **Preprocessor**: `#define TOKEN value` (substitution only). -- **Flavour example** — `sc_examples/animwle.sc` is a **Mandelbrot effect ported from an existing pattern** (`#define width 128`, float `cR/cI`, nested grid loops, inline-asm `millis()`), which tells you the target audience: people porting effects from other LED-scripting environments. - -### What the `fix-warnings` fork changed - -Despite the branch name, the fork's substantive change is **one commit (`4871509`, 2026-04-02): a null-pointer crash fix**, not a `-Wall` cleanup. `findMaxArgumentSize()` in `NodeToken.h` dereferenced `getChildAtPos(1)`/`(2)` unconditionally; a **nested external-function argument** (`setRGB(random16(NUM_LEDS), CRGB(0,0,255))`) produces a node without those children → `LoadProhibited` crash on device. The fix adds null guards (return 0 — a scalar needs no pre-call stack spill). *Relevance to MoonLight*: this is precisely the class of bug a from-scratch hand-written parser breeds (no test harness caught it), and a data point for "rewrite with tests" over "extend in place." - -### Structural observations (what a multi-target rewrite must account for — not a verdict on the original) - -These are the differences between ESPLiveScript's design (one author, one ISA, maximum speed) and what MoonLight's *different* goals (multi-target, module-bound, tested) need. They are reasons to write our own against our architecture, not faults — ESPLiveScript meets its own goals well. - -- **Header-only**, ~18K lines, 11 files; the two biggest (`NodeToken.h` 5,824, `ESPLiveScript.h` 4,100) carry several jobs each (tree + metadata + 47 visitors + asm emission). Fine for a single-include library; we'd split for testability. -- **File-scope state**: `string signature; Token __t;` (`ESPLiveScript.h:29-30`), plus global register-allocation stacks, output buffers, compiler counters. A consequence: one compilation at a time. Acceptable on a device that compiles one script; we'd encapsulate it. -- **No IR** between AST and Xtensa — the one structural thing that makes multi-target hard, and the single highest-leverage change our redesign makes. (ESPLiveScript didn't need it — it targets one ISA.) -- **`.ino` integration examples, no unit suite** — natural for an Arduino library; we'd add unit + scenario tests because robustness is pinned by tests here. - -The lesson is the LED-driver lesson: **study it hard, credit it (see § Prior art & credits), write our own against our architecture and goals.** - -## The comparable field (what else to learn from) - -MoonLight's "industry standards" rule says: name the prior art, take the textbook approach. The live-scripting field has three established design points; ESPLiveScript is the extreme of one. - -### WLED ARTI-FX — the AST-walking interpreter (our sibling project) - -[ARTI-FX](https://mm.kno.wled.ge/moonmodules/arti-fx/) (MoonModules, by ewowi — the author of this analysis — built on the **ARTI** runtime, a PEG-grammar-driven interpreter) parses a script and **walks the AST every frame**. Every value is stored as a **`double`**, converted to int when needed; scripts define `renderFrame` (per-frame) + `renderLed` (per-LED) callbacks and call `setPixelColor`/`setRange`/`fill`. Host binding is `arti_external_function` / `arti_set/get_external_variable` (the same flat-registry shape as ESPLiveScript). **What it contributes:** it proved live scripting works end-to-end in this ecosystem, and its design is maximally flexible and portable — pure C++ tree-walking, runs anywhere unchanged. The `double`-everything per-LED walk trades per-frame speed for that flexibility, which is the trade MoonLight's 16K hot path can't take (and exactly the gap ESPLiveScript's native path closes). So the two are complementary baselines: ARTI-FX is the **reach** baseline (runs everywhere, the product shape proven), ESPLiveScript is the **speed** baseline (native, Xtensa) — and MoonLight wants both, which is why neither alone is the answer. - -### WASM on ESP32 — the strongest off-the-shelf portable-runtime candidate (answering "is WASM what we want?") - -WebAssembly is a portable **bytecode standard** with mature small runtimes that **already run on the ESP32**, which makes it the most serious "don't build the engine, adopt one" option for the multi-target problem ESPLiveScript can't solve. Two runtimes matter: - -- **wasm3** — an ultra-light **interpreter** in C (~64 KB code, ~10 KB RAM), runs Arduino-class MCUs upward. Pure interpretation, so *slower* (the per-opcode dispatch cost, same class as a bytecode VM) — fine for control logic, questionable for a 16K-pixel inner loop. -- **WAMR (WebAssembly Micro Runtime)** — supports interpreter **and AOT/JIT**; in **AOT mode WAMR reaches ~50% of native speed**, "quite acceptable" for embedded use, at a larger footprint. Rule of thumb from the field: **RAM < 256 KB → wasm3, > 256 KB → WAMR**; the classic ESP32's 320 KB+ puts it in WAMR's range. ([arXiv survey](https://arxiv.org/html/2512.00035v1), [WAMR-ESP32](https://registry.platformio.org/libraries/mlaass/WAMR-ESP32)) - -**Why this is genuinely relevant to MoonLight**, and arguably *the* answer to "runs everywhere": - -- **One artifact, every target.** A script compiled to `.wasm` runs on classic/S3/P4/Teensy/desktop through the same runtime — no per-ISA backend, which is exactly ESPLiveScript's missing piece. WASM **is** the portable IR + VM, off the shelf. -- **WAMR-AOT is the "blazingly fast" bridge.** AOT-compiling the `.wasm` to native on the device (or on a host) gets ~50% of native — between a naive bytecode VM and ESPLiveScript's near-100%. That's the same "portable baseline + native acceleration" shape the bottom-up proposes, but **already built and multi-ISA** (WAMR's AOT backends cover Xtensa, RISC-V, ARM, x86). -- **Sandboxed by design.** WASM is memory-safe and bounds-checked — a runaway/bad script traps instead of bricking the device (the safety story ESPLiveScript's native code lacks; recall the `fix-warnings` null-deref). -- **Mature toolchain + editor path.** Any language that targets WASM (C/C++, Rust, AssemblyScript ≈ TypeScript) becomes a script source; the browser already runs WASM natively, so a web editor could compile *and preview* the exact artifact the device runs. - -**The costs / open questions** (the top-down must weigh): the runtime is **heavier** than a hand-rolled VM (WAMR is 200 KB+; wasm3 is light but interpreter-only); the **host-binding** for WASM (imports/exports, linear-memory marshalling of a `Buffer`/`AudioFrame`) is more ceremony than ESPLiveScript's flat pointer registry and must be designed against the MoonModule data model; the **toolchain** (a WASM compiler in the editor path) is a real dependency; and **whether WAMR-AOT actually holds 16K×50FPS** on an S3 is the load-bearing benchmark. But as a way to get *every target on day one with sandboxing for free*, WASM is the candidate to beat — the top-down should evaluate "WAMR as the engine" head-to-head against "our own VM+IR." - -### FastLED's WASM — adjacent but **not** what we want - -FastLED's `master` WASM support (`src/platforms/wasm/`) compiles **FastLED itself to WebAssembly via Emscripten to run in a *browser*** — a **simulation/preview** of effects, not a runtime that runs user scripts *on the ESP32*. It runs FastLED in a Web Worker (`PROXY_TO_PTHREAD`), bridges C++↔JS via `EMSCRIPTEN_KEEPALIVE` exports (`js_bindings.cpp`), and exports frame/strip/UI data as JSON for the page to draw. FastLED's `FxEngine` is likewise a *compiled* effect manager (switch/transition between C++ effects), not an on-device scripting language. So FastLED gives MoonLight **two adjacent ideas, neither the live-script engine**: (1) "compile your effect library to WASM to **preview it in the browser**" — a preview technique that sits next to MoonLight's own 3D WebGL preview, not the scripting engine; (2) precedent that the WASM toolchain is production-ready. It does **not** answer the on-device live-scripting need. ([FastLED wasm platform](https://github.com/FastLED/FastLED/tree/master/src/platforms/wasm)) - -### Compiled languages (Rust / TinyGo) and interpreted Python — why neither is the answer (answering the Rust/Python question) - -- **Python / MicroPython is too slow for the hot path** — the benchmark literature is consistent: MicroPython is *"many times slower"* than C/Rust/TinyGo on ESP32 ([MDPI study](https://www.mdpi.com/2079-9292/12/1/143)). It's the same wall hpwit hit (Lua 3 fps, Gravity 10 fps) that *caused* ESPLiveScript. So "Python is an interpreter, so live-editing is easy" is true for the *edit loop* but fails the *speed* requirement at 16K×50FPS. Its real value is the **REPL/editor UX** reference, not the engine. (For non-hot-path *core* scripts — a slow sensor rule at 1 Hz — an interpreter's speed is irrelevant and Python-class ergonomics would be fine; this argues again for a **tiered** answer: cheap interpreter acceptable off the hot path, fast path needs VM/native.) -- **Rust / TinyGo are fast but *compiled*, not interpreted** — Rust-on-ESP32 (`esp-hal`, `no_std`) and TinyGo land near C speed, but they are **AOT-compiled and flashed** — they need a toolchain and a reflash, which is *exactly the loop live scripting exists to remove*. They give no live-edit story on their own. (Rust *does* become relevant via WASM: Rust → `.wasm` → WAMR is a legitimate script-authoring path, but then the engine is WASM, not Rust-on-device.) -- **Net:** interpreted-Python solves edit-speed but not run-speed; compiled-Rust solves run-speed but not edit-speed. The only options that give **both** live edit *and* hot-path speed are (a) a custom VM/native engine (ESPLiveScript's path, our redesign) or (b) **WASM+WAMR-AOT** (portable, sandboxed, ~50% native). Those two are the real finalists. - -### Other embedded VMs / JIT libraries (textbook back-ends) - -- **Lua / eLua / Luau** — the canonical embeddable scripting VM (register-based bytecode); the reference for a clean host C API and a GC'd value model. Too slow per-pixel raw (hpwit measured 3 fps), but the *architecture* (compile → bytecode → register VM) is the textbook. -- **Espruino** — JS on MCUs; full-language, GC-paused; an editor/REPL UX reference, the "too much" end for a per-pixel hot path. -- **LLVM / MIR / GNU lightning** — real JIT libraries with multi-ISA back-ends. LLVM is far too big for an MCU; **MIR** and **GNU lightning** are lightweight JITs that *do* target multiple ISAs and are the closest prior art to "one front-end, many native back-ends" if we go the custom-native-multi-target route (the alternative to adopting WASM). - -### The design-space map - -| Approach | Example | Speed (per-pixel) | Portability | Runtime size | Sandbox | Editor-friendliness | -|---|---|---|---|---|---|---| -| AST-walk interpreter | WLED **ARTI-FX** | Low (double-everything) | **Highest** (pure C++) | Small | Easy | High | -| Bytecode VM | Lua, AssemblyScript | Medium-High | **High** (one VM, any CPU) | Small-Medium | Easy | High | -| **WASM interpreter** | **wasm3** | Medium | **Highest** (standard) | ~64 KB | **Built-in** | High (any→wasm) | -| **WASM AOT/JIT** | **WAMR** | **~50% native** | **Highest** (standard, multi-ISA) | 200 KB+ | **Built-in** | High (any→wasm) | -| Native JIT (custom) | **ESPLiveScript** | **Highest** (native) | **Lowest** (one backend/ISA) | Medium | None (can crash) | Medium | - -MoonLight wants ESPLiveScript's **speed** *and* ARTI-FX's **reach** — no single *custom* corner gives both, which is why one redesign path is a **layered custom** engine (portable VM baseline + optional native back-end behind a shared IR). But **WASM+WAMR-AOT collapses that table into one row**: portable to every target *and* ~50% native *and* sandboxed, off the shelf. The two real finalists for the top-down are therefore **(A) build our own VM+IR (+ optional native backend)** vs **(B) adopt WASM/WAMR as the engine** — weighed on hot-path speed (does WAMR-AOT hold 16K×50FPS?), runtime footprint, and how cleanly each binds to the MoonModule data model. - -## Architectural primitives observed (the decisions the redesign must make) - -Distilled across all four references, these are the load-bearing choices a clean engine faces — the *questions* the survey raises. Each is **decided and designed in the top-down**; listed here so the survey names what's at stake. - -1. **Execution strategy** — AST-walk vs bytecode-VM vs native-JIT (or a tier ladder). *The* decision; everything follows. ([top-down §2](livescripts-analysis-top-down.md#2-why-native-and-why-our-own-expanding-decision-1)) -2. **The IR seam** — ESPLiveScript emits Xtensa directly (right for one ISA); a multi-target redesign adds a representation between AST and execution so one front-end feeds many backends. The highest-leverage structural change. ([§3.2](livescripts-analysis-top-down.md#32-the-three-tiers-where-the-ir-seam-lives)) -3. **Host-binding model** — all four references use a flat name→pointer registry (`addExternalFunction`/`arti_*`); MoonLight's value-add is a MoonModule binding (controls, producer/consumer structs, module role) with no prior art to copy. (§3.4) -4. **Per-frame contract** — script writes its own `loop()` vs an engine-called `renderLed()`; determines the hot-loop shape and where per-pixel dispatch lands. (§3.4) -5. **Compile-once / persist** — a saved artifact skips device-side recompile; portable (one artifact) with a VM/WASM, per-ISA with native. (§3.7) -6. **Memory placement** — code IRAM/PSRAM, data internal/PSRAM, per-target; routed through `platform::` as one policy. (§3.7) -7. **Concurrency + sync** — in-tick vs a pinned task; the threading contract against the scheduler + no-blocking-hot-path rule. (§3.8) -8. **Live reconfig + robustness** — a re-pushed script swaps in tick-atomically, old freed, no reboot, no mid-render crash. (§3.6) -9. **Safety / sandboxing** — native can crash, a VM can bound; a user-facing editor raises the stakes. Coupled to the execution-strategy choice. (§4) - -## Mapping to MoonLight's requirements - -| Requirement | ESPLiveScript today | What the redesign must add | -|---|---|---| -| Blazingly fast | ✅ native Xtensa | Keep native speed *where the ISA has a backend*; VM elsewhere | -| Core (sensor data) + light (layout/effect/modifier/driver) | ⚠️ generic funcs only; no module roles | The MoonModule binding + per-role entry-point contracts | -| ESP32 classic + S3 first | ✅ (both Xtensa) | Carry forward (native backend) | -| P4 + other ESP32 | ❌ P4 is RISC-V | RISC-V backend *or* VM fallback | -| Teensy | ❌ ARM | ARM backend *or* VM | -| macOS/Linux/Windows | ❌ x86/ARM + no IRAM | Desktop backend *or* VM (VM is the obvious win here) | -| Smart memory | ⚠️ one `#ifdef`, IRAM/PSRAM | Route through `platform::alloc`; compile-once artifact | -| Infinitely scalable (N scripts) | ❌ examples run one script | Independent `MoonLive` per module; code+data arenas PSRAM-first; ceiling = free heap, not a fixed count | -| Sync with system | ⚠️ FreeRTOS tasks + `sync()` | `Scheduler` tick contract; tick-atomic hot-swap; live reconfig | -| MoonModule-compatible | ❌ flat C registry | Scripted module = MoonModule (controls, loop, role, generic UI) | - -The pattern is clear: **ESPLiveScript nails speed on two chips and nothing else on this list.** Every other requirement is new work, and the multi-target + MoonModule-binding pieces are the bulk of it. - -## Recommendation — native engine, Xtensa-first, behind an IR seam - -The survey lands on a clear direction (mirroring the LED-driver bottom-up's "walk Scenario B" call, not an open fork): **build our own native-codegen engine, ship the Xtensa backend first, put an IR seam between the front-end and the code generator from day one — start small, start beautiful, extend with no dead-ends.** Native speed (near-100%) is the differentiator; the IR seam is the no-dead-end guarantee that makes "Xtensa-first" safe (RISC-V/ARM/desktop are each a new backend behind the unchanged IR); WASM/WAMR is the per-target fallback, never the rival; safety is staged (cheap bounds+watchdog first, true sandbox deferred to the WASM fallback). - -The full reasoning — why native over WASM-wholesale, why Xtensa-first isn't a corner, the cost accepted, the load-bearing spike — **is expanded in the top-down** ([§2 Why native](livescripts-analysis-top-down.md#2-why-native-and-why-our-own-expanding-decision-1), §9 Staged plan), and the decisions it rests on are recorded verbatim in § Answers — product-owner direction below. This section is the survey's conclusion; the build spec is the top-down. - -## Prior art & credits - -Per *Industry standards, our own code*: study the prior art, credit it by name, write our own. This redesign rests on work others did first. - -- **ESPLiveScript — Yves Bazin (hpwit).** The native-codegen approach the recommendation builds on is his. A from-scratch C-like compiler — tokenizer, parser, register allocator, Xtensa code generator, save/load compiled-binary path, header-only — that runs a script at near hand-written-C++ speed on an ESP32 (85 fps on a 12,288-LED panel where Lua managed 3 and Gravity 10). That is the result that makes "go native, not interpreted" the right call, and the reason this document exists. The structural notes below (global state, no IR, large files) are what a *rewrite toward a different goal* — multi-target, module-bound — has to account for; they are not a verdict on the original, which does its job and does it fast. We carry the idea forward and add the IR seam + the MoonModule binding. -- **ARTI-FX / ARTI — ewowi.** The prior MoonLight-family answer to the same problem, written by this analysis's author: the interpreted-effects runtime in WLED MoonModules, on the PEG-grammar ARTI interpreter. It proved the live-scripting idea works end-to-end in this ecosystem (load a script, run it live), and it is the source of lessons carried straight into this design — the `renderFrame`/`renderLed` split, the host-binding shape, and, by being the AST-walking design, the clearest demonstration of *why* a 16K hot path wants native or VM execution over tree-walking. The redesign trades its interpreter for native speed; it keeps its product shape and its lessons. -- **MoonLight, the predecessor** (the [effects tutorial](https://ewowi.github.io/MoonLight/moonlight/effects-tutorial/), the staging spine and the host-binding surface model). See the per-engine sections above for what each contributes. - -Activity + credits also in the digest [friend-repos/hpwit-ESPLiveScript.md](../../friend-repos/hpwit-ESPLiveScript.md). - -## Risks and unknowns - -The open questions the survey surfaced are **resolved in the top-down**, each in its own section: the load-bearing speed unknown — can native-with-safety hold 16K×50FPS, else fall back to WASM — is the first spike ([top-down §9.2](livescripts-analysis-top-down.md#92-the-ladder)); IR design ([§3.2](livescripts-analysis-top-down.md#32-the-three-tiers-where-the-ir-seam-lives)); the per-pixel-vs-per-frame contract (§3.4); the MoonModule-binding mechanics ([§3.3–3.4](livescripts-analysis-top-down.md#33-a-scripted-module-is-a-moonmodule-decision-7)); safety depth (§4); editor + persistence ([§9.2](livescripts-analysis-top-down.md#92-the-ladder), stage 10). The single load-bearing one to flag here: **whether native codegen holds the frame budget on a real S3** — if it doesn't, the IR seam makes the WASM fallback a backend swap, not a restart. - -## Answers — product-owner direction (2026-06-25) - -Decisions from the design discussion that produced this survey. These are *direction*, terse on purpose; the top-down expands each into full reasoning, an API/architecture, and a staged plan. (Mirrors the LED-driver bottom-up's product-owner-direction section.) - -1. **Execution = native, the standout.** Build our own **native-codegen** engine (ESPLiveScript-class speed, near-100%) — the differentiator; MoonLight should stand out with something not done before (a native live-compiler bound to a real module system). *Not* a slow interpreter, *not* WASM-as-flagship. -2. **No dead-ends, start small + beautiful (the LED-driver method).** Ship **one ISA backend first — Xtensa (classic ESP32 + S3)** — as a complete, blazing first increment, then grow. The **IR seam** (front-end → typed IR → per-ISA backend) is the no-dead-end guarantee: RISC-V (P4), ARM (Teensy), x86/ARM64 (desktop) each become a *new backend behind the unchanged IR*, never a rewrite. WASM/WAMR is the **per-target fallback** so no target is ever blocked. -3. **The IR must NOT cost speed (hard constraint).** It is a *compile-time* representation that lowers to the *same* native instructions ESPLiveScript hand-emits — **zero per-pixel runtime overhead**, no interpreted layer. Equally fast as hpwit on Xtensa is non-negotiable; prove it by diffing generated instructions for a hot loop against hand-written Xtensa, and keep an `__asm__` escape hatch for the very hottest paths (as ESPLiveScript has). -4. **Source language = a C-subset, "as close as possible" to the precompiled effect, with pragmatic simplifications.** The effect *body* (types like `uint8_t`/`uint32_t`/`lengthType`, nested `for`, integer + 64-bit math, `static_cast`, `RGB`, `hsvToRgb`, buffer writes) ports **near-verbatim** from a file like `RipplesEffect.h` (our reference effect — it exercises the hard cases: `float` trig `std::sqrt`/`std::sin`/`std::floor`, `std::memset`, 3D with `depth()`, two controls). The C++ *file/class ceremony* that buys nothing in a script (`#pragma`/`#include`/`namespace`, and — accepted as a pragmatic simplification — `class : public EffectBase`/`override`/the `controls_.addControl` host-object dance) is **dropped or lightened**: the engine supplies the module scaffolding around the script. Target: porting an existing effect is the loop body verbatim + a handful of lines changed, *not* a rewrite, and *not* implementing a full C++ object model (inheritance/vtables/host-method binding) in the engine. **Not** a JS-subset (the ARTI-FX surface): JS's double-everything number model is the slow path *and* further from our C++ codebase, so it's worse on both speed and portability of existing effects. -5. **Controls = minimal ceremony.** A scripted control is a near-plain top-level variable (e.g. `uint8_t speed = 60;` with a range annotation); the engine derives the MoonModule control + UI + persistence. Lighter than today's explicit `controls_.addControl(...)`, copy-paste-friendly. (Exact annotation syntax is the top-down's call.) -6. **Safety = staged, climb the tiers, don't pay upfront.** Ship the **cheap** tier first — array **bounds-checking** (a compare-branch per indexed access, low single-digit %, removable in a trusted/fast mode) + **watchdog / instruction budget** (kill a runaway loop, near-free). The **expensive** true-memory-sandbox tier (a script physically can't touch memory outside its arena — what WASM gives free, native can't cheaply) is **deferred**, reachable via the IR→WASM fallback only if a public script editor in the field shows the cheap tier isn't enough. Decided this way because the price of full sandboxing upfront isn't worth paying before evidence demands it. -7. **MoonModule-first.** A scripted module **is** a MoonModule (role, controls, `loop()`, generic UI, lifecycle, robustness, live-reconfig). The script ⇄ MoonModule binding (reach the `Buffer`/`AudioFrame`/LUT via the producer/consumer pull pattern, no copy) is the MoonLight value-add to design — no prior art copies cleanly. -8. **General in core + specific in light.** One engine serves a domain-neutral core script (e.g. transform sensor data) *and* a scripted layout / effect / modifier / driver. **Effect is the first role.** `RipplesEffect.h` is the *reference* effect for the language design (it stresses float trig + 3D + memset), but it is **too complex for the hello-world spike**: the first running script must be trivial (e.g. fill the buffer one color, or a single moving dot), proving the engine end-to-end before any real effect. Ripples is the *graduation* target, not the spike. For how an effect is structured for a newcomer, the [MoonLight effects tutorial](https://ewowi.github.io/MoonLight/moonlight/effects-tutorial/) is a good read (a sibling project's step-by-step). The simple→Ripples progression is itself the start-small-grow staging applied to the demo. -9. **Infinitely scalable.** Run *as many* live scripts concurrently as memory allows, exploiting PSRAM — each script is an independent compiled unit, the ceiling is free heap, not a fixed slot count. Many small scripted modules coexist; the device hosts what fits and degrades gracefully when it doesn't (the same scaling-to-available-memory contract the light pipeline already honours). -10. **Inline execution by default; task is the exception.** A scripted effect/layout/modifier/driver runs *inline in the `Scheduler` tick*, called exactly like a compiled effect's `loop()` — one mental model, no cross-thread sync to reach the buffer/`AudioFrame`, and it runs on the render task's *internal-RAM* stack (fast). Task-per-script isn't blocked on memory (a task stack can live in PSRAM), but it pays two costs inline doesn't: per-task **scheduling overhead** (a context switch per task per frame — hundreds of tasks thrash the scheduler, a ceiling independent of memory), and a **PSRAM-backed stack is hot-path-slow** (PSRAM latency on every per-pixel local access, ~12 vs ~80 MB/s). So inline keeps the per-script stack fast and free, and PSRAM is spent on script *code + data* (decision 9) rather than per-script stacks. A pinned task is the narrow, documented opt-in *only* for a long/blocking *core* script (e.g. slow sensor I/O) that must not share the render tick — never the default, never for a pipeline script. -11. **Sequencing: hybrid (depth-first to hello-world, then prove the seam on a 2nd ISA early).** Build the full vertical slice on Xtensa just far enough to run hello-world native (classic/S3), then *immediately* prove a minimal second-ISA backend (P4/RISC-V, or desktop x86-64) on that same slice — before deepening to controls/math/2D/3D. This retires the project's biggest risk (does the IR seam genuinely decouple front-end from backend?) at hello-world cost, when fixing it is cheap, rather than discovering a leak after six stages. Then deepen, primarily on Xtensa; the full second backend follows later. -12. **Domain-neutral engine core, thin binding.** The engine (front-end + IR + backends) is domain-neutral core — it never depends on MoonLight; the binding (`MoonLiveHost`/`MoonLiveEffect`) depends on the engine, one-directionally, through the engine's public API + a tiny injectable platform seam (`allocExec`/`alloc`). This clean layering is adopted *because it is what makes MoonLight-as-a-library optimal* (the [*Domain-neutral core*](../../CLAUDE.md) principle applied), and is **never compromised** for any separability — if a separation would cost MoonLight's speed/simplicity/hot-path/principles, it isn't done. -## Out of scope for Stage 1 - -Final VM-vs-JIT decision; the IR design; the language grammar spec; the web editor; per-engine benchmarking on real hardware; the MoonModule-binding mechanics; the sandboxing depth. All belong to the top-down document the prompt above generates. diff --git a/docs/work/future/livescripts-analysis-top-down.md b/docs/work/future/livescripts-analysis-top-down.md deleted file mode 100644 index dd94ea511..000000000 --- a/docs/work/future/livescripts-analysis-top-down.md +++ /dev/null @@ -1,445 +0,0 @@ -# MoonLive — live-script engine, top-down redesign - -> **Forward-looking research document — exception to CLAUDE.md present-tense rule.** **MoonLive** is MoonLight's live-script engine (the Moon family: MoonLight, MoonDeck, MoonLive — author an effect as text, see it live). Stage-2 companion to [livescripts-analysis-bottom-up.md](livescripts-analysis-bottom-up.md) (read first: it deep-reads the ESPLiveScript fork, surveys WLED ARTI-FX, the embedded-VM field, and a portable WASM fallback, and ends with the product-owner-direction decisions this document expands). It reasons from MoonLight's end goal — *author a script as text, run it on a running device on the next tick* — down to a reference architecture, a concrete API, a performance budget, and a staged spike plan. Modelled on [leddriver-analysis-top-down.md](../../work/future/leddriver-analysis-top-down.md). This expands the eight decisions already made; it does not re-open them. All design is written fresh against MoonLight's architecture — prior art (ESPLiveScript, ARTI-FX, MoonLight) is credited, not traced. - -## TL;DR - -- **MoonLive is our native-codegen engine** — a real compiler (lex → parse → AST → **IR** → native machine code), executed by direct function-pointer call, so a scripted effect runs at **near-100% native speed** in the render hot path. That speed, bound to a real module system, is MoonLight's standout. -- **One narrow boundary, three tiers.** The seam is `MoonLive::run()` (the analog of `LedDriver::push()`). Above it: a **platform-independent front-end** (tokenizer → parser → typed AST). Below it: a **typed IR** (the seam that lets one front-end feed many backends) → a **per-ISA backend** (Xtensa first). The IR is compile-time only — **zero per-pixel runtime cost**; it lowers to the same native instructions a hand-written backend would emit. -- **Xtensa first, no dead-ends.** Ship the Xtensa backend (classic ESP32 + S3 — MoonLight's first targets, the bench hardware) as a complete, blazing first increment. RISC-V (P4), ARM (Teensy), x86-64/ARM64 (desktop) are each *a new backend behind the unchanged IR* later — additive, never a rewrite. **WASM/WAMR is the per-target fallback** (IR→WASM is one more backend), so no target is ever blocked and a true sandbox stays reachable. -- **Source language: a C-subset, "as close as possible" to a precompiled effect, with pragmatic simplifications.** The effect *body* ports near-verbatim from a `.h` (types, `for`, integer + float math, `static_cast`, `RGB`, `hsvToRgb`, buffer writes). The C++ *file/class ceremony* that buys nothing in a script (`#pragma`/`#include`/`namespace`; lightened: `class : public EffectBase`/`override`/the `controls_`-dance) is supplied by the engine. **Not** JS (doubles = slow + further from our code); **not** full C++ (object model = build + hot-path cost for zero-value boilerplate). -- **Minimal-ceremony controls.** A control is a near-plain top-level variable with a range annotation; the engine derives the MoonModule control + UI + persistence. -- **MoonModule-first.** A scripted module **is** a `MoonModule` (role, controls, `loop()`, generic UI, lifecycle, robustness, live-reconfig). The script ⇄ host binding reaches the `Buffer` / `AudioFrame` / LUT through the producer/consumer pull pattern — no copy. This is the MoonLight value-add with no prior art to trace. -- **Safety staged.** Ship cheap first — array **bounds-checking** (a compare-branch per indexed access, low single-digit %, switchable off) + a **watchdog / instruction budget** (kill a runaway loop, near-free). The expensive **true memory sandbox** (WASM gives it free; native can't cheaply) is deferred, reachable via the IR→WASM fallback only if a public editor in the field demands it. -- **Staging spine = the [MoonLight effects tutorial](https://ewowi.github.io/MoonLight/moonlight/effects-tutorial/) ladder.** Each tutorial rung (random pixel → control → trails → oscillators → 2D → 3D → audio) is one engine-capability spike with a concrete acceptance bar. **RipplesEffect.h is the language-fidelity *graduation test*** (does the C-subset handle float trig + 3D + `memset` near-verbatim?), the hard case, not the hello-world. -- **Load-bearing spike:** a minimal native Xtensa engine running the tutorial's hello-world (`setRGB(random16(N), blue)`) live on an ESP32-S3, hitting the frame budget *and* surviving a deliberately-bad script via cheap safety. If native-with-cheap-safety can't hold 16K×50FPS, the fallback is demote-to-WASM/WAMR — a backend swap behind the IR, not a restart. -- **Cost, eyes open.** A real compiler is more work than adopting an off-the-shelf VM — weeks to the first beautiful Xtensa increment, each ISA backend its own increment later. The deliberate trade for native speed + a differentiator, mitigated exactly as the LED drivers were: spike-ordered, one complete increment at a time, the hard multi-target part deferred behind a seam that keeps it reachable. - -## 1. The goal, in detail - -A user writes a script — an effect, layout, modifier, driver, or a domain-neutral core rule (transform sensor data) — in a text box in the browser. The device compiles it and runs it as a first-class `MoonModule` **on the next tick**: no toolchain, no flash, no reboot. The same leap WLED took with ARTI-FX. The hard requirements (from the bottom-up): - -- **Blazingly fast** — the script runs in the render hot path at up to 16K+ lights × 50 FPS, so a slow per-pixel path is fatal. This is *the* constraint that picks native over interpreted. -- **General in core + specific in light** — one engine, many `MoonModule` roles. -- **Target order** — ESP32 classic + S3, then P4 + other ESP32, then Teensy, then desktop. -- **Smart memory** — IRAM/PSRAM-aware via `platform::alloc`, no hot-path allocation, compile-once. -- **Infinitely scalable** — run *as many* live scripts concurrently as memory allows, exploiting PSRAM. No fixed slot count; each script is an independent compiled unit and the only ceiling is free heap. A device hosts as many scripted modules (effects across layers, modifiers, core sensor rules) as fit, and degrades gracefully when it doesn't — the same scaling-to-available-memory contract the light pipeline already honours. -- **Synced** — `Scheduler`-tick, live reconfig (no reboot), tick-atomic hot-swap, robust to add/delete/replace in any order. -- **MoonModule-compatible** — controls, `loop()`, role, generic UI with zero per-script UI code. - -## 2. Why native, and why our own (expanding decision 1) - -The design space runs from interpreted to native, and the choice is driven by the hot-path requirement: - -- **AST-walk** (ARTI-FX): stores values as `double` and walks the tree per frame — which buys maximum flexibility and runs on any platform unchanged, at the cost of per-frame speed. That speed cost is what rules it out *for the 16K×50FPS hot path specifically* — not a flaw, a different trade than MoonLight needs here. (For a slow core script off the hot path, that trade would be fine.) -- **A bytecode VM is the middle ground** — a compact opcode stream run by a dispatch loop, far faster than tree-walking but paying a per-opcode dispatch tax every operation; at 16K×50FPS = 800K px/s that tax is the open question, not a given. -- **Native JIT is the only thing that reaches ~100%** (ESPLiveScript's 85 fps ≈ hand-written C++ — hpwit's result). The differentiator we're after is the *combination*: native speed **and** multi-target **and** bound to a real module system. Each prior engine has part of it — ESPLiveScript has the native speed (Xtensa); ARTI-FX has the live-scripting product shape and runs anywhere (interpreted). Neither combines all three; that combination is the open space. -- **WASM/WAMR** gets portability + free sandbox but tops at ~50% native (WAMR-AOT) with a 200KB+ runtime — kept as the fallback, not the flagship, because native speed is what we're chasing. - -**Why our own, not "adopt WAMR":** *Industry standards, our own code.* We take the textbook compiler *shape* (lex → parse → AST → IR → native — the LLVM structure scaled to an MCU) and textbook *names*, written fresh against our architecture. An off-the-shelf runtime would make the engine someone else's and cap us at half speed; building means the language, the MoonModule binding, and the hot path are ours to make beautiful. The cost (a real compiler) is accepted and staged. - -**What would flip it to WASM-wholesale:** if the load-bearing spike shows native-with-cheap-safety *cannot* hold 16K×50FPS on an S3 (native machine code makes this unlikely), or if a public script editor proves to demand a true sandbox we can't afford natively. The IR seam makes either flip a backend swap, not a restart. - -## 3. Reference architecture - -### 3.1 The one narrow boundary - -Everything hangs off a single tiny seam — the analog of the LED doc's `LedDriver::push(std::span<...>)`: - -```cpp -// A compiled script, ready to run. The host calls run() once per tick. -class MoonLive { -public: - bool ok() const; // compiled cleanly - const char* error() const; // human-readable compile/runtime error, "" if none - void run(); // execute the script's loop() — the hot path - void bind(MoonLiveHost& host); // wire controls + host data (§3.4) - // lifecycle: free() releases code + data; recompile swaps tick-atomically (§3.6) -}; -``` - -Above the line is the portable front-end; below it the IR and the per-ISA backend. The host (a `MoonLiveModule`, §3.3) owns a `MoonLive` and calls `run()` from its `loop()`. - -### 3.2 The three tiers (where the IR seam lives) - -``` - source text - │ ┌─────────────── platform-independent (one implementation, all targets) - tokenizer (lexer) - │ - parser → typed AST - │ - ┌────┴──────────── IR SEAM ──────────────┐ ← one front-end, many backends - │ - typed IR (SSA-ish, register/temp model, types resolved, bounds-check nodes inserted) - │ ┌─────────────── platform-bound (one backend per ISA) - backend: lower IR → native instructions - │ Xtensa first; RISC-V / ARM / x86-64 / ARM64 later; WASM as the fallback backend - encode → executable memory (platform::allocExec) → call as function pointer -``` - -- **Front-end (portable):** tokenizer + parser + AST live in `src/core/moonlive/` (domain-neutral). They know the *language*, never a CPU. One implementation serves every target. -- **IR (the seam):** a small typed intermediate representation — the AST lowered to a flat list of typed operations with explicit temporaries, types resolved, and **bounds-check / safety nodes inserted here** (so every backend inherits safety for free). This is a *compile-time data structure*; it does not exist at run time. -- **Backend (per-ISA):** lowers the IR to native instructions for one ISA. `src/platform//moonlive_backend_*.{h,cpp}` — the only place CPU-specific codegen lives, behind the platform boundary. A WASM backend (lower IR → `.wasm`, run by WAMR) is one such backend, the portable fallback. - -**Critical: the IR costs nothing at run time (decision 3).** It is consumed during compilation and discarded. The CPU executes only the final native instructions — identical in kind to hand-written assembly. *Matching native speed on Xtensa is non-negotiable.* The Xtensa backend must lower a hot loop to instructions that match hand-written Xtensa; the spike's acceptance bar **diffs generated instructions for the Ripples inner loop against a hand-emitted reference**, and an `__asm__` escape hatch covers the very hottest paths. If the IR ever costs hot-path speed, that's a backend bug to fix, not a tax to accept. - -### 3.3 A scripted module IS a MoonModule (decision 7) - -The one deliberate class hierarchy in the codebase is the module tree; a scripted module joins it like any other. `MoonLiveEffect` is a normal `EffectBase` whose `loop()` delegates to the compiled `MoonLive`: - -```cpp -// src/light/moonlive/MoonLiveEffect.h — a scripted effect is a first-class EffectBase -class MoonLiveEffect : public EffectBase { -public: - ModuleRole role() const override { return ModuleRole::Effect; } - const char* tags() const override { return "📝"; } // scripted - Dim dimensions() const override { return engine_.declaredDim(); } // from the script - - void setup() override { // acquire the engine - engine_.bind(host_); // wire the host data (§3.4) - } - - void onBuildControls() override { // DYNAMIC controls — re-runs when the script changes - controls_.addText("source", source_, kMaxSource); // the script text (persisted, editable) - // The engine declares its controls as NEUTRAL data; the binding translates to controls_. - for (auto& c : engine_.declaredControls()) // {name,type,min,max,default} — no MoonLight type - controls_.add(c.name, c.type, c.min, c.max); // binding maps neutral → MoonLight control (§3.5) - } - - // onBuildState is the rebuild sweep: it fires on a source edit (recompile) AND on a - // grid/size change (the engine re-sizes its script buffers for the new dimensions). - // MoonLight has no separate onSizeChanged — resize routes through onBuildState, so the - // dynamic-memory re-allocation rides the same hook every config change already uses. - void onBuildState() override { - engine_.compile(source_); // recompile if source changed - if (!engine_.ok()) { setStatus(engine_.error(), Severity::Error); return; } - engine_.allocForSize(width(), height(), depth()); // (re)alloc script data for the current size - } - - void onUpdate(const char* name) override { // cheap per-control reaction, no full rebuild - engine_.onControlChanged(name); // poke a running script's bound control - } - - void loop() override { - if (engine_.ok()) engine_.run(); // the hot path: native code over our buffer - } - - void teardown() override { // release: free compiled code + script data - engine_.free(); // the "destructor" role — arenas returned to the heap - } -}; -``` - -The same shape gives `MoonLiveLayout` (role `Layout`, emits coordinates), `MoonLiveModifier` (role `Modifier`, remaps positions), `MoonLiveDriver` (role `Driver`, consumes the buffer), and a core `MoonLiveModule` (domain-neutral, e.g. a sensor rule). One engine, many roles — each a thin `MoonModule` subclass whose `loop()` is `engine_.run()`. The UI renders them generically (the `source` text control + the script-declared controls) with **zero per-script UI code** — exactly the module-tree payoff. - -**A scripted module implements the whole `MoonModule` lifecycle, not just `loop()`** — that's what makes it a first-class module and what answers dynamic controls / dynamic memory / cleanup: - -- **`onBuildControls()` — dynamic controls.** Re-runs whenever the module rebuilds, so a script that declares different controls (a new `@control` var) gets a different control set in the UI + persistence, live. The controls are *the script's*, not a fixed list. -- **`onBuildState()` — dynamic memory on size change.** MoonLight routes a grid/size change through `onBuildState` (the same rebuild sweep that applies every config change without a reboot), so MoonLive re-allocates its per-size script buffers here (`allocForSize`, PSRAM-first per §3.7). There is no bespoke `onSizeChanged` — using the existing hook means a scripted module resizes exactly like a compiled one, and inherits the no-reboot + robustness contracts for free. -- **`setup()` / `teardown()` — acquire / release (the destructor role).** `teardown` frees the compiled code block + the script's data arena back to the heap, so deleting a scripted module returns all its memory — the lifecycle that makes "as many scripts as memory allows" (§3.7) safe to add *and remove* in any order. -- **`onUpdate(name)` — cheap per-control reaction.** A control edit pokes the running script's bound variable without a full recompile (the fast path for a slider drag); only a *source* edit triggers the heavier `onBuildState` recompile. - -So the binding overrides the same hooks any compiled module does; the only difference is that each one delegates to the compiled `MoonLive` instead of hand-written C++. - -**Crucially, all of these lifecycle methods live in the *binding* (`MoonLiveEffect`, `src/light/moonlive/`), not in the engine.** `onBuildControls`/`onBuildState`/`onUpdate`/`teardown`, `EffectBase`, `ModuleRole`, `controls_` — every MoonLight type — sit on the binding side of the §3.9 seam. The engine (`MoonLive`, `src/core/moonlive/`) sees none of them; the binding reaches it only through a **neutral public API**: `compile(source)`, `run()`, `free()`, `declaredControls()` → a plain list of `{name, type, min, max, default}` structs the engine owns, and `allocForSize(w, h, d)` → plain ints. The binding *translates* — it reads the engine's neutral `declaredControls()` and calls MoonLight's `controls_.addControl(...)`; it maps a grid resize to `allocForSize`. **The engine never takes a `ControlList`, a `Buffer`, or any MoonLight type** — so the rich MoonModule lifecycle is entirely a property of the binding, and the engine stays the domain-neutral core §3.9 describes. (This is the seam working as intended: a different host writes its own binding with its own lifecycle against the same neutral engine API.) - -### 3.4 The host binding — script ⇄ MoonModule (decision 7, the value-add) - -Rather than a flat name→pointer registry (the host-binding shape surveyed engines share), MoonLight uses a **MoonModule-aware `MoonLiveHost`** that exposes the producer/consumer data the script needs, by reference, no copy — the same pull pattern effects already use (`EffectBase::buffer()/width()/elapsed()`, [Layer.h:499-504](../../moonmodules/light/supporting.md#layer)): - -- **Buffer + geometry** — `width()`, `height()`, `depth()`, `channelsPerLight()`, `nrOfLights()`, `elapsed()`, and pixel writers `setRGB(i,c)` / `setRGBXY(x,y,c)` / `setRGBXYZ(x,y,z,c)` (the MoonLight tutorial's exact surface). These compile to direct loads/stores against `layer_->buffer()` — the **identity-mapping fast path** preserved (the script writes the real buffer, no intermediate copy). -- **Controls** — the script's declared variables (§3.5) bind by reference so a UI control edit updates the running script live. -- **Producer structs** — a core or audio script reads `AudioFrame` (level + 16-band spectrum) / sensor structs through the same `const`-pointer pull the C++ effects use (`AudioService::latestFrame()`), so add/remove in any order returns a live or silent-default frame, never null (robustness). -- **Built-ins** — `hsvToRgb`/`hsv`, `random16`, `sin`/`cos`/`sqrt`/`floor` (the trig Ripples needs), `millis`/`elapsed`, `fill`/`memset`. A small, fixed, recognizable library (FastLED-flavoured — the vocabulary effect authors already know), implemented once in the host and callable from any backend. - -The binding is generated *around* the script body — the script never writes `#include`, never reaches a raw pointer it shouldn't, and the host decides what's in scope per role (an effect sees the buffer; a core sensor script sees the sensor struct, not the LED buffer). - -### 3.5 Controls — minimal ceremony (decision 5) - -A control is a near-plain top-level variable with a range annotation; the engine derives the `MoonModule` control + UI + persistence: - -```c -byte speed = 50; // @control 0..99 → controls_.addControl("speed", …, 0, 99) -uint8_t interval = 128; // @control 1..254 -``` - -The front-end collects annotated top-level vars during parsing and the engine exposes them as a neutral `declaredControls()` list (`{name, type, min, max, default}` — no MoonLight type); the *binding* reads that list and calls the normal `controls_.add(...)` the rest of MoonLight uses (§3.3) — so a scripted control is indistinguishable from a compiled one in the UI, persistence, and the live-reconfig sweep, while the engine stays MoonLight-agnostic. Lighter than today's explicit `onBuildControls` + `addControl` (the engine writes that for you), and copy-paste-friendly: the `uint8_t speed = 50;` line is *already* how RipplesEffect.h declares it. (Exact annotation syntax — `@control`, a trailing comment convention, or a `slider(0,99)` initializer — is settled in the spike; the principle is "declare the var, get the control".) - -### 3.6 Live reconfig + tick-atomic hot-swap (decision: sync) - -A re-pushed script must swap in on the next tick with the old one freed, no reboot, no crash mid-render — the no-reboot + robustness principles applied to *code*. The mechanism rides the existing `onBuildState()` rebuild sweep (the same one that makes every config change live): a `source` edit marks the module dirty; the next `onBuildState()` compiles the new source into a *second* `MoonLive`, and only on success swaps the active pointer (the old `MoonLive` freed after the swap), so a failed compile leaves the running effect untouched and surfaces the error in the module's status. `run()` reads the active pointer once per tick — the swap is a single pointer store between ticks, never mid-`loop()`. - -### 3.7 Memory placement + infinite scalability, routed through `platform::` (decisions: smart memory, infinitely scalable) - -Memory placement routes through the existing `platform::` seam, so it's one policy, not scattered per-target branches: - -- **Compiled code** → `platform::allocExec(size)` (a new seam: `MALLOC_CAP_EXEC` IRAM on ESP32; `mmap(PROT_EXEC)` on desktop; the platform decides, the engine doesn't know). PSRAM-capable on S3/P4 where the chip allows executable PSRAM. -- **Script data** (globals, stack arena) → `platform::alloc` (PSRAM-first with internal fallback — already the project's policy). -- **Compile-once** → a portable compiled-artifact format persisted to LittleFS, so a known-good script skips device-side recompile on boot. The native artifact is per-ISA; the portable fallback artifact is one file for all targets. - -**Infinite scalability — as many scripts as memory allows.** Each `MoonLive` (§3.1) is a **self-contained compiled unit**: its own code block (from `allocExec`) and its own data arena (from `alloc`), owned by the `MoonLiveModule` that holds it, freed when that module is deleted. Nothing is shared or fixed-slot — so running N scripts is just N independent `MoonLive`s, and **the only ceiling is free heap**, not an arbitrary limit. This falls out of the architecture for free: - -- **The module tree already hosts N modules.** A scripted module is a `MoonModule` (§3.3); the tree puts no cap on how many effects a Layer holds or how many peripherals System hosts. Ten scripted effects across layers + a scripted modifier + two core sensor rules are just twelve modules — the UI, persistence, and `Scheduler` handle them like any other. -- **PSRAM is where it scales.** On an S3/P4 (8 MB PSRAM) the compiled code + data arenas live in PSRAM (`alloc` is PSRAM-first), so the device holds *far* more scripts than internal RAM alone would allow — exploiting PSRAM is exactly what lifts the ceiling from "a handful" to "as many as the script sizes sum to under PSRAM." A non-PSRAM classic ESP32 holds fewer (internal heap only) — correct and honest, the same internal-vs-PSRAM split the rest of the system has. -- **Graceful degradation when full.** When the next script won't fit, the device does what the light pipeline already does at the memory edge ([architecture.md § scaling to available memory](../../explanation/architecture/moonlight.md#scaling-to-available-memory)): the compile/bind fails cleanly, the module reports a "not enough memory" status, and everything already running keeps running — no crash, no reboot (the robustness + no-reboot principles). The cap is reached by *degrading*, never by bricking. -- **The hot-path cost is per-*running* script, not per-*loaded* script.** Memory scales with how many scripts are loaded; tick time scales with how many are *enabled and rendering*. A device can hold a large library of scripts in PSRAM and run only the active ones, so "infinitely scalable in memory" doesn't mean "infinitely slow" — a disabled scripted module costs RAM but no tick time (and the disable-releases-resources backlog item, when it lands, lets it cost neither). - -**Memory-scaling status + the one refactor ahead (watch-item as the language grows).** A memory review at the 3-builtin stage (`setRGB`/`fill`/`random16`) pinned where the per-effect cost lives and what scales with language richness — recorded here so the optimisation is planned, not discovered: - -- **Already in place (don't redo):** the exec block is allocated at the *emitted length* (word-rounded), not a worst-case cap — a `fill` is ~50–70 B of native code, a four-call `setRGB` ~400 B, so per-effect heap scales with the script, not a flat reservation. And the effect reports `setDynamicBytes(codeLen())`, so the UI card's "+ dynamic" reflects the JIT'd program (0 when a compile fails and frees it). These two are the right shape for everything below. -- **Flat regardless of language size:** the engine's at-rest members (~48 B/instance) and each `Builtin` descriptor (32 B). A full math/color library is ~30–50 builtins → a ~1.5 KB table that should become a **`static const` built once** (today `lightBuiltins()` returns a ~520 B table by value per `onBuildState` — fine at 3 entries, wasteful at 50; make it static when the library grows). -- **Scales with script complexity (the exec block, heap, per effect):** today's one-liners are 50–400 B; a Ripples-class effect (per-pixel `sqrt`/`sin`, 3D, a fade loop, two controls) is a few hundred instructions → an estimated **1.5–4 KB of native code**, comparable to the equivalent compiled effect (the point of native). PSRAM-first `allocExec` (§ above) absorbs this. -- **The one architectural change ahead — transient compile buffers must move stack → reusable heap arena.** Today `compile()` puts the staging buffer (`kCodeCap`), the `IrProgram` (`kMaxIrOps=64` ops × 32 B ≈ 2 KB), and the assembler buffer on the **stack** (~4 KB peak, off the hot path, in `onBuildState`). This is fine for the current grammar but **will not hold once scripts have loops + real expressions**: a Ripples body easily exceeds 64 IR ops, and growing `kMaxIrOps` to 256–512 makes the IR alone 8–16 KB — too large for an MCU task stack. The planned fix (matches "code+data arenas" above): a **single heap arena the compiler borrows during `onBuildState` and releases**, sized once and reused across recompiles, instead of stack arrays. Introduce it **when the op count first outgrows the stack-safe range — likely Stage 3 (math + loops)**, not before; it's a contained change (the compiler's buffers, not the IR or front-end) and the `allocExec(len)` direction already set the precedent. -- **New at full language — a per-script *data* arena.** Scripts are stateless pure functions today (no per-instance data beyond the source text). Once a script declares controls (`uint8_t speed = 50;`) or state (`static float lut[256];`), each effect needs a **data arena** (`platform::alloc`, PSRAM-first) alongside its code block — a few hundred bytes to a few KB per effect, sized per script. This is the second arena §3.7 already names; it lands with Stage 1 (controls) and grows through the oscillator/LUT stages. -- **Backlog — grow-on-demand source buffer (small *and* big scripts).** The `source` text control today is a fixed member buffer (`MoonLiveEffect::source_[512]`), and the textarea control API now carries a `uint16_t` buffer size (was capped at 255), which covers Stage-1 multi-control scripts. But it's still a *fixed* size on every effect (a 512 B member even for `fill(0,0,255);`), and Ripples-class scripts in later stages will need KBs. The cap is that the generic control write path binds a fixed `(char* ptr, bufSize)` and `parseString` truncates the incoming value to it — so a buffer can't *grow* mid-write. The future design: a **dynamically-sized source buffer** (PSRAM-first, allocated to the script's actual length and freed on teardown) so a small script costs little and a big script is allowed — which means the control write path (or a MoonLive-specific source-set hook) must re-allocate-then-rebind *before* the write lands, instead of truncating. Land it when scripts genuinely outgrow a sensible fixed cap (Stage 6 / Ripples), together with the data + compile arenas above. - -Net: no resource cliff — at-rest per-effect cost stays PSRAM-friendly (code + data arenas, a few KB each) — but **one deliberate refactor is queued** (stack compile-buffers → shared reused arena), best landed exactly when a real script body first needs it. - -### 3.8 Execution model — inline by default, task as the exception (decision: sync) - -**A script runs inline in the `Scheduler` tick by default — not in its own task.** A scripted effect's `loop()` is called exactly like a compiled effect's `loop()`, on the render task, each tick. The task-per-script model some engines use fits when a script *is* the top-level loop and owns the device; in MoonLight a scripted module is one `MoonModule` among many, called from the same single-threaded render loop as every compiled module, so inline is the consistent shape. Three reasons make inline the default, not just a choice: - -- **Consistency.** A scripted effect behaves identically to a compiled one — same call site, same hot-path rules, same `Scheduler`. One mental model, and the UI/persistence/lifecycle treat it like any other module. -- **It sidesteps two costs task-per-script can't.** Task stacks *can* live in PSRAM (`xTaskCreateWithCaps`, `MALLOC_CAP_SPIRAM`), so task-per-script is not blocked on *internal* RAM — but it pays two costs inline doesn't: (a) **scheduling overhead** — each task is a TCB + scheduler bookkeeping + a context switch; hundreds of tasks all wanting to run each frame thrash the scheduler instead of rendering, a ceiling that has nothing to do with memory; and (b) **a PSRAM-backed task stack is hot-path-slow** — a per-pixel inner loop touching locals on a PSRAM stack pays PSRAM latency (~12 MB/s vs internal ~80 MB/s) every access, exactly what a 16K×50FPS loop can't afford. An inline script runs on the render task's *internal-RAM* stack, fast, with no per-task scheduler cost. So PSRAM scales the script's *code + data* (§3.7), and inline keeps the per-script *stack* fast and free — the two pull together, where task-per-script would put them in tension. -- **No cross-thread sync.** An inline script reads `buffer()` / `elapsed()` / `AudioFrame` from the thread that owns them — no locks, no race, no memory barriers. A task touching the shared buffer while the render task reads it is exactly the data race the single-threaded hot path avoids by design. - -An inline script obeys the no-blocking-hot-path rule (it can't `delay`); a runaway loop is caught by the instruction-budget watchdog (§4), so a bad inline script degrades, it doesn't wedge the tick. - -**The exception — a pinned task — is narrow and opt-in.** A *core* script that genuinely blocks or runs at its own cadence and must **not** share the render tick (e.g. a slow I²C sensor transaction, or a rule that ticks at 1 Hz independent of render) may opt into its own task, pinned opposite the render core. This is a per-module, documented exception for off-hot-path core work — never the default, and never for a script in the render pipeline (effect/layout/modifier/driver), which is always inline. Two execution paths exist, but the inline one is the default and the task one earns its place case by case. - -### 3.9 Layering — a domain-neutral engine core, a thin binding (decision: domain-neutral core) - -The tiers above already separate cleanly along MoonLight's own *Domain-neutral core* principle, and the layering is held to it deliberately: - -- **The engine core (MoonLive) is domain-neutral.** "MoonLive" is the engine's *name*, not a coupling — the front-end (`src/core/moonlive/`) and the IR + backends (`src/platform//moonlive_backend_*`) know the *language* and the *ISA*, never `Buffer`, `EffectBase`, the module tree, or anything light- or MoonLight-specific. The core's only outward contract is a tiny injectable platform seam (`platform::allocExec` / `alloc` / `millis`) — a handful of functions, not a reach into MoonLight's full platform layer. -- **The binding is the only MoonLight-coupled layer**, and it is *thin*. `MoonLiveHost` + `MoonLiveEffect`/`MoonLiveLayout`/… (`src/light/moonlive/`, with a core `MoonLiveModule` for sensor rules) sit **on top of** the engine's public API and consume it; they never reach into engine internals. -- **Dependency direction is one-way:** the binding depends on the engine; the engine never depends on the binding (or on MoonLight). The engine does not `#include` MoonLight; MoonLight `#include`s the engine. - -Why this matters concretely: **it is what makes MoonLight-as-a-library optimal.** A clean library needs exactly this — a domain-neutral core with a one-directional dependency and a thin, replaceable binding, so the whole stack composes without circular dependencies or hidden coupling. So this layering is not extra structure for its own sake; it is the *Domain-neutral core* + *Complexity lives in core, domain modules stay simple* principles applied, and it is the same boundary MoonLight needs to be a well-formed library. - -A true property of that boundary, worth stating: because the core (MoonLive) knows only the *language* and the *ISA* — never LEDs, buffers, or MoonLight — the same front-end + IR + backends would serve a **wholly different host**: a different output device, or a different application entirely (a script that drives a display, reads a keypad, computes a result). Such a host writes its own thin binding against the same public API + platform seam; nothing in the core changes. The IR seam is what makes that portable, too — the host targets whatever chip it likes by writing one backend behind the unchanged IR. This is a *consequence* of building the core domain-neutral for MoonLight, not a goal we design toward — but it is real, and it is the mark of a well-factored core: it doesn't care what you point it at. - -**Hard constraint: the layering is justified entirely by MoonLight's optimality — never compromised for it.** The clean engine/binding split is adopted only because it makes MoonLight architecturally sound, fast, and CLAUDE.md/architecture.md-compliant (domain-neutral core, data-over-objects on the hot path, the platform boundary). If any separability would cost MoonLight's optimality — a slower hot path, a heavier binding, an abstraction the engine doesn't need — it is **not** done. The binding stays thin and the core stays neutral *because that is the optimal MoonLight design*, full stop; nothing in the layering is bent toward a use beyond MoonLight. - -## 4. Safety — staged (decision 6) - -A user-facing editor means a bad script must degrade, not brick. There are two distinct failure surfaces, and they need different defenses: - -- **Compile-time (parser / analysis).** A malformed script must be *rejected cleanly at parse*, never crash the compiler. The `fix-warnings` fork's null-deref is exactly this class: a nested external-call argument produced an AST node missing its children, and the compiler dereferenced them unconditionally. The defense is **AST/analysis validation** — the parser checks node shape (children present, types resolved) and emits a diagnostic instead of dereferencing a null, backed by a **regression test** per malformed input (the `fix-warnings` case is the first such test). This is a correctness property of the front-end, not a runtime cost. -- **Runtime — cheap, ship first.** Once a script *compiles*, a bad *value* at run time must degrade, not brick: (a) **Array bounds-checking** — the IR inserts a compare-branch before each indexed access (a clamp or skip on out-of-range), e.g. a `setRGB(i, …)` with `i` past the buffer. Low single-digit % overhead, inserted at the IR so every backend gets it, and **switchable off** in a trusted/fast mode for vetted built-in scripts. (b) **Watchdog / instruction budget** — a per-tick instruction or time budget that aborts a runaway `while(1)` (near-free; the task WDT already does most of it). Together these catch the common *runtime* failures — out-of-range index, infinite loop — at low cost. -- **Expensive, deferred.** A *true* memory sandbox (the script physically cannot touch memory outside its arena) is what native can't cheaply provide. Don't build it first — it's reachable via the IR→WASM fallback (suspect scripts compiled to the sandboxed backend) only if a public editor in the field shows the cheap tier isn't enough. Safety is a ladder climbed on evidence, not a wall built before any script runs. - -## 5. The language — a C-subset as close as possible (decision 4) - -### 5.1 The model - -A C-subset, not full C++, not JS. The type model is exactly what real effects use: `uint8_t`/`uint16_t`/`uint32_t`/`int`/`lengthType`/`nrOfLightsType` integers (with 64-bit where overflow matters — Rainbow's `uint64_t` phase), `float` (Ripples' trig), `bool`, `char`, a `RGB`/`CRGB` struct, and arrays (incl. multi-dim). Control flow: `if/else`, ternary, `while`, C-style `for`, `break`/`continue`, `return`; user functions; `static_cast` (or C casts). Grammar: **hand-written recursive-descent** — the recognizable textbook default, what most embedded script languages use; a PEG is the alternative but recursive-descent is simpler to make fast and to emit good errors from. Built-ins: the fixed host library (§3.4). - -### 5.2 What's dropped vs lightened (the pragmatic simplifications) - -- **Dropped** (file ceremony, zero value in a script): `#pragma once`, `#include`, `namespace`. The engine supplies the surrounding module. -- **Lightened** (the C++ object model): no `class : public EffectBase`, no `override`, no `controls_.addControl(...)` host-object dance. The engine synthesizes the `MoonLiveEffect` wrapper (§3.3) around the script body; the role/`dimensions`/controls come from light annotations (§3.5) and the script's `loop()`. -- **Kept verbatim** (the part you iterate on): types, the `loop()` body, all the math, `static_cast`, `RGB c = hsvToRgb(...)`, the loops. - -**Why not full C++:** supporting `class`/inheritance/`override`/host-method-binding means implementing a C++ object model (vtables, member-reference binding) in the engine — build cost up front, and the object machinery is the very "object graph in the hot path" the architecture forbids. The wrapper has no runtime value; let the engine write it. - -**Why not JS** (the ARTI-FX surface): JS's number model is doubles-everywhere — the per-pixel cost that makes ARTI-FX flexible-but-not-fast — *and* it's further from our C++ effects, so porting an existing effect is harder, not easier. A C-subset is both faster and closer to the source. - -### 5.3 RipplesEffect.h → scripted form (the language-fidelity test) - -`RipplesEffect.h` is the **graduation test** for the language (float trig + 3D + `memset` + two controls — the hard case, deliberately not the hello-world). The body must port near-verbatim: - -**Today (`src/light/effects/RipplesEffect.h`, the C++):** - -```cpp -class RipplesEffect : public EffectBase { // ← dropped (engine supplies) - const char* tags() const override { return "💫🟦🦅"; } // ← lightened → annotation - Dim dimensions() const override { return Dim::D3; } // ← lightened → annotation - uint8_t speed = 50; // ← kept (becomes a control) - uint8_t interval = 128; - void onBuildControls() override { // ← dropped (derived from the vars) - controls_.addControl("speed", speed, 0, 99); - controls_.addControl("interval", interval, 1, 254); - } - void loop() override { // ← KEPT VERBATIM (the body) - uint8_t* buf = buffer(); … std::memset(buf, 0, nrOfLights()*cpl); - const float rippleInterval = 1.3f * … * std::sqrt((float)h); - for (lengthType z=0; z=h) continue; - uint8_t hue = elapsed()/50 + x*3 + z*7; - RGB c = hsvToRgb(hue,255,255); - setRGBXYZ(x, y, z, c); // replaces manual idx + px[0..2] - } -} -``` - -The diff is exactly the ceremony: gone are `class`/`override`/`onBuildControls`/`#include`/`namespace`/`std::`-qualification and the manual buffer-index arithmetic (→ `setRGBXYZ`). The math, the trig, the loops, the types — verbatim. **If the engine can compile this at native speed, the C-subset decision is proven.** (Float-trig native codegen is the part to validate — see the perf budget.) - -## 6. Testing — the engine's biggest structural advantage - -A live-script engine is one of the **most testable things MoonLight can build**, and MoonLight's two-tier test framework ([testing.md](../../reference/testing.md): doctest unit tests + JSON scenarios, each run in-process *and* live) maps onto it almost perfectly. This is a genuine edge: the bottom-up's structural note that ESPLiveScript ships `.ino` examples and no unit suite isn't a knock on it — it's the gap our framework closes. **Every live-script feature gets pinned by a test, back to back**, because two properties make a compiler exceptionally test-friendly: - -1. **Every compiler stage is a pure input→output function.** Lex, parse, IR-lower, codegen each take a known input and produce a deterministic output — the easiest thing in the world to unit-test, with no hardware and no flakiness. -2. **A script's *result* is deterministic and exactly assertable.** A known script over a known grid at a known `elapsed()` produces an exact buffer — byte-for-byte checkable. There is no "looks about right"; there is a golden buffer. - -### 6.1 Unit tests (`test/unit/core/unit_moonlive_*.cpp`, doctest) - -The compiler front-end and IR are domain-neutral core (§3.9), so they unit-test on the desktop with zero hardware: - -- **Tokenizer / parser** — source string → expected token list / AST shape; every language construct (types, `for`/`if`/ternary, functions, arrays, `static_cast`, struct access) and every *error* (unterminated string, type mismatch, undeclared var) pinned to an expected diagnostic. The fuzz-class bug the `fix-warnings` fork fixed (a nested external-call arg) is exactly a parser unit test — the regression that would have caught it for free. -- **IR** — AST → expected IR for representative snippets; the bounds-check / safety nodes (§4) asserted present where the IR should insert them. -- **Codegen (host backend)** — the **desktop/x86-64 backend** is itself a test asset: it runs in-process, so a compiled script *executes during a unit test* and its output buffer is asserted directly. A script that fills blue → assert every pixel is `(0,0,255)`; Ripples at a fixed `elapsed()` → assert the exact lit-column pattern. No device needed to test *the language*; the device tests only *the native ISA backend*. -- **Determinism harness** — the existing `setTestNowMs` clock-override seam (the same one scenarios use) lets a time-dependent script be tested at a fixed tick, so an animated effect is a deterministic assertion, not a guess. - -### 6.2 Scenario tests (`test/scenarios/{core,light}/scenario_moonlive_*.json`) - -Scenarios exercise a scripted module *as a wired `MoonModule`* — the integration layer unit tests don't reach: - -- **The MoonModule binding** — `add_module MoonLiveEffect`, `set_control source=

    zZaR;_%F>bKA7td_o_!mTt#s(P8nq4pNz|oAzJHk-0#Ddc_Mv5MtA?|XKpXpuKfH`A zd}NTBH@}3ZMD`l&*FAzxHA5wv5QpzoI0-giiz_nHaSv0G5Q7}=g){LlFx`7*sI8AK zAe`UmHaQe*@!CotGrPM9yo0L5q;C5dj7nO8tW>82sc_ldvZ|HH{*W#9b+iNxs#1l? zRmUsKJ%@^jk4BNXDq=zfC>jMiCJOEcM&KkE-T>G}u{a$_JdQy6B8UcnGYm5y{2*JZ zDLYu$QwVOJ7%%>6{hNi2lZkP|Eey!CqPRc{>8J$)Oo%>d{`ti*tgy^@Q(im{?tn`W zS3iy`PC`1tM<4K-A=?smQZ5VwTur z+f1k=AnuBQq^>~7#i9&0h7AZ%Z{lHaJ4`W~4n;q^z3gB;(pdxBKLxH(aG1upgN?Q> zsmQ7M$bXwR%utuDQxWsjq(V`owc6TAU<3bOwVzR^A*AvNn?-Jcw{Y7<<>x22*?f6y$3S8P3lDuOPa_5WE=QFvIzCE%Xov8;={3S*s zeF4nyP-P*0xC-C?9NZu_aHgOZ&COu1^Hc*Mn-OOqzltM-YvI3K(4%SfJ}fbm6>~%6 z^t~}4IUkFZtu%PFFWQW$;U@*-yl-XB%f}Dv8t|!T z3l*OCj*$)m2ii$1H3fIpBd+wQcm!AajeJfW)wd9Nd%{_)3Bvxg#|anUQq6i z{Z4RZ7HFX>CaX7tTE>rZy`8=bX|j?)UMvtLz}|ejX_%c;&t9}ZRe&+&2sPNlYP#IS_c=o$wcWFXX5FCcFgF$ z;90o7@8laDutA8(5e!s?c<8Yk*y}j0p0jmWG#jV;L4L%yQZy5i#Xje>47lAVlY(~< zdOZxHt@wh^dV{3%?GI`283wG2Vfm)+q~4y5VFH^q+>n)OlHA}mPk_o* zYqP3}(}b)1?i|B`$8PoFSA8e%(L*}{Vk$dDuF-($h@R>4y@)ejj=a&{z!XKY1+z}+ z);wp0P+#JJuTX0P^2D$jTZGTg;j)08IL;yJ3Zz6+v$=6wKilWHiWn!zq`J^w@EvLD z2#M~uRD{E3VYPf@_rckL9LfcP`5VBzDqVH&$?*fLNCKAa745O4ztJw$0frOKFei&a zAng7{{jC*mQI2TTn7ksy3Ann)dddmy6nP9#~;vaOSJ%gm#)P(qIRAtE@aeU zDD<>Oz${Q1Z-DM?Mk)|uE{L7W2FwQ8FZRyfnq=wAb&851dIwoOj6-`F+{`Gzi%?WB z3q4y8u^KJNRW(MHHp$j5y@)wZJyN&%IM9N879R3xvbF>d8R@|N+5&_0;{fb`$7#Bji6D72+kco0l~m5DIagAB%uPoL3VKo>^Xg99NB zg3(v5jz~w2Xm}iN75`vSE#hqVMQ z3fLq@#H^E@ACO&a!f6A%UPQ^@nT6CK86w;&w&efOb>0t2_5b@naM&UoaN!1AI0N@e z0T-GTnwHu^(^6Y#%O+Drv&2=7N*tBi!q#ow4NNQBXjZoFu;Fd%eq+h`6$nw*KiS7s)VijVVlMWE>9DUjK6J^Q&JzSQ%efl3aQxG zz0gw+UHq%p^Tx^`@KYUa8^*mvh0Saj`9Znhw(=T8`}hSjV!Py$UZBjv^YpBS#W=kC zQOqEAxpx3>5b>{p7J{9i@4d6&Ig(wf?Lm!#+^1M$Y1m;Uyif=3^hVk7v6GFwV{GiQ z&RByA9sFZGnuXYx4lV106;cs!+qpvLsA6d)Tp%ylgM8o5ix!GzbJcW3K-P%KYl5Q6Jo?X29S&mf)bYuTcN9}`Ak zB=?^I#4&*Q6fk|FDZ)-K@@+#YPcu=Z0|q^ktupC&s>zZy-WuG^F0_w=b*sl3P)YVp zOmLZ?yOD;F>Chfp>{Nrvd|#CHD(JS7x0btLs{r1syOPAltl$^1xwrS;-dMiG^s>PS z^|L!-wO|GnkI4RDX33@(yeYT9CVAYM%aA;mi;DfXE)6veZWXiZ8XG}gf;04 zpr!df`wBsC0K+#HqpQze`bAuP1Cc0jB&vZm-{<1^oM@)G>4cY%=7^3VDWDAqC2%x% zsfF>KyO{JTk!C@ap?iTs7o>@vY&2*sptT8hI%fL7%>l~vjx7)X-^0^xzIj(QwaY~k zx!-riSGu#g-=MXz!sXuo&VqrNguvS5A#CRV8m`^#B8?3f4jM-9&E7C*yJyH;^vsm5 zx12xWba2Av)r5obgPpTo|^6s5b1$awwtJ z|LXxH|G*cL{>iw|w1+wW|Hs!`4eJ^4zl$=WQaCO;h7Y#ww0%|X$I59e!J z^Z7_F#5MU&Jm~vl-yh;nOH2e{;vsY{)mow_1VC^6NMycY#yW}~cfTzzU;gJ;-z}>3 z9K+jblLeg#ZZeOQX_G97=)3|occ$hv=E2pdgy#+O+)JyoFo`1GP|bd?BaK0W-cvLPM-$PhF5#shqlk_wyI?{eoV=io5^ zcwyX~_+;;03I2+ySI6eMa}W6=jPAraGA5NXr^A=qJ@3ZS3;{x^s{^HkToFp(~0%K#xYq$VP_BD4m< z9BfDI@{7l*Y&?}=zh~k#RMN6Fch!A~I4DZyngYZ&hEhMZ(_>Pqt`l~xsJd3;n(Y-M z!{bj!f5=T(7iV??uL}DB7fd*!&xUt)+vG+2T=LD!U`Z?vPec90C(}a-!4KcwpL0X$ zpETe9@W#SBZ(l4Kn&)noMljsRM6>%ZMyAbCEKn6R2~>x(^6)BOPBft zxc}C8P5TqOZ>}*&<1~z`;WQRNS|tc*M|3t;u6VY`23El zRfjgFpUw7Y&MivOiSCaFga8#leMd86P5+ViiG}anm}>uya*rKT=rR$et)zk`SG{gK zzRe%|PxS^An5MQ4cn)$PxAuf5Q3Yn204>o@jzEvMI%gUR1|E~G_$-t;u%hqvuh2IUw=0%KWlW1iKJf1^* zZaVdCdCs;+WzP$OLu6a@`KPUK72faQ`Fztie$MXL5}!0MAJbBrIPFAo79c$7NeU4_ zGk;Li8)^0IlV{7MGai|K%Gs z;BSv1fZuUG8>y}I*-~5z<9cL-z(b}rC8OLy(srxp+0!fV`uFU@U^>Eai8BN}eBkw3 zFBb_iNnYJ$t`$1@^#B^WOcQnQF0AqdGs?DoveX?vWbU|QkhDdIIMd2hi+J}<3v_J! z45ygC<}TYc{+hLEa`_VGJt{7@^0dtaMrPhQ2N2Nt$NA1R?PA=2w}#{YQI>IbkUUNX zYwDc^hg?!_P{E+yBNa3d8)CFq><^&j$a2<9JqciDXKm`d| zJgM`F<%vNuUq)!ZwnF`dv*7LpF=FFU7@Gy!WoJW8c3FsrJCfnIA!&{BecYyBH|;j7 zLBv@d=v2t|*m|Aht3S#5+befq;DwJ^9*kA72K+ zS;0FGMsIPgIvT$*3TW)%EqK!36;Dr^dk0p`Jf-iAgv4Tdo-`xHr1gK-Uf0|Z(;_0d z%SH3P*b?PKR84RfhaynkqeQ!-;!!b=6D&Nn`B_=jG32%!rNu~flke4>M02TT%e1B{n{D-x{hfasp}6qV!T(qwWAeIKAc+mVtaKmelL8X z3K2QRB5u>ZaeQXe7{4RyJUINnSl9I9@q!~I)j~e};21%Z*28@1MMd!nBEz z(~qf9<8^d}05dq~~v?}s7PhB)~afGmJ`@m-uh@*Aa7AbEb)Owa+&AXeDpuHfwJ(5 z(7|1U%K|Vro7d2cr{5+Hev!;uK0uvMlReGaNH)L@4e{18Zc^vM>w>Zun_K<9Nv{nM zhrp87I)b-tJED%+&qP^x%t>N&QD1QUluS^^6U8_QTFUMoB7hdAymNu~cyAu<$yP5o7kj;k6^9 zu{vH`x=xne6$=x~y!uxuaSwSfNn+XJ`D6ud;G5$fc$%Al1sYLpGK!R09y@Q31S4}Q zS1cCo*iy}R_=?YGUqi0@-7dn9kkdJ`IJDX!cEszQK|RH0WL2A#VF~Mb^v?>JorS>} zMPYDFlnA6_4-KEG?Prp|5*HpYI5&}c9nZz(RhN6sJaFrj#itmE`WZRXg(h>j!IbWG zazg|KMu({mtu+ZU_|MfBZzW-Q+ zFFtdffxN|%jY~rKk}y5?uP%uVw)iN+_5i88rURa-L(Ib@+<@*oo5DKxI z4z~Vx>ALYck1wF3LgqAywiQ119^hIgK)wz{Z4VYi>a`U3S55*YPq!b%`1p&!xf)PgHMa?$&$AO44 z52Q^8Jwyl^tktrXYCEKEH$x{5rXVOgtYnkYaTey(?=!QTAa~{7<`F!DZ~Kg9IWz9; zPdO5C`((NnSkO{Z4#7hPNq(#h2{sh z<-fjGIa&I@8cD_u7>*OZTM6oam(c)rn4qkN0dC+Df>>p*l*qIqIkg9zNH0#;!vVrGV8- z%-mJQA_^)JuR-gs#fgiLD*z|ub&*gid@tvaVRfNpbvv-DoQ$dbGarJT!Y6P27^$!9 zkyV73`QE8>6N3>Fs~T~INON37MYsD_1=dU06a-5wL8gg0d4LZuhTu;|)l19Sdq#W} ztC^T^Gcek>(juTt>BNk4(x=bP4VB z4{nXa+$w*#`#U9Y>3gxaUQVN6p88{?@%G;e@E-<{e|n@p*q3tk=*L}wf9j8URTqb0 z>~8SP8R}B&KJ<=}V!-*zvQdPMCTyUCWZHBzK_W)0AdrDx*-&DtuR#N_YPx567#7X| zlztfh`Xg)L^sUGCud2Ddr~7Xkg}9K3`IB6d!;Gy_YMrLAwj+BBm8F~5=&kzTn4=5j9Ly2R(7&cP3&e(m{vp=tvJiv=tTE!3y z-Vp)GGK$)RFL`lo!AZ6$pzCOCK$!6;xc7Ud0DDgu)+7Kk$G3`uImY4N^9w6$sILjZ zfH7aB&D5l#hKm@nQG_G6X$4pNmQfTiBU~6=R>Bh7$eQX_nU&V%{Vn24$MO+Ah;qlY z(j8OfLI_455311w%Zk9d!XsOLf&YZSSr|`}xU+1e+GTjRUH#o74eaL0S+WsHct;dP zEtx9Y$FG+;i6u6jF!X>lQIA=`y)eQm!-Y91ewVEY@-me_AQi_0jc{w^IrlxO~qz1mo9(^3ZeUCO7JB6F)n7+9=W z9-WKW>pPFmM)X@Bv27~H4G?$lkUIbXGI;M2zotk=@se=tY_W|!@9?J8_V4}6(y8+G zs)G`-j6PvSQ(Bu%C<>DAb^b0(q&0acW&TXxvhD`o26)hbL}VOYPGchlu6lU#be>H& zfQ#B$)eQW7Xbtfn_a_N07LDS+MyE<&H7Yr+7y?C7#%x;PoATmUX~bn2Bh3z@)uMV3 zhK0d%B41RoZ|(L_&JD!NL`;}Z$6Paa;1)|^?u&_<9ah5^U%co#tEH!4ygo-2y8a0FUlSef9(N+_rm=ZmT7?Zukg{?*wQsYHGDZ*C3fSbWxRwV)d4C@wW#Mr z>HzMg(7bXAyND<8poTw_RCqJUIYoDe)AzR_=}UJ@D~+pN>UB|3`MiPRv1@mlb6?oE zl%alOpS7e^2#?^oLp6+-=`{F)c!^qyVow65hc79h654=rdUjb}Co}T-tXY|sezdqU-M0aolu}&Penx!>7T@R?5+qY43Az&j_21rgv(U`0 zq3;bxlL-s&{zYXIKlC^>rNfhbD?~~d3j%$!Ece5KP=Q^{0m7>!ux?bonF&-pD~r7Y zgyqh{jn>eL>i=~7BdSO4NGtt^dntlhW~0U8Hv|1quq)c&Lpy-Y_m^Gz*kQ#%Wn*Zw zr}o_72g%PFiM~gV4w4%c>3J<#!D`!xNH;fs(I2$&#vC4?%u>J zX-eK}9uj7}?@oM@FYo*}`j;t8BM!5O&%WdEBtGPtxFl6uY%Z2!vG3Z$8y#Ct6E!P8 zh%?SVMVZ}_;yPfYt-#g9{n-k~guxekj+wO62?+%N+sMM}Wo{kwYQdLl$?vO2<>hI4 zD#~SxhfkZwQ%P+EG)DJ|fAB zfCcDeip9|(GmNvN$FH0DgPI;(&aSNNEi?daaCV;hOLjdHo`p`BTtu(LhL*z2zUk+W zTnz||!rzt1U^&IPXu_9{x({qQ;Y6DC27Le$?y4H}A)`ofdj@ZZ?ztAET89CWbe`8ikb+Krp{I^6*A-dD22^vH9}kaPwzuoJVvk z-0J)HN`T;n1~$c;bCjC{@xLm)hErS-oUf*5cl+$>o|J%Y6=+M6ww=kh#jTluOmlLq zB^J2gja62LX)fRNiA&B6c4>X5(mD8&@EP0iXT=jZjNz*#f+5$3aelGE+Mrj{!s7J~ z<~)c9TI+!#^>jQs8j-kxh?G&1p%x#{wW>`8Gj8~Ce`o&@s^J3~@ zQPK2*hmov>B$PZv^Y$U!Q0u3(F)+^DEq4eovcWjzcz{VIAlqO(6T@(Q<{!L`0FC34uqX>Uhn&QlL_t~>> z9ejB8R)1HCdk$Nh;Zk1-1AJ!63Qy!nvb_urG1&d2uB)(#jvK= z+`MTh;=hU9VCciRClL+b2H39knJA|{iBtdmE{b~PrJ;miTmWj)GmKHI;gnhVr{5GgsA>QBFB`-z)wkEpAu9UcLc&9mM zM^op$r~{XN5HA_{g~d(ah3;Lh#>GtX%Clu6f4Zfihf{_*Es5&);)B?JyO~%YAF2mw z?m+JbAr+Y57>_Utj(Ubuiu&Z0y@i*))8PojeAK=P(Rq!{b=~vp59nEEJg=3XR(c$k zTOH0_WMPPA%TJ`Y062-<{f7>kUpJT5sDUGV+CT_6WlE zAzwm8PD3)N^OvRCw??=I6hOZNACr^R<1} zUtb@SFrCbgF>cW*B=~|g?peDyCV~nM%6Fp_GNfs2?jfZj0T+AQGs^nQ0`ir)EjxDw zn_`NLiy7yx3Xs%CD$>a8snl(&mx$s1D&j_bf0Fz0XE7))BA9oLAJ`zN@M$$*i~Tvm zm$x)N|7}4DX21s1-7h0g*CHn*#dM6V4Sa@H{79j(UY&a{@(L9qZ&ZQ>m`;*8+uK12 z$SmUP?NPMSdv%%c#ciuTJ(duV_;}NT0j`Nq1(esjq7YS5cs_(pnr`Dc+~QEy(9FIW z6U6S$Jb<7lp8L~>os*!LA5D{yjXFnj{s7f$g*9QAhF>lKsQo}PfeAz8O`@>_A+g~U zSD3My%EMPZBvQbd+Y5(1p3sO}b#uTlItHFbH4C`L+}*lZJj=HiR`T+w%_H7`6MRD9 zKOi@of1I1tL4`N;Z=_vi#kllq7H<1TYt2dFQL5iBdThv|0z!Qs6;~p@cU_#3+#Q4e z1eH1sbgTk~j&^mYWCK{MPxbe!U0|FU! zSyhQYWfYc{GGOcFURPTSI$i?%Hnqhfsq~h!U>Vy(#a5o~)Hxo@v>2XYmHzvuE$iv; zQv3b7YKr-ok9h;v5jEHtxuy(&X^urD1}d}j6o(cCw{vr8#qbanYV82+p?l;!y)$y(${eYM6X71wIdTRXtoM36VYw~ZEB!L9+*t3_=s0p_6 zpZ2M}P|RJoknHL26m8J{Yq}~{`3RMv(G(q8is+-f8TW4 z#=h_B&!O$Cmwhr!Y!4e~S)I6D9F;ulaLoLpZ0Mr3rT^DDk#bJ(lF9ihO+oi=*cPfD z?rm`E$b@wq5m;o@(>UB+cnjh!bHn)C^tq75KZp;(a}06RFZQ}e!8*$(O_2|6*>X#O zuOwCUf<|y`mvSyX%N>zPp^risKfEm$YMdZ83Zmi#NOKYamx(Dz6isjp-1|T0-kO8tH2iV@jS&EV)Oc$DD{9L!n}006OC@$aAT zIckKZ0^O{g3w8sR2$4Z$s#9SnqAVWqqhiUhCq}BP6>=co?eC<(Bd>M$9!>Y>xYatb;J`e-gJt*a{8q z_8Heu2<9N%`$&x6_;zX7e#it`HYAoA#261QqH8qIf*fW)3fZ#=2lQyGn~|klZ?($I zcgV~=iSFCKdlMX>0-zlgE&59A5-eD3wi_h*C1~KLPcRimT2He_(e8`nnhY3W1P#u$ z4KxUmc0u!5ZPb1K^z(h(H$RA020ak1p$ll?C3clS&aDjq&lq3k|>7x zm#$x06Ka4W>{P}E7L;3wA1d3*xN$jr22w(dZgt$2DyXWxdNUPqQd+sFBhHuX0|>?3 zF_O(`CO82*`hPSc);b$?;O26N(ZDnk+D?;Ur$-#(T8}G9=r|}xY(CtW*z7uC@EVG!#Ye-(qgcs-?5R$xgUtsU7kXxv8Zsu$*(v*etKvG?C8r%Cw zRoa`Cl{`-U3J|0UkhDImdo9{UJl_DIW((_y6R<#au?35|mQUWOLBXNjf-fmYY4A=c zTP&|>GSD6VQo=hCo0gA3mfwhH+#_E02AzwBVum7QC9^Db_@lM!3z$f%lC08taE$_1 zEz(4b>VgnklyjGf-TFsKJ}XjFAv~F9k_#ZWeuEOlR*$xztX3fd0bs%c*TQ6Oa7NzZ zLY_K^Ft~PrYDole**{r?LF^VbXjT>tR&mEmwt}RHJTc;+(L1NaGDfPKTp5G{m_hAg zl8ITw26*Y#)y@`BmCLZk#1^2;^@ZCBmc$i@<`!)H*$TC)*yR1F<7xyeD(Hq5o65(z z%VTOuhnaBcF&4@_1=$&mbR)xV9rnf1%88j0hbouPAcZjMflMuJGLXpYqM<^iDJc#r z9kyCWT|4_3xmb%x_OIsUVWU*2GvW}C1K!~81Y5u{u3#}aTA*r2@EMPb0ynh#?(#1| zej|!Cl?IJzcsoR*D*qI0-uZ?284}umfi{#`?4qIz08q5q8XEQZ=3k7XaU^=2I}625 z!7)@2jSs*44z9?zi9-_tQ`X~Hkz?AmE90D&2w_WQk#Zpr09id8HhNb}B z!nXzViU(Zavy4cP`6y!Fw`|kLXcGXiR3bx_NQmt{M~x|^p2YAGtIj}|+2sflRv+MS znePnC@)1~;>@3VEaak5Lp3yH^^eVH`V1Ni~Rygm2gT`m%osslviDh3wtIbD+wOV-Y z1F15*Zpj8*_arQR5|Rmuw}e2$q0oqHXhhO+kb?$=$BZ3EOZnJfUuzQATQ0!A&vZUS zhKU96I2{NNg^pBrE&x*cj)a%Mti6Mvm*0-$t56HiL31>?dmd3aSx9FI{-p)E#~<>^ zGe?R$=j7ANnXV=zgonP7yt&=~G_p#G{`ELuC*69x3VvH0X-EwhGr=fIVOA=`gt>MNVgmL0BD6w{8@aJ@vTDW7KqyJm&~gM> z36b`-1h#psG|E2@iyaIq)J{I^l5q^$WMt=b8(_E9rr6PH&g31Evu zK7?BV&C>m}Aa!rw4+2f_%kI>RbEvpaw&%*>a?^iL{;z#~LLo>40HPB4zSNrwAlNEo z(haZGZL634Aa<%nsrn;+iO4e+D`8S70DzgHFPp%KL=q{C8-Gy6pEU?qt6 zV4O(F#kC-I(r+24bF{WLFaTExTpsU)ngv#=zlev9VC$Ji1q&{y9pHpHcyy({`Y75-`v#`>GO(U3(xXKhn0S=&}_o;wQ8 zR?YVJh(hA15n&@2NV4gbTE z_&S*pbDib;fWQFf1%F_3Xx7aBdoO!q{U*B0Xh%#h(iTb=sR}l}JTSbH+-$jL*kSNV z!RIFqgRqx70x_@=kB9e`tR4}4cHa4LC;12QUbgwvrxmE`Q%w~cH~#HfT4nBdhq1x* z97@B+)|OamyFI62JKBg*wF>;5lC)A z$y|Nq>afE&7y^?Pn5qXC0N|6KYYs!mpU2Kn*?Y48LXgyKgB}~AML4SvzO00xWb|>e z5Fv>^o^js5#H^65pE(nXNV+w88df@ycaPC5!hwxe!b$jO)jc%3o@?Mj+E(p01O}{dV z=p|e7?(3DNV?t9IL_p7m4k|A$A7mMu^j>eI=S&cnzkdNG?SApP?D6Fz>1Uyl*XVWn z?Jv&;qymTyeB48^H`NZ8!DOz9+nt{Xxhm0{1!LT)F@qL;Xrc@t^OliV(BMigm#<@`1Fm>$q%GcGQ0VL;K4FD4?@GWrqRFLs*(HyKY zHX+~D5`y9XGBY=XLwJ6#_@zF-U+XP$y3&(-=!>x;aS+C|BU52`sQVGgX6I+tlAVlB z?@)UVDPfkS(=1vGE$hXlCmH|adq#hMLp%6lx4E6$!Oc4v74{>_m)~3pe*E>NJMrqb z!?Q;Suo3=e=QYhgikg3}g#X-i`CH+MpKE7PcMvBDag#1B6WeEvl&v2To%#v)X5>Up zzA4z4vwkxFl&`pZc@phQ(RzE6*9@>57MnCDR0R{LV2L_vVhv>UzJ>gPx@CDLE#Toc ziq)%5xuNGX-?OrRoi`sNPGC~sr0nZY9`Cj&1t0%d|Ekx|jF`L{b9nt1(JJ{LTmKce z&D@Oh*H)=zW^05E-Ok+mo#1`lv3H`Z$GbDp!I=3oi?dx)w_)0G4~BVO%2$Ns*Hq`& zsedcW&w2F{A@u zYA5st9th{|gL9N$Bdn zVwpgNA%~{XQ0h$lm{b%k{^Iz=lKWO=T9G{vC$BHkNgmBun;#QRula1e{Y=xvw7^9P zI({S!S_^~Q;8ZdK+!!W7oE$JCF`>}pwQswgwTSN?o zceIqmnl8m8EdYgrfcX*^f61af$Lod>{^!WzEJ>bP1eNiyvF(0k$F<6$y|9jV%+eYa$ws>RjmPw9`Q3I zm7oBJ5qfJL;q-r!WmZ!@*A;;WHT%fSUs-U+ztk|=oBNbaCUoM^y)zv*OIkO5GMX-E$tM&<&3XU zpIff5^6l?ZQ$16Bu4`n|>jo-BUrOq}%1$!Qb-y7_l~mkeZqHVVxK)ZJMJyDHnvDeO zUvj>+m5K0g9E)M?QAB|_A?WT);g}~aL2)C=Z{G8YS*XYm4JArKv9}DhlDBPE;s+T zF^Lw9*-#%vJ)l-n`J;|C1CrmCRWS)Ya14$!Kqr%&i>T2yOWq?v0}U2mROX!ZtIx*R zUbl&XkIC$af|0V!=nMgDYm^xO1!w`=@5TjCuQfr1@Im#RxWGwy`Z{q}`}a?dhuPOS z)O61|DKKRv=IsZS*#GifqbgB>m{0Z zr7)TYeF4;32_hjFF^@`;iKf{Frjj_pOp<9yMdk*BzK_FgX@Q^pK;6RQWY1L1m(HwC zc>1F9{N#lb#kpD7gTA@ZbbTxTE(f1g)Qf08?E2&`+oVNTysm-+CO{wNrRo2T+}j#4 zo7Hb?=;^O%&BnHPWuvUp*jKB2%OsdgXm?eA#g3GYV&fDGDgq+sq}uTKI&g>SJxBz&Oi>JLttoqW)q2!iV6y={_m2x~i}|0nwNHNSeRlS{*sJ4z zt4%`sHz25uTvMlbV#TA#LLG7`N9S1gvez)imk@UdE}M59v>65(@h4>4KNa_FPa!49 zsgF|cO1lsH|w+EY*byJ#%=sz($J*Q;}-J7edE^U@v| zior-77kPm%Uu_JaTb;e`(~^E_XUtJa&gcpUG%1ebvkCh8wrjp#@|jh)74rTQ_ywhFjwVZ^;J&X!IVQ2XGnVnTEYA8`WXzTkUl3k{Y83Ma>#$o zRr)1Wa$n*${hK?s{C>?x0 z^Ki6T15x;FF?jd?zl8(A+RapHe5@wpoWtg!>wo6Hnv7CuJ(kYlNTVMuTTo{MFW zS#15$Ys%g&>j&W6h_SW5mt7e0v?dc9l_22YZ1TUS#pnN_20J)LUqxHrw3!hc0GGhL z9Q44)8->dvSz>}Q8V_HhWkkNrGc_SeERO3WGqqeHB2RXVs=Ft{z@8ekfZhGlNc`r7 zBn#>!M|u|HFb!4{+2G|1CD2uxC6}nzEGwp3&N~3ZqB8fR)w2`!wcHAr%jVfVP3_CO z)6{|9x*K_7L01;FWtMwd{A|nW+A{&3T`cKAMrIyZb4;PC09R@aX_VhjKpr9DHv9A)- zE6)9))nsa2_sX|v4xgulu3G{SZr}DjyddS1%v?oT)v?skwslZsr%Hz@xAs_mc^Be` zcz#VKkmnlzjV1S5r0AP1){zhvjyyRLK zS_d~@tmsqAPg$F-oI-m91DHvRD3Vw{Rch>8`TX%JX7ZAV0ynMN*Y~XY$ZbGu0^5*& zSX6I(vCA_lCG#mI&S|i$ZmEsFDq!-$`7qhG`tpJ|CFjWv9x%B`0TSw^9+QN5`UK}#?eS{Vy~mHr9|W5Dg0 z=Bdu+n0mPCj-5r1thK1i9guuYC*?rWUvTN75f=Kb++#qDuXj*|n$J)2@4Z(3EF?&t z2KY=m8s4{9(RyF!9Wik9GHQ=ZrWL0`(qUh#LF$DPig|_>wX$gMS`J7N(l|x3)hRH5 zEMo>sD}phuLb;Y${Ow%am>GmbkAdcGXKgwv+mQqp3P^ zA_h^+5Zmx;Sa4_LuRWMxM{XKAU$eJZjIhdA%q=_hH$vrCBqk?aPWt~E4;DFSAw`P{tot*cm-wjs&j3$wW?>=2h!}b=7WeO*3 zyYRe7FU}Z6uWL$PFx)guH{WH`T&3|tr;%Qn>?_v6$+=SM&@sc>GE6|Q?5!pc$=`2C z*mpWw(Z@TokSabAf?2dWh{+^2(Q6m$XZL@Uw?n4kztu7!yF}e1>XA~)6bzNJjA!~; zin-bOcd?L}*_41$OV$iqqJ&I(hC8EU!Ug?#k3{38v*z4Itj6>|2R?b|g^dd^k}<*Sd?_30}dq2$pl?uotF8hggA; zGP5bmAkb|5ejzFJGRM-Z2!OP6Pk}dJ6u{s)ZtW{D$-dL_^_H*m62+5Sk}c7GHz!Nd zE&i6NcboOJJ*;dVfVHVO-<~Hs8g4xg zQT*oaW2pevi+v^-MA!gu;Q(-)st9?`N@Qa&!Oin>Z=?$4!Axlw44{n2ujprgI!<;g z&_t%mk4>Vjgc9uJv(uetK4oHKXFJZONQV}eId#lC-y6VH$cEM)Ozt)r)1&|bFk%vY zDNJq&iDO;ll`8wcmMS9#tPFk7Zcp1Q{{$;mXIt+tI8n!!F3X@r{v!CQN^%6lr^BJv z*%*rAUU0Bf#Fn)OgL5brAqv@kwYH>4Znp&R-qiYCTuD!o^C_T_f-==1^K)%kIHX4! zl7Zn=y0GNbe%U}pc0L&CUd>IH0~aI~zE)qwZ2U!OP1MuvF$$K+eNlb+&}!Q_YMVR7 zk7t2(-ywx57u#)Uf%H`iLiR+9hx~fFO?XL0xhh30-dd%^wAde4N$=JdzxHMM4LADQ zKPwh$X1AeoAQN(LjFf7mtgrnSEc-J`ODm&bjDRBpX`zIP7esP|RC(Gz9!A5VKtaoG zGBN5#|4w+B#E4t_RQhOA5@v)OfHH+AdG%Jyof6*JhBHq>F(y=mRI@kzJD)aLa()|N z!o1?wp}@poBKHo-dXD#eaFJ6wn9Iu4Q*X9bT?;OflGxz#C`{c743Q6j9pVT8z6gU% zn|^{w5TluJQ`Vag^~IjKDYmUTCv3xmWYDqrb-T`_L_yKKUt(N{7c98`RDy^V$g^)d z_!k*-BJh06Hk08iGWO-GSVXA2a{)zC^Vvepzfi_6o;~`uvkCK&dLcI6w7)&w`!_m{ zg7pAoo*e5zX!rS0k7%seW(s{frWQ#71Um=@8rV|5KfXi8#K?L+i`}j~HkeN|Z7Jh= zv&CG68VIblop-{mDthNZKZYizBo_@A?!mUiCT>L1LmWfi-4Ny8XzQw@SD_G1)m?Ls zqu)q;l*qFd<*PA@oYZ7nT_VC`J^gWA$ z6=bG4UHlO>^o$I`av^ZO&kd0<5IJx3q99mX|0{ARw+yH%-@--smii;FtC%l*YY_NsXt*C~5&Jr^M)6 zp!R;}F773zq*%+@)V7ape*yZVL-k^dTm>2^p7nNb;#zxCEj-_|GdbQy_`Y&!@S@2P zaTs)<@0m?uo?WyqSN-mdmSR=XgL>dm9LTU(k_iv#(fccaq$?IKR2b2ak&Fe*C09K+ zSg+lySd6jCd2Quv@CJO$gL2?Hq8GAE8IQ(`-${=B-Q}O zFBwp`L96WSKvcpx}Y{~)Hu)qn_w+`G#m3xguJHq8SUSK^2d zEdRd#x(2|l_xYqhrHPzpVy|41TRhq97_af&AhJ7~z192*L9v!o?06HJC6{`gk0FHxk%z@UA0SQE zbXT!x`HL(upgs8je~wZtPN!c6$`*m?trd`UkGdJ?`qNgiCvb*JvfBZB{U-8jYSL|c zlRp#anz=~EaI(wv8BE;re&IWIxOp|s#JQgzvZsrsMTX4# zHh*={iEm@BM`@Ym=gpjgwQCt2yt$%iS0K)CSU8Ig1m*UGmWZHR$S0D^D zzs+9r2fRfKW@caG-U)oY#Lli?kF*B?{$nc;*}@GroH)8lIxKYF6S(sPO^!01t(RoT z>@0c7c#`UNVfaG~F`NH=*k)_6DedL^ilqI0lKYN&4He81xE(<=}N01TT=Jy%e z2aIJAonu6LxCF#Bgo>Q_1oIUfvdKKoOG8UQJ`-=Ph z|J8Nge@XTK<38-ek^|yifO{3LG_?f8k(uGlHjYZI47FiXP*gBWaAjqMW~F6?wrqo% zX<1=eS=k+$m5sX_n^qfU&s9hNEB|}6i-x$@M!vRZtC`AV zzK7S>cT|9t05GI94ocrd8#ptZX@7~Uu8A#XUW-5416fbpAX{su(ljC4lLMhIWdAX- zY3T&pYHyFs^|1m$QL(V88YCqELgIU0vc_Xk8~gW!lC2}@=7q)Mkw}}(j8N)2j&sR6 znr*>+DR8j}i0e$Ad%a21=6j9^VV|M>Y`N7Q-dp;@Ij8rJpdJF(jzHjXA^CeaRG_)~SYqv{QWVZtV zF9oL*;WOcq4u8nZV1xqsYT&>(5{m}I>L+{rD+4Z>Ky(;X$8qe0YQvPN_Q;N&*l86u zexlRlY9{D6+!iWLPc2W%QCU2QzgpZ4+bxt8c^sh`Dmr8MBI`&>nWodc8bGYm-QPi> z;e)((rywrFVP}{!n`!Omu(es9pXu zi64d|#$>FcQC~z7DNwOb-6@3YZpG4RdPRP>LlI$rm8|Cp3~-^j8?i$%!Mey2UN#M1 zw-B&A2S!#4F##QGsyg_)1JXwc>AFK1KohV!N=ThQ*?mMz!KV8PJ+R}#glI3qYL=Xi z8-hZE6^ZK_2kCotdz1BSw2d-zCRtXjn;yBw^B!xE36Xf##`ANODtr!9WZEu(T`ehb zv^H$VPRB~SPhzqWzmR5=x{_7bM62|YIxpxMRN(G6%JQY9rYOuOdE?usDBSU{I|y}mIw1#4a_WYk<#6?x6D~8if<&_nDjToa_<+? ztHm(tF~ia^;kHcTl12*)_{jQF>=(JCli)$8#h6h{{l_$z^#k!aId_0cB?}w3q?`Wm zZGWj>x7mnQB*PT8Qkt$ZNf6%h;P6|Jvj6P*jl;Ve1wvc^hZL*%%-VbRNdcFA9(uXA zG58C~r-OKG;eZNh{t#R|x*9Fe#ILc01Gr`H?Dk}Ejm5VR=LBNM(LWK`dV8|32hudN z?nC%zT1rar0#q!B>M>;yyuYo->iMCFLdyQr;;`!lnRi3hwH@1X=OK|H0Y)8i_k)pS zJ*im5JwMmIx^r&BLMwTc<*-I|ut{ZJ4?M8bJ}p_>3^>h^C=hi@Xu+!e(TI7howb7U zf^HgOmG1tVPF=;aX5lL9=B^O5<9xg55d`wvFgH+8SP-yzuujG{&o6=g=A_RBjd2JT z^H>4R9=7dw(aKL^s`oO*W9I%_w5ziy(q0&Uxqwwcn>aCnOhwmkUO}@R5wD`;(p&~s z5)C<2;dgVGx4}ObF4&(NIOaWV^LRyb@(Bq+yfq+kJ|)v1f;Deh$rcn_)hYMgt*N8O zP7b*l_O}zgwnO#DyY4A!7go0}pj0YU@Fh67xk_;uAM0Ib1~`?$r;ho#@UlaBwwwH;w!y**4uGW(JBa5pIE=7J5#Y&&Sa?z_>un z{AVoVxC9xx`;A0hXmYdT-3 z_tVTPnE?v>ymp#~cz2N+nE$KqL$d%y~UT@nOes9YLOe3=2XKY1| zX=rNcE7{E7zm2QQveLD&Vry$VN zoc;afpHeH!LR9J4V|!-{QP8@)jl>v`JcR=b2FcIr;$^2BFWvkrYt~FZ>o3xQhwq%5Ya3oa+^?{h_e90gPC& zJevmxg%KBPX%95;BMNK@AN2f*NxTR#lwRCMHzRd4iwUhzAomUUy?f()c!IP_vsDja z&pm{k&@>-!$Q3|d4Tw8^G3Z9x28IG*`-AlC4aP^v6w+2~pujWat9s%b2r06ic>$2Jt=F)6A{T9x+^*`nc2%F+SzEH<@827bTf2v?fVXoK3{WaD#CNvv$MSYuZi zJY3-w&}-8rnD^=;bcl&C*vhgdh&PIYWW|`*Lr{)}u*!5MLd{>M4z5;0h9W0%EPw73 zM|NlKcC`~xFAq23BMgczan}N=59Fycwy86F&LkM_J4?1#FF#lJ2FOw)2Y9H=+fc8U zBY|l*>Oi{7TlUu&g4m?NH_M=>C$=FPP3#j0{p&{ENWsY zu=p82NxlkC78&^3VEUEihvscRJPL%vs$D?f?pEX7}JB3;H`h9*YWC#(` zt-gz_)g&HZ60CJ7Gz~5X59n2!ibR!WWHN_|G1CIiW*d}INOD)Mx39s_gGCGoZX6?! z=^=qEP*<`TO1Q4UH}dc|DKipF;6sz!JiVd57Eg~9<82DeCKe3KN66_* z{!G+x8dfZn<3BPeJROO}#I*ZHoY0zPcN%DvV+ef%ERQz#tGQ#T5N%Nn-*p8#raf^t z8JVI*?-EeIoZBpDf~U=focXW?hUAS*{I|z%?nMmiCzl{dx~nEHXO$2~Ot&|{Q7x!7 z7ej%IK?D&pf@feyO&vF4*sO}B&yhdn0cV9l>9XZ>zLEZsg|AVfub+eRgeA9t*@KTm zc*+yE>ks^=2qXoG#~ z5EIGaI7cXF&Qt;Q2!PC-Wq_XE%5n<~m(rE>$C#@g5BcYjGk~S#Q5nS<=&o++DSoPp&`u%u#x^y29#t?Uymm@>XcB*(LFh{ z!6iEI5rBU74ZSN6S)oOsHg6&3Y|egdUI548g)*N$$VY{6WTr7-$Ocx80Wst8f#wp- zfMY8QyVf8SJ0u{7C=iPTC{I5*?dVn`HonI|Bqk{Jic+o?kT93Osg0scWc~IQA9KBE z$6*y2g53r$9u4jqN@-xuMC1T?1$4S0-ARM~bOowohVK+EUJUCPBO5EtEgRB-M`0iv zV%Q1~;h`eSUqOa?dYba8@7t8)40@CWb(IixH{ZAC3wjA37K#9`$UBbR^+d!Lc*!je zj1(I3YtXg>*27(LF3k>IWy)m%8@dl5=tG@ac(zw=&Ln9kvz-3PWZ6afi%P%|&7nqhL%O^716UKlxtk1>*??zofpiBT8$818Ym z-mh)M5wmn&jW!RXzIAej05Kz3fz7;Zq_%Pr!v0I0KlX+6cA)Z^`92YCewm)IvKI2{ z^^w-jay-Oxu73>q3+64gM}O;`w||py{n&C2-y{#hkvdMgLFwP$W#C1?eQLHe4fP6> zO+N?ir-h{niG&fbf#n>^?9)T6vi@5}6I9P!R`>j<>L4jmzGi?!J80;7H>#k|%H=hzlBE>F9#h=aeA`f# z^H~#2wzB^w5He$p!xkp`<`+^b--HuqOw75yG!UZxiP^<7a^s6TUt>HXE`{<5u>kXG zhJCht?E>MdPQ@J}EY+&VD#2qGBE@YJ3%)>sjM4!ux}rk;ox6!FFy|NP%%|x6#X({o zb`z~9TZlhKJJ9zFeU_ERAhS1w<~|;Td=yq@B5K4s%{)q|B*ZS#YtWUG zt+zz0+=3w!z~EGcUD5`*LCA9&M4$jR%L8*lAaGg@33bR!O&UEAUNYYX0fVhgH09Dv z?D&W{72vKyo#Bud@u|h%NmCz>7`srMCxL*=wyr+Uru$}|Zl|FXz_k%zo%gebrg(-5 zNmC&$#*P>)6coc7i9q%c{Q@_jo)Jt&r!iS=Ntf>#HmuGQJl0=dZfY4!d0aUh)9L5H zUiwpAKmCh&KCElUROivoVY6@Qp_ecJ(f@s^wfSds`T5t&|9QLfpK;HBR;nRmz5vkG zShBV)FWO@e2b!sN$a8=TuD<=7)AX|Wgn_KOM5lNsYWb64-bf{dPI-P0l8&!EpuzLf z(JT1yhl7scx#&^k*^y7s1{MBOUOh2|FspLg*WV=cCC6l?jc69M7eI%%nT_P4pVtiP z6<~q_E}D$`a0kkF#bgW1@FFO982X2AH18DA$sgy$!=80OmkwZGy)lbq6BAj8W%XD= zA2eH&_RE!A_ON+FI?6c!nv{%IyzyTdJ$oe&Nm2RS(NRxKk*=0U8YI1!dDtr1PFKZp zS6vHXz}ZLVHMSWX2wR}jYjL;u;#VwUt;#`HEamXA-97$EtFaYK&`E}!HPNdFR_?`M z_ijdq!`$d)#0$S}5PG)WZmuTMkrH3j&!|-V#FLGOFI{id==m5T3Oip%pfsTKjF4%3 z<%?_3V~DU;XW1T!MwB6wMlic*Z|>ee&xk;0>AWHoZ!lLOFtx}97Lv%yzBV9UDqp*? z#IK*W4X5#&s>Lo;q&q$FhkDQY;P)Jat_AzgnDOp?r~);|eS6Hmz6;fJ6VdO_`a*8X zPd(>@Y9Vlrz}kuA5yKb@Vd0k5QZ;M6E}vNhwQD}h$k(*b?NgPskktJ&1b0~?-dB+AtOSBLRIlv54su!$i-3Cm-nBV^; zT!eSsgL40nQbtZ*SAQ^uQ5)41st`4!**+!FbiJlh!Oed@0&91qM)Ij9OxxkAn+7P* zO64t4IdO%DE~t@@nh_CqqT6zfhwX=nFt>HOu++z}dF{|Tp3{3Pi+#q!-Vv<-e(u87 z^RjZ9_O01LXfr#z&%%2+uK$TE-!bo<;q0c^vG02Qk~gqDnRd8|7yAo_J=!R@7uYf= z>Lr_o_O~U3m_|K*c=LDHOS5X#)p@r4o^B@}_*tb+^|d|w9S_)l|E?GT&^l2|G7zq( zx6#|p7=UAU$jyiU*{2=7of=MAbI-=@Vdu(gq~2ayZSKTsYcI*GnAg>L!lBNS38Lj2RPJ?lyf?ma;kaO;k#!vsgK2ZeD=eRwc`g1hg-deim{`svmgKww6>C=$cfa6h7wue`He2w~roN zPE&4rQz*^Pb?}~WU(_OfJ-zAgrO&I~QEu)JJC;;eaU1llgLTP{uT75`uk*`fc)foG zfB6IV@k+xP?kfFB(6Pwj7Cd~u#og_M_DdJ-J39_td7pKqpc3xU z7*@A7rV;st5`OCCG(EcG{7NpjJyUS0?!TN$G z>CCx4pZ-Mn%%5w0k8KD(r5m|9G#+nn5r6Eid^uw%vymu#_^;jZgw(Mkp)|RRu>ARm z*zzK;y1B$i|EmSJNLY821UVe&A}?}NE66h~$YG3t?Y;IT-Ii*5@*dSdiGKgn6@S(g z7oaCnjFwNoFMn7N!yB^$qw8F#bDY{$Mb}cnuDx`zG}TA`Tj;ni==d}K-Ez}r!7)<6 zALla1HcJLbb9gv<#2r3$doSbLCvSV!>Z#EqLs!HRjTweBoc#rrvfe!fQ@S+5@>#)v z5u@aBXN94X?$S_hKAYP(awb29fm-K4FXhb`S48dUuNyDv>zrCsUXY7LNmhn+Y&u8d zU{^IJ=wYAitd+@~^QGRp4@W$b3vXL*khL5aNv(6ZjM-Cl_OkNL+g=}uO8~j6r0o(H z(G|D*&0_^$mSh^VzuoW)1>M%B$rhh7MvnB&6#$The*gKZm~B}Iy%2+x_4Y^uTbftZ zW=zaaO7o5C0tZh9hwi7JHy2l!SwndYbi!!EczJg1&%) zi=@=@wyZhqX~iL?MOXW+=yfji61itFV-lr3hsSXsOAb=O4|jA&=*{3L(PsL6-e>6TaqYRc|VR^-jB=v1W$nHol2ROh6v@?>SAYmxlY~=i*6Qhk5^YZW+kVB z{o9MayX6^2(HQnGCYFUyKSqC87R7MONDi-+?>;!p)iaWm@c(=%UQxy4b|~#jBS=TG z4TusOKRob|nOc@O-bih!e9RS7kOw|_xTB|T!+M-64Bitvij;+PYR9Xkj-|EOhasyu zR41z3He#xT>5@~EstPlIL>^wL;1hJN#h{HVK>wz|(E2kO0K~#ylNB+mRDN(9~PVME0^bhc8K&5Qd8Sn5e2FW8SG;|xn?7R?#y^NyzJ;>=D})T<3liH(+7v;--JeUsv|A3 z^%}=;hf_14{7%}K7Hv;R39N_URFAuO>c+&Cb+K6TE}3CV^P}B-ep%f*1z=qVTs!;g z&~NDBi@LEj{v*w*D9y%WlGCF?_6Pj3)<35Tb^v}eYkQGR(4~&H&bg%qTf_-%V~WQ} zlp$ig1$R7!#k^&_VsR!ej%JT9iswf1hNA!#O1e=*z0SO4n|0lHqj1W5{HfTEg>QxcDxn15*h$zSL zd7X4>3P!43bhY|;>hTt_YdjM^=y5?=)6Zp`0t;2={SR#*(4h(!;!>Apx&8_*73=}Y zSS1r?_nENnOv9`Fa}0_VO$vuXMtXAIJJ0TIIP?V3O)^CzSDbCaB?gq-5?uWJu|>SJ zvNBFJ4a1|(IurqQ1fkQz0p}?W!)W;@?q}H(moWhE`VS?`e3c!rQOI1~D_W)>I4s?< z2HFJ{t2`Qo$l*qQa%?lxZA3cB-u_as32^B@*6yNkfimlQn>$sa1+D2-6sd^NDHNG) z;t*?_FLN6q)beuCTto(o3s;rQC%iVEc>NTpNSR>=6gC+Tbh;r!$4m4@Up?9|^xx!C zr$-U6p2xkCvTgjGv$(0$2lqaY)k@qHZ51*Te=0fVIQR}yd1_gjLRQ} zWI2*y=NJWwkFXmH1Eib9qEE|i1l4zaa_xUL1ypuyphMaM_QaHi0}Ma@b! z-J=mM^)liw!7_L^1sztkETnzDjmv=>(Xev7_J(~sO{%e#2Dlvn0Ze3B9J4G&2wyz= zs3ENE=7L(T5#Am>XrmRG4~a0K@Z92Ba|b+%MciSj5cf%zv0$U-(sZaajZd^uZV;X_U0Gf3j{_XnpD}J_&@{jhyToq<3uenADVCxD??|_%gFwCc;2=X4jVNus(kEGCRQ1UNL{QHh1 zyUsg^W!4fW4QvI;d~vvTA1G{+3bDa5!>2`j6zvYuTzNfNx}4|I(d%U`FrC^8hcA>y{Tf`0-e$;6A;6thSOgRihPbfUMvD$#1o_m2P&Ilyr7aH7eG3kbvl`6gnA+p}{uZBP#x3+iLh>W`M9raRS;T?0L$u&>?9&ncR9(b|R*0oKH zkE}ZrX>zC!oW2SVyMx+4C^;n|IX#wW_k$co$(dd%D*zP&A`d%Q9~(5PX%+$bc8PTe z)b+lGXJtu|qR$-{o<6@_qbWNhtJyG#wkOYWNyU1#6qZ5#hTF0_jykiYt`9P11IDm} z;DIaf3>m_;s)#J>9N_hq11EsqLUul~W+H&AcmSnWDRjrcRP32Qr5lx$GXX^j*uppn zJ{>s!KPkgem{_AhOBE!KW-Pky;9H5^X$Z~XN#b=P-SDkSPK+QI7&I4iCQEOzFP3MR zbJy|`5~OZ}hqnqjsphQ(8eoyi2+ur0#9HbLtH5+1WI=M_Q0A3WI{3`w+A|tlM<0B) z#EHnc|*;*0D1!)U;{DBiP(~bX?z~LE}-aRYxqn}*$T>5ti&(Q_> zL3N#dZA;Btmo=Lg^JhpWg)3;NO^-l9rzna)Xj>;bqi{u;i{hdFrZ}lvFCzv}`;V#< zx{y7iEU#Y#KTSD;cep?Z-_8o%qlnLX6Lam5#7=jW-b?*gDluJ!Egmmk2vlC{02_6P z*^vlVbLWpVoJph!2deVE9t5Wu2E4pC9q9kA!goqbj0NJ!^LB$B&khcioTBwhbsm?1 z8Rd}4<6l?lvSdqRY;k^}p@k?S@DX0Ihmp;7PD)aEy&DM<#gB_KC58Twac4B>B`u5- z^t-i)yywu5D=kz^#tyJFL)OwUVkyl)I1Me@!(^;KS!q@;8GfcF{VvVrE$mq$HDaFf zI{+gOVGq9t99_g*jj_Bh-Z9IquQ0MBzsP!Ye;@&JEpN#jyCE4VPCB;adYqR##fw9Mjlx69(yh8yV=amf8od;+lqyOqb*J19-^stFk!b4+TuS~U6Nn{; z5FI6M=eMC8+Xos_U~A-o$D3CyxFA0M;q;?4>`!^Z&29Jx+w2Af!dY5O)&CEl(wjV+ zIIF#h1_*Bd!>77RiDrIwnyo(8tm>Z^Uf$2& z+IxalQnl3BWaFcEETE(a?imdMs-$N8tDc=rw}1uB^UOWJ37%@{lw?3KHdzr?PVruc z%~$E}g~SwO!QDVEYDM+pJ%8gHj?x|#uds7koDs@q-_p<3dUY(x5njUOQMtat24)J* zVx~tNVM= zUxh41x)3aCHJ7YI87|dqwvk7ZgsuLD84j^+KHp=BJM?5}=!w6^EGc!B3}zUP3?Q)% z9y!+d;>a;_S$OkAg;(VNoMm)ox-f{ggc4?D8X$Wer}yAN@PJ)fKD)j?C)~tk?x2(t zWBRQ~fMRE8Oi{Dow( zVfyI2HqJcC?VQ}$+txnrz`vAg;bcZ1kuP|S`hZdt$L z({$SXkjuw;n*G;@k$$|G&+8_5j!FS!ufWk3A@5*rVw@8!p?)(qlAZC=olj+aQ&QgE~6$Oo8= z^-3>mIjBOfpLl?tAdok_(~q708o$lFTK2$idDUX}%KBza&*eBY$7q2=AwTE#vE;jI zR51^oHp}7sUgj&7p3UAKN-gpvzG;krP>};8hdIK6<`0CJlcDCRto`i#1$g&uogt>@ z6=7Nt3a5r)b7@7Ce0nTV7sm$Mth0DWcOCl55`x903n%-i&vMG_;>fTyQz_eHb#_p| z&x>$EVWQr=o2K8ZZ6OD|v5Vx+KdVvY=})xX~|7$YwH?!8|6^gSRfXtHj8Je|Mneqak->`eySn zHIbQr&&jUxQUUNEW`j!!I*QsDpawUi$i!RgRztSY6^wQt32Dp%x8)EQ7J1pu)D$MA z&{QZ7fc1Emg@`2RhV+rdP+U4i!e>nK+2MasuoTnQ)ROEn;&`&2w_O4DBPx63av6{$o1 zt{`W063T_h@ZO&ljse0NO&0=8zXVShq7%}zs1CxYcv%YoK6dM!;qt|lJ^mJgyb%$H z`=I~++MDK@i|tk$C?vzwXd~?ie|6C#XX^&?KKCK@#@CBf`qARL-&nxqtq$ZwzxF0z z;U>Ao_y5QTXeUylJ%*g`p5GRZFl#O{g!N8dn6f`AitH-v7hJn4V0k*|3afq3L$7b_ z*+_^LexbHs|97#1^koN^MAN|zCyiMQTA)w>OYGp#(4ucvh44AeXHB|Kqx!^F%s9}I zszl(-5hG6H2{Wq|$c=`QAj|3Tus;7~}t9rV%{jusEIQAQh!-IGL( zrwyt~FB$tq9UK_WW@2V{!R#FPh?xzhm^JT}>_}Mbk8F~`Wu=zFAw@y;q}^c`qo_*e z!8zQ~<&Ni;vM;8Z>_O3~NP!A&grS#H-|G=Z0@YHAu*iBJaPX;blgLU|C-ZYF*1a?N_ z#N=%uuJhT>B-iI&_tePeNmY@b)??~egtwFmoLj{i9*~co^&`YcXtY9`!&;TUa=GIqssD*JY4uP*RFke^CbgCJz$en0 zwGLuBumZ7t%W-4Bl0N#eR-GF_B1{!|TXS}wkSC*NmGGcdX5D0tyM%1&jTF56g@QM? z-(}JR2d+Q9NV0ex;=+XQ8~9AL60DI1AHDE#*1cM`AS`k6$G%gxXKpnqpY4x3|6iR; zw*`8lh%}gNR$Q+(j|I1$|CvNq73DY74(Dy=)MtF@@Njtn-tzuB}> z)@Ip_+i0m-IJx_vTf&A_9mNtS{X~xovJ>NEUIric&RK5(e^PnTc0orDI_A2BWn``n z*~{v7Y~`b#x`X<;HkFf!Cm#D9%6}7J#wcGlg&^WaerGtWfq0p_z0SInI+kclnl}o) zc7I2QuYsm3!4`G=MZPm!us~(JLobZUI=8{TiC`2)Ly3HSDTdG_D@hS-hy^|{hLIq+ zL$V>yu+2zh_y+w@4MfATa)+ET%)|BQFm>elhWesIqDOJ8EkT}dBuCdMch1fanUxMb z@G;f{n8WH#yJ$-?0jn(l7g7PI+7=~i1a7c+r~%urpxOdfXBrT5WKP;fgs&XAj+f~E z26`}vBC;*Q`N)G4#soVLnCuw#yM{t zi3;P@LT^-qRT#r5AdLK$w(V%#l8R(VrbWc7LU9IlvTD=iuQ2ywFiXg>R{Km(KXD#vrJ3ik()fA&0iF$t zjN8kCIX-oAqhpSmSP|MVTX;pSDUjL>d>02rYk{ta0H;LTxpm=ErfCOA{t$_izH`ID zP-ZM9t^uV~L3V+tj!1)mhfC1ugxZ`n_y#ze zhR%WT$pF^sJ=Eq)v*6=%WLRhfA)>9`m4>b6Lkt<(5btC|NWB)m4%js_Po5Z2fTm+{ zBfAc8ja~Tueixv2%@!0Jv6&A)+U~~T&559)he|M?I-poh4%!R2qg_Hk?%2i#SOXKtV9$7|P@chKDwB+?KV*+{wemY8SgU#ifHt56Mo@s6X2xc96MwsEt}?z^;n>qW?* zdAqN|CkSVdH0cQfe;nHu9cxJ#D8LQC){jF!{KJxf!{`+7OarjMX~xlJKqy0JRinx$ zO#l8ur17YcFz-b`&^+c$TrA>e6?y{`eNO9%mj^|DGxdXx8ffe_EflQG#7|)5BC&$$ zU}Vj@tpheXq*dfGsLq>(O+hf4b8nG>_B=NqOQ&-IUN^>kwnr_#G;vJib~M=UXqnm^ zt7&+m4|UEiGb^iZCbTu5%}FH0HV1w=7Im?u{v^e#G|a7yWN(P z80UCz%L#lA;zsKJBQP)q*pT)Vf|8B5SNW$3n3EuyV+l1LA6gNKQMVIR&1$15v zEq~vh)|MP6M)`k)-0oE8FtNlG?DD*}L|%LzwAqX47L%()e-mlGkv3~F9jpV?lr#S< zhIm_vdfj#$c_WUF+|R@OoP?gRg1#+YF*^gYyR+d%2E4l+8jr(GjDmSu1S<~u$Nuv6 zQ*DP{6IXF@*M+!MTF*=cDw0+ZAl}BWjhG(jPP|h6oitb4=3I8H zj0G^X-oKL|bFSosj=Q$X<%T;S7rW_vH`#_}Lr~?F=vAJok`s=puzwool{Ek)mHphU z){*So?@!kwzBlXnFb9kqptPg0PM$VMv|w<=BP)^)uoe}Xh^SVVnr(n1eqVfJi<`TH zcrXQEJZ!W34z+jl0_l>ihkjEP-bd@7s2RbX4$C^7c67Sbce-8c^eCrT3R?9lU)+eN zC4gSu4l##2YkrYDwwai)PaVwivJx&isYS$Uu<6QV)W}t*;ll(u{IM}Iv=?G=A-&Ay z-~kK&gq??gaI=6+TrApwSp|FEvIIiUWWusCAu$4ZN&$D%g`awM@UMN1UeS==n7%(} zXM>=pMr%>uj@t`i2E9z%bivVx(>y)B%Z`k3Vp^G9hSqtXk5z3k@j`|QJGM<>(shiT zQ=}RQ#j!#u{(xz!&F31>oitKtnsNn#;S&XSkLgmKVKe^9rSD@8YzVb? zEDADLxuf_*3@0+6vBq6W8?X1!)MJ<{17>JkFWGD|6olsg06M%qqj{!qt_$oh`=h5cKNJn>) zFuO9a1=ru~rzh|7^J=CYvR}$>iK}Vem|Dy3p%j?T*0Ab7+A2MSZKkf+Ru@RRQ;LOv|MJ2+S!ofFmn}Q}-7IIiY z8i$M)TgVs&iiJf&iK^O6?fm51aIn(Qx_$qCA$`DI;|;*>3Q6A@_V}eB5`SRNiH2%R z>vr8EH+luV&!wDJRTS67zJ#TF2%HwB7UAeNkttU`M|dS_+t4Y7H%E?YWLLcAoKXts zE+kCm&D|u!CTgBZwsKzcAS(f;o)csupt_LzesnX!(W`e0%>s0dT0&AFwsdSs~1?cLrWg|KFt@I zX)qhK7N0T5PH%@P1Q#W`vOsGB*KAnbM0}_sY<-09V$Sr?z}tz=E_}%Hbx$b7ARMOj z@T0^$Whu`JodSEKaC6Mj%$)N?FIcS(N@NSQ>*m|;7P3)t_giVu!_x{`Gt_GcCN735 zKRTEI7>8eO*`FMEJXn&@wTf0jh23_6tv8B8Y)!4v!xnxW@Vk}><@zF$??bwti?eBq zY%ru;pAOVODvG?hrOOTFyTkHPMV`!dDG&sG;gVpdAMSss*Bpt}YafVYK{%H2$B&@F zQ1QLYY`h8Svlx1-C2D!Z9bTzx#SH-K08_t9$OSa`i<3~o&si=f zqL;Lph?spFzMy$w=G8kXc1l%#%8L3*c1RLhy?QmhS_X@T7 zP`C^I6Us+g;TYIn*`xKB?KE)*s~53p>s1j}`uN%|=G-b=js zgZ;>j{o^&0g!oJ94CLgvKEpS3l-YeVR7?*%=mu24KIYmx0Q%P-;qS%J8KGu`Z)hC< zKzorE>4_dU=kHrdw$vDf^6VVZ(T37>Oy?$FRWE@Jg3HN?j|$%2D)eoibFT>^oDt*5 zjLXcg=$uvE|I9Xxd-3@Y;Q(@HrT!G6c8TSwCvLytKFc2M3huy_nyqN>#1A=#lT zt6dZLX9^mokC7!Mk;U0AK@LMj<%<24S<_R$Zetruk^1aC;y)zri^IXEHbzZU*msE* zP0LT?C)h6l92`HW%zAv2Re{XC)+llIj)QN<4#!tMvWuxNQ8b-gtwptz8YWN!CC+>i zYU-+LxNwPw7TT=;EGy0cUb;_qT_;qc@ zc?1?}EG2GxDLQw^?c2Bmjyc^{NMB^xwAcAcV4Cg-dh+*)@`Aa)FqNX`qoYT-xf$@? z1)mj?l3TXebQ%{K_twsh_CeTSr0=6M4S1NNXg_!$+%;x2v`{gNgGGU-z^&y0D?S|1 z+FgkhgTR&kAd-A-AbE6eT(clu#(D_B_IPet%m zc0#Qu(S*WLJ7nw!aX4|hr4hG$+H0L+61_w;6BwFYW4;>cjlh>sQ`2cMjEqvDU6qmDy9<~=BFSWWz+S@IyS^8F0=X@HuLxwlwzJ$e(9%o@47u4?Y{jH- z#G;|8h<%^oU*~(De2YC{ogu*!g!s9YT>$3oR7ThCTe2}x1+8PRlc*5t)jt_|er0TC zVM!h^I4m?hKlA$W%6CUrF}yv?f)b>vvX4~4iMmSppUBmL?|{BMWiWVf$Npr0`+B+Z%BKV6jxON@pRdw0ZWSr3E$4=?lShG<-Wk zgey1EmXclAB?OSxhCqdDW;Uj(pN!y2vW|`&**exRbp#Qe#<3T*3Of&Pcp^Ws&H%=< z#HCHI56i^FpIRk63469d1kdN+E_hyLF8s{6ZVPrKQFFO z=#w_wL>t4N{sLb$dX+1eB_ST{5So<|pL>@;^I7vlc8Rb{84Z(b^RbxVLE-@+n2 z4|*FmmQ`dPI2LmpR;AZWkosI8hO?4o7EU(QLK`tJ>?I`r_XVt$GIwFho0rGZ z%H~Hc&z0NQnbT8|u3^--p)#Zk$NJWv2+6il;QrsEJH$g|%NhPM43vU2%)PIJqpbza zIhA^I^?R^TT|m>?ZOj1J%!ffSvJsP4@vlR)&<)WJCo@q-R}MPPMHG$PgosV&-`7>{ zWj7f*^F^dlU4^Sq7&tPp*H9unp1C93Y>pmKKbcmS!pRPSftn)wDVhEJ4la$^e5WG6 zP%iqmoAEXFAnWrjIo8(628oS8S)16T@ghP<0!x?L%l!o`t*aShoYk0w^J9@2$0sA~ z4`_>1?m57s!EQn~Pg=0fHJBO@*61VI!A3hoBlpZ1$((t+wmJ^k{=fLgf8fIt zhh>FmbG78r4&e}+u%{N$@~Q0e8>A<4)SvA-57W6uY*_mwW@R!WY%e@67#2J1$SKwH zzF5$~2C6t;m{6iXr-3m1d_b6`AJ`rHH#8iY2S1X$^hSX;1KI^3=V5Zu7p59cCk@TR zl+#L+FOM)+3>v0KOyi4AUkKb{_pBPTin4}ugE+Ud!dlt>#`Ndep4oR;O%oLfp2*p8 z$z1icdXH{O3lm0mZAW;)foN`CtKQgjmSbceMUSh-XZ z-Fk(1sOJnC&fJgOL|bW^J+E;i^@fWI-83FndYua)=XAQcnt;UV5!h($EVW)%?CrrO z`hn~#P$r%UGAdR%STL@}$?{DGN{Qc6XqT4b7G}kdRL`|Xdob@TncJ8^2GUme@Q@4? z#ev-X@7kSKy!Y;d28Uzr`Y_&GG^bDiiD z4KtheU}lBqZl5X9J;(wPbm2WfUWd5k_$=0YT2w**c-(Wx2sqc}lKpDLY2vWx@-^)DbU;gU;;}vS0m3KBTi}O#ciAc9iA5s8~yw9 zYXC(T_l`!e zP=XPZkTfL>o(f=8$czfDq{6(Y@QqZol`;Uzppga`DgdPdNGg!B3W}wIaiW8-bO=UA z5a~okRRC0xMQT7<4VIw}$*Duu>hL`ps2EKsm;pfyq=ptCYe7r35F~AY(}uZd!!By$ z$l6NEx&WpN>FGi*^`Jf`Y#9@+qz`H8!(c2(nFSAHAx#V*c>@&E0PAl^q8kB>5rSz9 zV8(cNV>xvb#5NODvWba_DN5T6XKoH@nj?|sD5?dr#0r92<8juiIyMm12JUKu+hGeg zw?*jMA+OlWSv$Z`4kT>{W$oo8@^W*l6(}7J+>V3bI>Ggw5hQ0heOH*OD+2C{JLra8 z?uLtWXP9~-FL}xhQB7~}-o5*Yq5Bo>4iFO$D##_1{8LGW=~SgGxwtHC`#b}qB6){m zc~-GPaB*>QiM(8u_R@L^rM|wtp`qb8$+xMg=``88S&@8&Vt3`rm82qoTrhDh@0Htwp?#i~kxY4%fwesi`dN0nqw_K;pmxF#5rbA2dn(zbDDR8S;Ok z$NzBSf4K4gJ2w{pzA%e2F;;VLmb52hD2D61o2zf8k#syO-CJsI9i~~tjdZuvcI7gh zGSxiJ)%6@)6-E zqo{#18xCKa(j#d(eHa~-OJ6Kzv}^BfraM&HDvT$ivG2n-_i?WaQy#;%9bulWzJZzz z)Y^W>>N(ro4MOq7Jxl71s^LJzRE*Ky|KvC%zluMGpwJ~k7{Qt_CPKk>KiRkzxiiem zX0mM{jeWFKJImC6iYvvJ?%a;mSu*zrOd}~pbO=G0f@GqTR0xY@7Vwmq*GQNim67^S zy-O{>@}=0)m;o&FZS1V0W>Q;wN(`gYw3D<<$|yg$c!63Z>}A2R)Vk2sy-Uw>tM$<7 zd~Gd)hGf=>mj22>1Tm*1PT2SkgJ}O*KfiHw&2vFAWtAFV2%%3UCKEN102}&t*`^$- zhhYLX)!w2TPM>OhWO#DTrKcyR&s_64;eX~A)RI#t)sTpP$>kz2QU#G<1yBJ%7A}h_ zrd)}1B@BuG9{hK>q|ks{nK?Iy;BWgQT!{E)6*RHptUP2ZmW9ihQ03sG)2eT>l10U~ z@k9)Hhk9E%Y6lM4Ba*Y;e1D(ZhOaGbr22~0`}m$#$30U4@IOsg4C^=;cnSBuJyq$X zV?33bPj>SyR0+B@Ir1rXq%A_iTsS=LUTm_`_f_u}52jLE{Jl@#pILYmpK=U7L;TK5 zTEFa?8g7Pz%u<=?vE4OqW*__Do_3J+mY~7fB~uL>C|lK%e%o~itLEV4&Xcl*@=6^< zrRzJzAD<&J?ld#Ut{X6bBCu#wfF{btuR&K_xXQ2W%6IDsToaRDtR49HgWq8Its7Y1 zi<{%mb?i@9S3i6|{K*FT1f9z00eqmYcpbMcq}mTVU);Iiq`SbS~&FcpZ^Wf-4%7>D5zIg}7 z{!mNlyGi)``cel$D`oZ(qM}LvQE=4;y@z|XB-|9rXC+G~d9_sjMj>|l?9u&NUSKSe z9QFAD#?ZFKMZwOY$Vr~RU(%1jhSi4%nP1%=twGg|cILS{+=6iMsF=RJUyde~`LnZU zbAx?fzY*p9y36|5HZ<1qWyy#5VZG9b;&p)~KE*0gPO=*QuOdbUA3?@RPZ@weS&3a( zCy`43pG4d{CC^L2qWE=sdxgR#jzu()q9`HZ>8f!b3ViE`_(wsXjVpCDxV6;A?Ol0V z+OTuhmVBWQfmI+Z81c9>Qn3<&+Q@Bx?|9Bupl0_f*%gAjR5}~fJTeY+YKVInHa@N? zTGbGe?P%7%MTn_QHxB+hnLZwHI^hEDT*r2V*9FUpF+aYvvGa7m!ri!8VuRn=;wgbw z=8&Gd4&li+8&NWzjF#uo%)&m;zEPC5FC^iqop1ZlutxE%tTGa%IeIa1^VI5wRm&W+ zlrf`_u6vz%KljFUY4WEV-_9)Rf9oz-vbObt=D}AAtUTd(Mi-THUoPz^bT^w1i4!uE z#e~RCzuQWSL%&xhQM!d_4f=PN&;Z5>5?W&$|6^{DT&CW0gic}TxKhN?`AW0~PdX~x zcB@VEP=`{R2B6IK-Nb0ScRdm(cncjB{V-$VooXqP`W*K|?)60ryG_LfI{pD#xOMBb z5OM<(x|+zq0Y9oAolrY4v}q_K)~5 z1`dD6N3@hJg28{%JM<=iaLX#)`>WGQ%1hl(b?56eZ0yP@8x}R!_N1OUcj$a7HeH&m zAO5|9J;Q4gWEOXi7?=*VY#RG8HL?2>BUf+jxls)JXOg%7)E#y^OJ2cm`6JxrN%x*p zL^p*BYva6i&2sW#Pp@pg8`z4MD;HU27OH3o)Lqj@J6sN7cPne8sMv0SAYK2g`Jnm8 z6urO?Bkk=l_IOGnvy5JJ2sthVfE_e#csS<{R_^p^y+eq1BDdyiyK8GYrjr$%4$7%t zX?A{bX)&L#H5)dipcB)_;;U?qVV(QfJU=VXRx(QZaB5U&y|c_dov`8jQ9JtuLfeav zG_nbdEwXv1kTLpU;bp#g((Dwe{hF7`JbE9s2!eQgC=|JtE8?8Bnj(3@LK-TG%jQ1Q zfm=jU%Av;4My<5$%g3wN-TTE_j8haR4Ra`2UBrWtsr&039s*7mjMY|DET4d=3DHI5o2x=L|f?@_TYbX31$Y?ZrdO znBGrSm@qS+Q@@{mU8asQpzGc?$KTHcf`wq%p_D^~?%2t@fh|I!rdv){XZsME+5OJh@J`?!<=_Ue2FyWs(~EM{j2Y+tzlB%>MT0CGQClS|>?Z80Xo7&fl}BI3d}q zu)J|}&o0h`BsKAmqoT#dxc%4kNwPhUtu21~b|jT&pW$*l|GaU4Db z3b8lYwxm9}yM7E|KWeq={4G1{ zN^P?9HtFKNS=Pc=J3e0b-0{gf>RQ4VkBVT&8KqzU41q7(sb;WUnzRw__G;r7)9QqU zc^yN+x1}{{M0(46n<~zv_wO4-`_z;8;&Fu`iBK(t(LtaLMKgd#W8Mqs{pI6*i7M|# zlaXkG?u$eujoTh~>Rw71g!!Npo~5z>Qu!|Y6%0FWvw?eUq8xQ9Jc&(C8OXCDz76;F zT-tL~{G9V8B8Tujsiw%uBTz4Y-S*+PiI*m$-GSH1rZ?N=e(rtl-v|7J=V6Jy(U+ zRP6Kinm66@c4=Z>+dz#7p5dGk*Q(Rr1ykCG32pBX`j${3FuzP7c03=U_legLk(CKw z#AMl7L}eihsc*==PKp5X$ab{a9vWK1JGA3B(ks7Kp>BJ%`#w{L&zeUpQWW0)E_Zv%>*KvIkU?+nAn#O!=9@-&x zjuug3q9V-o_mRZRz!n`pN2bcN zwZFPjI0c7jhhaz>%8X@}d^UsP(Jr=nRm-ipEmlL5O0}`dOa1o27A$se=l4SHL9foa zyyiF_vf7fcfJP8bduA69O0kx#K^V$^v$R@UBm}4!$AuoHn8ytqecj5@*0Do+yXGZh z+a)s{Kyn zoWENpK+pB{y_#jhkbbIVcg!16V7Q3 znO8lG)vxmT?;->T4F6q({L5*Oqp`R^<}?shsk|qo8kb}OQk!FuMl3~)LUw)5_EmYuE4F>U`;xU#vam^VS;vlLegU2 zZ}6!O+duEm2W0z%W|r10f~0HWsr$rpHZsE^h^d{T(|B->g#*)5skAS1*zSK8Lt{MlI!wFIV?E%;upmV`oNp|Jqc0sVO5E^uH#kUdrxG^S-E%gW68MQ`qET# zZ_Ls3rS!K|2{nO|quG z-F1Zbks&<180Yo20O2k^NVz8Y;&?noe2~Q(_#y@zw^WcPtad{8WWd0rzoAtOF8QGO z*Fr-*lv_pxVb`N3i+Yq0=2-GbDl8ma?Srp!RG;^g_#3V<1*PmXvc8WAm7(-oKvph3 zTwXj1A-a)>b(V@G+ZCPL9okjV^y!ott@U{zy z_Xkjic>JKU=gbohY}?yGc>GGEKew5Xb=f``2Pg$!LJ3pYI&)2R;zeis%FLO8Lmt>X zbV=gGevun_sa1keC&Ju?sL8Nzj-E`XTj##C_gO#USBcLpX&Sz?Xd~`S$0=moe!4Ps zSHp82n(kb_^}Rz=K(;4ZN(ek%g=e zk9pyaK8kFhva6{-C#-n)pj+T%cST{;cPEwduVr5**T!G$8FyR$wCT9Z?svP#tKk35 zYOJjF-1=X@Vz5*m4~#jALzi?plenSoylOTawIb^7ygUGTjK%zsZgQv?ukRyd z^T{_eP$me*yp2}I16}4Sud>03J904NBRER~Qw~-}-9o?}MUrVAOEL3l;@%Pye*#E? zT>BV4-nuOB-nnCIPUwqP%Sb>~o+l*-u*M0f9fRqv%n9J|{n|f1wl;WQZlb zvMXP?u}_Hn-Trw<(zhzT-Cr@o$`p)p`kTT{456@p^~a(=G=)SL0UZ|nHqjBw!1U!i zt2(w-diX~oRwJp-bE8QbyJ;c&7#F)=9mb!C@jEPc8B>$h`<}WX(%L$GJcx-Tv7NWr zmA=ZgfH8FQSA-2|6)2VMltNzgq5ceRz_4oKYfh}6oALIkk(>~pq0oHv2D^=As9_d! z+h*p_h~x4rwD3xD{JQAU?y)j;&#h;Y^eaTfXpnS(+4F`iVAAW4!b9ISR@(UBbnA-G z`8P_+4)o}7ViF9aD!en_^_4gHw8ot9ICmnp>CElmpZ;Oo^y#+AAO7LzchhI3s3kMa zozzV;E#1tMGw1GFPtTn1a}Dl0adq{{lNSb}PQJf5k}&=L(rD_E4{a00n?78g>ZKc= zoescnx-xrvI{NCTN1J|S8>9YVK$2ua;w8%?{}d7o#6qx}g*Rb1q^r>ssaob&Q_S!3 z1llscT5jojR(x$&G6Y+AHh)&w4o4B%2p)rM*<=&eZ6pL7F~ST_4q&e2b2@})LKGSnh6klLYH+Kcp5pADzR|WZRU#Urou!%if29JW7{S<03ra0v zmU8^gG3W57o7rWe@VjPL1mDr{Y*m|T`3IskbT8DIrnPJKbvs1$W+G|$yjW3Z#x$O0 z$jfU-p=uXHyyl=I6UVpb=&0W|>bV~IE?pn%$q=f2{F40k5S`X@?DY*S#jCTDGyQzs z>Vw9&>iUG=vz_}qKC3kPaV^X)jXG)PTar?GJkm9RtrF{*4?LkRbZ__WqwA7 zQmM~f8&?*Q9rEH2-E5d6LYfu&3d3|IW|vK-ZS8pbvSN8vMs>OSz~xuv^tWI9O5(m^ z$&&rJ-hL++`U&moOhe`9+C0~`V`pN^3bMsd9X!W+nFHv!N!m{rw;;=(vfRA^?f* zl-oeNd)IhsJM0D$!Yj_4Gxa))Q(#>beR+w!U`+G99v|LKVh{vD0W}ky136j{pK!h4 z^~!cV_Ih_ISszZohJ9ZWU*jrAGNF`S$cLC~Pqy3a5fkpWG5oG-=@rQ@$Tc72MJV3it7IKerYs{m?qhWF>efo!sfKP z8FH4U15O$(>m;eKk$qzfv29^JfW_Ni*L?q^uuT$&-Ng6s$c?(ZjErQviq4 z*q8IQx=T<542FG9rzZtQ(sgHWW%eQth9I>GImsw2g3&2xm6cG&8?L8dt?GMEs3#hZ ze%bb>|1>N&s=JvIic(eKV{XNmzw^&i4<{=c{;|;w8GL!qmgcougDs3CSobJ+?{(2{ z_`2NNc>djTWWtBJbO6B#`xnhFjPau#(I)pbd=}#4-VPjw8VLqFq8|3L!sQK1+~zSEzX0c$i(mxRa)|@kK(?`NYm~qD4q)6+2XfA!M5K?k|sjs%n#fDwg)w z_2khkH64GgN&@p#SuA-=oIQ45o%ziT4Zfq4j$#UBIK0kocq!8Rg%1zH%onl=q>IgD zv7+|e0|;FhSpv!VJbL1;8Nkv`zmXkNv%&gkqjqNKFn1%KDQwf;yDOIMLXUl7GqlIz zqV77#`ju?nYV^5neYVZylTP45_n>>Yl<{GqKH9e6$l9EFUiv?wBFLcH3U>XIK|ft% zcaqQv{9urKi&kgm&(YuPm{ST7nYv&zz=sNyT^c633u?RaHIP&mUn=z~v)od(pv({g z$O`$@OE?;JrEsqQ7}nge~o)| zdP|GpJpJNU$9m7|N0uPIA>hRNt6wMHT{!XlCOz74MWE!k_b*o2pPd;})~h>8%E2pQ zEKMdKgvD>YZLmi_CfjRmP+_;FRWy9%L87H_?Yp;EmvAPlroJ*{a~rzl)E8eNN>0Um z=6d#k_T5~Sy{Zf9*V>-~g&ZB5d;1tKN?z8VU97^aw|xjOt`qxS zdjSQr6)>*-vUg<6cwJu7;P3vcARu7C7}?cC4YnDX1VT7Y7f=851Z zHavRDqY)$g;U<=5DOkF2GB8=M00lCgPBn7YBAz0{v-Fp7bq=RyB=w8UQlC6LKr>}O z4OOP@R`#|xPUYqJY%d;+xyDfrDw;ERR3vbYR~cVt+^3b!FfKKfuH+Te*O$60{PBR~ zU6=1lV3KBi5B+-CRj(4WU&-lc;>Bj>G5B>XYn6=>-w_|cYp_KOj4M)}xjrJkNm z)l%HFYJMjwPFn87>YqN_Ui@_P2G>YIa;D~)z_%CN@9DO;Nj))JT`gRqmNee23idm) z>caGyCfzG`+j#c3_io!T`{88mweKjSSt&w!^Xyfid1|(UU^6p&ljP>5aFVPaWEO|r zcw_h)KJ)tNND6 zRBj@IkhtABvk-lAuFUT22uf}!p~2HEK9wL*!D+~~G{p|dnVv@t`@erXgo}LeodrZe z2-|hPer72&&5oU}*4AI90KG)0onPwvq*atnH5T~hdiIO5X=ZA+BYf8Wy<0^u+MlJX z4_sUOkQh>GDiVnz|3Jyp0g<}sl;Zu+|Ak;R$8vP5k(r=Fjy}|thce|~62d;1Awh~K z3vxo?t*TR$0@7`SyNx1&6UDpoVjsyVJro&($m2G>{Z?h(@#=4Ov6rOGK!| zZ}4iY@$Bnk&Pex$XREfBy^$FXyY&Ly|M>EF0;yLBj)L!;Ii&gyX?rI&)Y_5*?eJgi zX|SG=ALU39urn$Iy>`(Yq^=`Yzw#RQW)=3mz){X5i`y>R>(d-nyyKmI;Ex5jf181x zfB>xjU#fcy@6oB zRLd}D12~djg~&72@{|poblG{Uk2_^rZyE5CT8jE}J>W)b8FNitRj30fC5EK&VeQJB zxVf(nBYLwUIj(cVX7AU+v5hzZ-U`4`UONUZut59sYkQ5KMhH0~v6M=Yi4uqzh(x}9!*p2QeWd~n7b0KuNxjjo;DfnlKdh>(Tu(LyjdO# zMQ}pCc(@=BMHHfJ-uX=Lii)1i@#|E=VEn1U9CtzgA!Q?>fWg@?>*uEKRX97!R(O=( zO>|h#$`kt!`H7R2sU#6jk0mWmYa4qbTYL~m+YhAQQb22`PxZkMpIr#cGTWp$HU?T2 zUU^w0*>b?HmM>uh>-#ff2p@T>uv@n-&k=#Yw%i)v41~KWM_K~;qSdHo+hmf(so4j> z$aT=${+;PSDk65plN|f6Q{x%xNdbYxV+Ua6Z{qAS#)&Jw?hK15iks>8&~-&<{a0z| zCM0^^&q95^MQ5E{14{WXc^73Xz#kK&h#Ck_o#|`^MuW#zr zA~<95v{7y|B3E7Ch{ykRmz$if3GeRll`U*JrU`@7abN-B!bm1V&1wfJ>H>D?gc)nh z>E!+{vAmmF3k*%U9`D8wVHCtEV6-J&D;1;Fv1e{fgvqo_F)ilEfeJwnXxwx4P_AiP3lFQuU%w-h5 zUPykRwS^?DNm_go->t0NOGMZr-#uUXPdVTLDqsPR zzx4kb9Q-xcFXWQxHSZ9vXN^urz`-G!d?}r=|go#R!t54|0NE$>X z#tM%V8Iq{`$@a+61%tfNRb@;3sJgvF;{z$C_`CjBEM*-gM9@X@cd=jyEP2HL%ehJJ zNr$wYy4`Eu-8dWj{7k)J^QnN#Ub!p2*(n+fm$E8?-e=nA`&O;UfZ2|mH|{S_sd)GM zCiA$#D~HPV&=kb**ZGz^=V#4Ax82up**Vonn%94)v&_>*PC<;yE-ld)jGBgaycU5y zt5WTn4H&NF+TSpr0>j&Vs-o`UZjQ8ih|3+Y6(L7s>ZZe_Eaolr^lVd%vDWLtMMSIW$B89Q#0p%Ez3JuKM0>Zo~piO*4J`se%#mU z_~v2aia%#-v03oC%MJEhx9;dgk?;1%udH|i>AIQUU+ukoeu6v+EKdyPP<^xJ2ITL2 aYZh%ob~E4&(<&Y#drjUmD><@Au>W6p3VLGz diff --git a/docs/work/future/archive_images/moonlight/effects/E_Oscillate2D.gif b/docs/work/future/archive_images/moonlight/effects/E_Oscillate2D.gif deleted file mode 100644 index 2d69f7e646806c36212454eb8ca209347a382f0a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50818 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�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 Ov5V^>kl+x}w*LVW?e;MM diff --git a/docs/work/future/archive_images/moonlight/effects/layers.gif b/docs/work/future/archive_images/moonlight/effects/layers.gif deleted file mode 100644 index 8b5ca66a15bedb524c089d23c258dede249ebdcf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 181128 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 z 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