Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ A high-performance system driving large LED installations and DMX fixtures. One

5. **The whole repo, continuously.** We are responsible for every line in the repository, not only the lines changed today. Anything spotted in passing is ours: a British spelling, a stale comment, a doc describing what the code no longer does, a duplicated block, a test pinning the wrong contract. Fix it in the change that found it, or backlog it by name; walking past a defect you have read is what lets debt accumulate. "Pre-existing", "out of scope" and "not mine" say nothing about whether the code is right, and the next reader meets it unchanged. The one thing provenance IS good for is scope: work belonging to another branch is backlogged rather than smuggled into this one. (Applied to review findings in [§ Handling review findings](#commit).)

**Never say "it is not mine".** For anything a check can find and a one-line edit can fix, an em-dash, a British spelling, a typo, JUST FIX IT, in the same edit that found it. Do not report it, do not ask, do not explain whose line it was: saying it costs more of the product owner's time than fixing it. Provenance is worth a sentence only when the fix is large enough to need its own decision.

**Scope: the files this change is already editing, not the repo.** "In passing" means a file already open for another reason. A repo-wide sweep for the same defect is its own change with its own review, and folding one into a feature branch buries the feature in noise. A blanket find-and-replace is also how a symbol gets renamed by accident: a spelling fix once rewrote an API name inside `draw.h` and broke two effects that called it, because the word was part of an identifier rather than prose. Read what an edit touches before making it.

6. **Robustness.** Unbreakable in use: any input, any order, any size. Degrade visibly, never crash, and every discovered crash becomes a test. Every setting applies live; no reboot to apply configuration ([architecture.md § Live reconfiguration](docs/architecture.md#live-reconfiguration-every-change-applies-without-a-reboot)). Out of scope: power loss, brown-out, corrupted updates.

## The Process
Expand Down Expand Up @@ -194,7 +198,23 @@ the fastest loop, and anything the desktop can prove (UI, logic, tests) is prove
through a multi-minute compile and a 60-second flash. A device build comes after the desktop is
clean, and only for what the desktop cannot show: the platform layer, timing, memory, real hardware.

**Bench boards are free test rigs.** Build and flash freely to verify work; re-probe ports first. A *rigorous* change (anything that could brick, boot-loop, or wipe a board: flash erases, boot/partition/build-config changes, a first flash of an untested board) gets a one-sentence heads-up and a go-ahead first — the test is reversibility.
**ESP32 build and flash: ONLY when the product owner approves.** Not "when it seems useful", not to
confirm something compiles, not at the end of a phase, not to take a measurement the agent thinks is
interesting. The PO says when a board is written to, every time. Ask, then wait. This is the rule an
agent breaks by being helpful, and it has been broken repeatedly in one session.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

**Desktop build and test: only when needed as a prerequisite to continue.** A build earns its place
when the next step cannot happen without it: code that must compile before it can be measured, a test
that must run before its result can be read. Not after every edit, and not to re-confirm what the
last build already proved.

**Fast cycles: ASK before running anything slow.** Applies to every expensive step: ESP32 builds,
full scenario sweeps, gate lists, repo-wide sweeps, `collect_kpi`. Run the cheapest thing that
answers the question at hand (one test case, one scenario by name, one check); when the heavy one is
actually needed, say what it is and why, then wait for the go-ahead. A minute per step compounds
across a session into the PO waiting instead of working, and a sweep run twice wastes it twice.

**Bench boards cost nothing to break, but they cost the PO's time to use.** They are free test rigs in the sense that matters for RISK: nothing on them is precious, so verifying on one needs no ceremony. They are not free in TIME, which is why the flashing rule above stands: the PO says when a board is written to. Re-probe ports first, since they drift between sessions. A *rigorous* change (anything that could brick, boot-loop, or wipe a board: flash erases, boot/partition/build-config changes, a first flash of an untested board) needs a one-sentence heads-up on top of the normal go-ahead, because there the test is reversibility rather than time.

**Invite the product owner to test, then STOP.** If the PO could see or judge the result, hand it over ("running on X, look at Y") and wait for their observation before concluding, documenting, or moving on. Leave the state running; don't revert, reflash, or reconfigure what they were about to look at.

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ Specific people whose work directly shaped parts of projectMM. We study their th
- **[WLED](https://github.com/wled/WLED) and [WLED-MM](https://github.com/MoonModules/WLED)**: projectMM is born out of WLED, and takes the usermod idea to a new level. Here *everything* is a mod (a MoonModule): effects, drivers, networking, the file system, the system manager. It also integrates tightly with WLED: a projectMM device can act as a WLED device, and it talks to WLED devices (audio sync, discovery, and more).
- **Frank ([softhack007](https://github.com/softhack007))**: main author of the WLED-MM audio-reactive usermod, the most-used open-source audio-reactive LED implementation. The ideas behind [AudioService](docs/moonmodules/core/moxygen/AudioService.md) (including the adaptive noise-gate concept, analyzed with his permission) descend from years of collaboration on WLED-SR / WLED-MM. He also inspired the [Flying Toasters](docs/moonmodules/light/effects.md#flyingtoasters) effect and the sprite support behind it.
- **[troyhacks](https://github.com/troyhacks/WLED)**: reworked the WLED-MM audio-reactive DSP to run on Espressif's [esp-dsp](https://github.com/espressif/esp-dsp) FFT (a low-latency, "stupid fast" alternative to ArduinoFFT); the same esp-dsp FFT choice [AudioService](docs/moonmodules/core/moxygen/AudioService.md) makes. See its Prior art notes.
- **[Stefan Petrick](https://github.com/StefanPetrick)**: the generative-field vocabulary the LED world learned from [Animartrix](https://github.com/StefanPetrick/animartrix), [FunkyNoise](https://github.com/StefanPetrick/FunkyNoise) and [ColorTrails](https://github.com/StefanPetrick/ColorTrails): noise read in polar coordinates, layers on independent oscillators, a contrast window that turns a field into curtains, and emitters carried by a flow field. [Aurora](docs/moonmodules/light/effects.md#aurora), [PolarNoise](docs/moonmodules/light/effects.md#polarnoise), [Tunnel](docs/moonmodules/light/effects.md#tunnel) and [Trails](docs/moonmodules/light/effects.md#trails) are written here on the published algorithms underneath (Perlin's noise, Quilez's domain warping, Bridson's curl, Stam's fluids). Stefan brought that shader vocabulary to LED panels and showed what it does there, which is the tradition the [power functions](docs/moonmodules/light/power-functions.md) and these effects sit in.
- **[hpwit](https://github.com/hpwit) (Yves Bazin)**: the clockless I2S / RMT / Parlio LED-driver techniques and the [ESPLiveScript](https://github.com/hpwit/ESPLiveScript) live-script engine behind the LED drivers and MoonLive.
- **Christophe Gagnier ([@Moustachauve](https://github.com/Moustachauve))**: author of the native [WLED-Android](https://github.com/Moustachauve/WLED-Android) and [WLED-iOS](https://github.com/Moustachauve/WLED-iOS) apps. Their source let us reverse-engineer exactly what those apps read, so projectMM devices appear in (and are controllable from) the native WLED apps.
- **The [Improv Wi-Fi](https://github.com/improv-wifi) project**: the open Improv serial provisioning standard ([sdk-cpp](https://github.com/improv-wifi/sdk-cpp) / [sdk-js](https://github.com/improv-wifi/sdk-js)) that the projectMM web installer uses to provision a freshly-flashed device over USB.
Expand Down
12 changes: 12 additions & 0 deletions docs/MIGRATING.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@ projectMM ships **no migration code**: the persistence layer is robust by defaul

## Unreleased (`next-iteration`)

### Noise2D is gone; Noise renders it

**Action: re-set one control.** Affects any device with a Noise2D effect on a layer.

The two noise effects were one effect with two names: `Noise` is `Dim::D3` and draws the identical
field on a panel, so the 2D variant earned nothing. A restored config maps `Noise2DEffect` to
`NoiseEffect` and carries `scale` across.

What does not carry is `speed`. Noise2D took a 0..15 divisor of its own; Noise takes its rate from
`bpm` on the shared beat clock, so there is no value to map onto. Set `bpm` to taste after
restoring.

### Infrared is a list of learned rows, and the remote must be re-learned

**Action: re-learn the remote.** Affects any device with a configured infrared service.
Expand Down
Binary file added docs/assets/light/Effects.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/light/effects/AudioSpectrumEffect.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/light/effects/AudioVolumeEffect.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/light/effects/AudioVolumeEffect.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/light/effects/AuroraEffect.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/light/effects/AuroraEffect.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/light/effects/BallpitEffect.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/light/effects/BallpitEffect.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/light/effects/BlurzEffect.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/light/effects/BlurzEffect.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/light/effects/BouncingBallsEffect.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/light/effects/DemoReelEffect.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/light/effects/DemoReelEffect.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/light/effects/DissolveEffect.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/light/effects/DissolveEffect.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/light/effects/EchoEffect.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/light/effects/EchoEffect.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/light/effects/FireworksEffect.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/light/effects/FireworksEffect.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/light/effects/FishTankEffect.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/light/effects/FishTankEffect.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/light/effects/FluidEffect.gif
Binary file added docs/assets/light/effects/FluidEffect.png
Binary file added docs/assets/light/effects/FreqMatrixEffect.gif
Binary file added docs/assets/light/effects/FreqMatrixEffect.png
Binary file added docs/assets/light/effects/FreqSawsEffect.gif
Binary file added docs/assets/light/effects/FreqSawsEffect.png
Binary file added docs/assets/light/effects/GEQ3DEffect.gif
Binary file added docs/assets/light/effects/GEQ3DEffect.png
Binary file added docs/assets/light/effects/GEQEffect.gif
Binary file added docs/assets/light/effects/GEQEffect.png
Binary file added docs/assets/light/effects/GameOfLifeEffect.gif
Binary file added docs/assets/light/effects/GameOfLifeEffect.png
Binary file added docs/assets/light/effects/LavaLampEffect.png
Binary file added docs/assets/light/effects/LissajousEffect.gif
Binary file added docs/assets/light/effects/LissajousEffect.png
Binary file added docs/assets/light/effects/MoonLiveEffect.gif
Binary file added docs/assets/light/effects/MoonLiveEffect.png
Binary file added docs/assets/light/effects/MovingHeadEffect.gif
Binary file added docs/assets/light/effects/MovingHeadEffect.png
Binary file added docs/assets/light/effects/NebulaEffect.gif
Binary file added docs/assets/light/effects/NebulaEffect.png
Binary file added docs/assets/light/effects/NoiseEffect.png
Binary file added docs/assets/light/effects/NoiseMeterEffect.gif
Binary file added docs/assets/light/effects/NoiseMeterEffect.png
Binary file added docs/assets/light/effects/PacmanEffect.gif
Binary file added docs/assets/light/effects/PacmanEffect.png
Binary file added docs/assets/light/effects/PaintBrushEffect.gif
Binary file added docs/assets/light/effects/PaintBrushEffect.png
Binary file added docs/assets/light/effects/PlasmaEffect.png
Binary file added docs/assets/light/effects/PolarNoiseEffect.gif
Binary file added docs/assets/light/effects/PolarNoiseEffect.png
Binary file added docs/assets/light/effects/PongEffect.gif
Binary file added docs/assets/light/effects/PongEffect.png
Binary file added docs/assets/light/effects/PraxisEffect.gif
Binary file added docs/assets/light/effects/PraxisEffect.png
Binary file added docs/assets/light/effects/RandomEffect.gif
Binary file added docs/assets/light/effects/RandomEffect.png
Binary file added docs/assets/light/effects/RaymarchEffect.gif
Binary file added docs/assets/light/effects/RaymarchEffect.png
Binary file added docs/assets/light/effects/RingsEffect.png
Binary file added docs/assets/light/effects/RipplesEffect.png
Binary file added docs/assets/light/effects/RubiksCubeEffect.gif
Binary file added docs/assets/light/effects/RubiksCubeEffect.png
Binary file added docs/assets/light/effects/SdfShapesEffect.gif
Binary file added docs/assets/light/effects/SdfShapesEffect.png
Binary file added docs/assets/light/effects/SineEffect.gif
Binary file added docs/assets/light/effects/SineEffect.png
Binary file added docs/assets/light/effects/SolidEffect.gif
Binary file added docs/assets/light/effects/SolidEffect.png
Binary file added docs/assets/light/effects/SpaceInvadersEffect.gif
Binary file added docs/assets/light/effects/SpaceInvadersEffect.png
Binary file added docs/assets/light/effects/SpectrumEffect.gif
Binary file added docs/assets/light/effects/SpectrumEffect.png
Binary file added docs/assets/light/effects/SphereMoveEffect.gif
Binary file added docs/assets/light/effects/SphereMoveEffect.png
Binary file added docs/assets/light/effects/StarFieldEffect.gif
Binary file added docs/assets/light/effects/StarFieldEffect.png
Binary file added docs/assets/light/effects/StarSkyEffect.png
Binary file added docs/assets/light/effects/TetrixEffect.gif
Binary file added docs/assets/light/effects/TetrixEffect.png
Binary file added docs/assets/light/effects/TextEffect.gif
Binary file added docs/assets/light/effects/TextEffect.png
Binary file added docs/assets/light/effects/TrailsEffect.gif
Binary file added docs/assets/light/effects/TrailsEffect.png
Binary file added docs/assets/light/effects/TruchetEffect.gif
Binary file added docs/assets/light/effects/TruchetEffect.png
Binary file added docs/assets/light/effects/TunnelEffect.gif
Binary file added docs/assets/light/effects/TunnelEffect.png
Binary file added docs/assets/light/effects/VectorBallsEffect.gif
Binary file added docs/assets/light/effects/VectorBallsEffect.png
Binary file added docs/assets/light/effects/WaterRippleEffect.gif
Binary file added docs/assets/light/effects/WaterRippleEffect.png
Binary file added docs/assets/light/effects/WaveEffect.gif
Binary file added docs/assets/light/effects/WaveEffect.png
Binary file added docs/assets/light/modifiers/BlockModifier.gif
Binary file added docs/assets/light/modifiers/BlockModifier.png
Binary file added docs/assets/light/modifiers/CircleModifier.gif
Binary file added docs/assets/light/modifiers/CircleModifier.png
Binary file added docs/assets/light/modifiers/MirrorModifier.png
Binary file added docs/assets/light/modifiers/MoonLiveModifier.gif
Binary file added docs/assets/light/modifiers/MoonLiveModifier.png
Binary file added docs/assets/light/modifiers/PinwheelModifier.gif
Binary file added docs/assets/light/modifiers/PinwheelModifier.png
Binary file added docs/assets/light/modifiers/RandomMapModifier.gif
Binary file added docs/assets/light/modifiers/RandomMapModifier.png
Binary file added docs/assets/light/modifiers/RegionModifier.gif
Binary file added docs/assets/light/modifiers/RegionModifier.png
Binary file added docs/assets/light/modifiers/RippleXZModifier.gif
Binary file added docs/assets/light/modifiers/RippleXZModifier.png
Binary file added docs/assets/light/modifiers/RotateModifier.gif
Binary file added docs/assets/light/modifiers/RotateModifier.png
Binary file added docs/assets/light/modifiers/TransposeModifier.gif
Binary file added docs/assets/light/modifiers/TransposeModifier.png
Binary file added docs/assets/light/tutorial/aurora.png
Binary file added docs/assets/light/tutorial/fluid.png
Binary file added docs/assets/light/tutorial/nebula.png
20 changes: 20 additions & 0 deletions docs/backlog/backlog-core.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,26 @@ of code is fine; a new IDF component is the expensive kind).
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/projectMM/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 `<name>.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.
Expand Down
7 changes: 7 additions & 0 deletions docs/backlog/backlog-light.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ Forward-looking to-build items for the **light domain** (`src/light/`: drivers,
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

### 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.

## Drivers

### Logarithmic brightness, and a power budget the device knows about (2026-09-02)
Expand Down
Loading
Loading