diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0f192da61..9cf441cca 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -28,6 +28,7 @@ on: # C4701 fixes each shipped unverified because they touched only test/. - 'test/**' - 'esp32/**' + - 'teensy/**' - 'moondeck/build/**' # package_desktop.py builds and packages the three desktop jobs; without this line a # packaging fix cannot trigger the jobs that run it (the NSIS-escape fix needed a @@ -91,9 +92,8 @@ jobs: TAG: ${{ inputs.tag || github.ref_name }} run: uv run moondeck/ci/verify_version.py --tag "$TAG" - # The shipping firmware list, read from the generated mooninstaller/firmwares.json - # (projected from build_esp32.py's FIRMWARES dict, drift-guarded by - # check_firmwares.py). Emitted as a JSON array of {firmware, chip} so build-esp32's matrix can + # The shipping ESP32 firmware list, read from the generated mooninstaller/firmwares.json + # (projected from the firmwares.py registry, drift-guarded by check_firmwares.py). Emitted as a JSON array of {firmware, chip} so build-esp32's matrix can # fromJSON() it: GitHub matrices can't read a file at parse time, so a job output is the # standard bridge. This is the ONLY firmware list in CI, and the chip is the IDF target. firmwares: @@ -108,7 +108,7 @@ jobs: - id: gen run: | set -euo pipefail - echo "list=$(jq -c '[.firmwares[] | select(.ships) | {firmware: .name, chip}]' mooninstaller/firmwares.json)" >> "$GITHUB_OUTPUT" + echo "list=$(jq -c '[.firmwares[] | select(.ships and .platform == "esp32") | {firmware: .name, chip}]' mooninstaller/firmwares.json)" >> "$GITHUB_OUTPUT" build-esp32: needs: [verify-version, firmwares] @@ -501,12 +501,70 @@ jobs: name: desktop-linux-arm64 path: dist/ + # The Teensy 4.1 firmware, a HEX for teensy_loader_cli or PJRC's Teensy app; test.yml builds it on every PR as well. + build-teensy: + needs: verify-version + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + fetch-depth: 0 + - uses: astral-sh/setup-uv@v3 + - name: the Arm toolchain's version, which keys its cache + id: gcc + run: echo "version=$(sed -n 's/^GCC_VERSION = "\(.*\)"/\1/p' moondeck/build/setup_teensy.py)" >> "$GITHUB_OUTPUT" + - uses: actions/cache@v4 + with: + path: ~/teensy/arm-gnu-toolchain-* + key: teensy-gcc-${{ runner.os }}-${{ steps.gcc.outputs.version }} + # Same tag and version resolution as build-esp32, so the HEX reports the version and channel its asset name carries. + - name: Resolve release tag + id: tag + shell: bash + env: + INPUT_TAG: ${{ inputs.tag }} + REF_NAME: ${{ github.ref_name }} + IS_MAIN: ${{ github.ref == 'refs/heads/main' }} + run: | + set -euo pipefail + if [ -n "$INPUT_TAG" ]; then echo "tag=$INPUT_TAG" >> "$GITHUB_OUTPUT" + elif [ "$IS_MAIN" = "true" ]; then echo "tag=latest" >> "$GITHUB_OUTPUT" + else echo "tag=$REF_NAME" >> "$GITHUB_OUTPUT"; fi + - name: Compute version + id: ver + shell: bash + env: + TAG: ${{ steps.tag.outputs.tag }} + run: | + set -euo pipefail + V=$(uv run python moondeck/build/compute_version.py --tag "$TAG") + echo "version=$V" >> "$GITHUB_OUTPUT" + - name: Install the Teensy SDK + run: uv run moondeck/build/setup_teensy.py --no-loader + - name: Build the Teensy 4.1 firmwares + env: + TAG: ${{ steps.tag.outputs.tag }} + V: ${{ steps.ver.outputs.version }} + run: | + set -euo pipefail + mkdir -p dist + for fw in $(jq -r '.firmwares[] | select(.ships and .platform == "teensy") | .name' mooninstaller/firmwares.json); do + uv run moondeck/build/build_teensy.py --firmware "$fw" --release "$TAG" --version "$V" + cp "build/$fw/MoonLight.hex" "dist/firmware-$fw-v$V.hex" + cp "build/$fw/MoonLight.bin" "dist/firmware-$fw-v$V.bin" + done + - uses: actions/upload-artifact@v4 + with: + name: firmware-teensy41 + path: dist/ + release: # Publish a release on: version tag push, main branch push, or workflow_dispatch. # Plain branch push without matching paths is filtered by the top-level `paths:` # so the build jobs already won't run; this condition is the belt to their braces. if: startsWith(github.ref, 'refs/tags/') || github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch' - needs: [build-esp32, build-macos-arm64, build-windows-x64, build-linux-x64, build-linux-arm64] + needs: [build-esp32, build-teensy, build-macos-arm64, build-windows-x64, build-linux-x64, build-linux-arm64] runs-on: ubuntu-latest permissions: contents: write # softprops/action-gh-release needs this @@ -588,7 +646,7 @@ jobs: V=$(uv run python moondeck/build/compute_version.py --tag "$TAG") echo "version=$V" >> "$GITHUB_OUTPUT" - - name: Generate ESP Web Tools manifests (release-asset URLs) + - name: Generate the installer manifests (release-asset URLs) env: TAG: ${{ steps.tag.outputs.tag }} REPO: ${{ github.repository }} @@ -603,11 +661,14 @@ jobs: # The shipping firmware list — the same mooninstaller/firmwares.json the # build matrix reads, so manifests and builds can't drift. for F in $(jq -r '.firmwares[] | select(.ships) | .name' mooninstaller/firmwares.json); do + # An ESP32 manifest takes its offsets from IDF's flasher args; a Teensy one is the flat image at 0. + ARGS=() + [ -f "dist/flasher-$F.json" ] && ARGS=(--flasher-args "dist/flasher-$F.json") uv run python moondeck/build/generate_manifest.py \ --firmware "$F" \ --version "$V" \ --release-url "$BASE" \ - --flasher-args "dist/flasher-$F.json" \ + "${ARGS[@]}" \ --out "dist/manifest-$F.json" done @@ -675,6 +736,7 @@ jobs: # is a glob pattern, the action does NOT strip `#` as comment syntax. files: | dist/firmware-*.bin + dist/firmware-*.hex dist/shared-*.bin dist/partition-table-*.bin dist/manifest-*.json diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 945c4e2c6..1935ccebd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -36,6 +36,8 @@ on: - 'src/**' - 'test/unit/**' - 'CMakeLists.txt' + # The Teensy job below builds the firmware from these. + - 'teensy/**' - '.github/workflows/test.yml' push: branches: @@ -84,6 +86,40 @@ jobs: - name: node --test run: node --test "test/js/**/*.test.mjs" + # The Teensy 4.1 firmwares, built on every PR: a change to a platform seam the Teensy must implement fails here, not on the bench. + # The HEX images are kept as an artifact, so a PR's build can be flashed and tried before it merges. + teensy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + - uses: astral-sh/setup-uv@v3 + # Only the Arm toolchain is cached, the large download, keyed on its version alone: an edit elsewhere in setup_teensy.py forced a fresh download, and the small source clones fetch fresh anyway. + - name: the Arm toolchain's version, which keys its cache + id: gcc + run: echo "version=$(sed -n 's/^GCC_VERSION = "\(.*\)"/\1/p' moondeck/build/setup_teensy.py)" >> "$GITHUB_OUTPUT" + - uses: actions/cache@v4 + with: + path: ~/teensy/arm-gnu-toolchain-* + key: teensy-gcc-${{ runner.os }}-${{ steps.gcc.outputs.version }} + - name: install the Teensy SDK + run: uv run moondeck/build/setup_teensy.py --no-loader + - name: build the Teensy 4.1 firmwares + run: | + set -euo pipefail + mkdir -p dist + # Every shipping Teensy firmware builds and is collected, so a second one reaches the artifact too. + for fw in $(jq -r '.firmwares[] | select(.ships and .platform == "teensy") | .name' mooninstaller/firmwares.json); do + uv run moondeck/build/build_teensy.py --firmware "$fw" + cp "build/$fw/MoonLight.hex" "dist/$fw.hex" + cp "build/$fw/MoonLight.bin" "dist/$fw.bin" + done + - uses: actions/upload-artifact@v4 + with: + name: firmware-teensy + path: dist/ + # Sanitizers — the safety net for the multicore render/encode split. A second thread now sits # beside code that was single-threaded for its whole life: a shared output buffer handed between # cores, an atomic stop flag, a try-lock over the WebSocket sender, and a child array that can be diff --git a/CLAUDE.md b/CLAUDE.md index a06360c39..01a3405e3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -179,6 +179,8 @@ On "run pre-commit": run the checks whose trigger the diff matches, report one l **A failure is something to report, not to fix and re-run.** A second run needs the words again, as much after a failure or a fix as at any other time. +**The gates run once per commit.** A run costs minutes, so scope freezes at "run pre-commit": a request made during a run goes to the next commit. A change made after a run re-checks only itself, on the product owner's go. A fixed failure reruns that one check; any other change runs the checks its own diff triggers. A full rerun needs the word "restart". A cosmetic change can ride on the run it followed, since the next commit's gates check it again. + ```mermaid flowchart LR pc{"🧑 run pre-commit
PO says the words,
once per request
"} diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..f291c05c1 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,13 @@ +# Security policy + +## Supported versions + +Fixes land on `main`, which ships as the latest pre-release, and in the next tagged release. Update to the latest release to receive them. + +## Reporting a vulnerability + +Report it privately through GitHub: [Report a vulnerability](https://github.com/MoonModules/MoonLight/security/advisories/new). A private report lets the fix ship before the details are public. A maintainer answers in the advisory, and the fix and the advisory are published together. + +## The security model + +What a device trusts, what guards it, and how to secure an installation: [Security](https://moonmodules.org/MoonLight/explanation/security/). diff --git a/docs/assets/deviceModels/esp32-s3-n32r16-waveshare-rgb-matrix.png b/docs/assets/deviceModels/esp32-s3-n32r16-waveshare-rgb-matrix.png new file mode 100644 index 000000000..831d9931e Binary files /dev/null and b/docs/assets/deviceModels/esp32-s3-n32r16-waveshare-rgb-matrix.png differ diff --git a/docs/assets/hardware/boards/aps6404l-psram.webp b/docs/assets/hardware/boards/aps6404l-psram.webp new file mode 100644 index 000000000..20c85e1be Binary files /dev/null and b/docs/assets/hardware/boards/aps6404l-psram.webp differ diff --git a/docs/assets/hardware/boards/mdbt53-db-40.webp b/docs/assets/hardware/boards/mdbt53-db-40.webp new file mode 100644 index 000000000..029364d9a Binary files /dev/null and b/docs/assets/hardware/boards/mdbt53-db-40.webp differ diff --git a/docs/assets/hardware/boards/pi-debug-probe.webp b/docs/assets/hardware/boards/pi-debug-probe.webp new file mode 100644 index 000000000..c12c91671 Binary files /dev/null and b/docs/assets/hardware/boards/pi-debug-probe.webp differ diff --git a/docs/assets/hardware/boards/pico-plus-2-w.webp b/docs/assets/hardware/boards/pico-plus-2-w.webp new file mode 100644 index 000000000..d544ecc26 Binary files /dev/null and b/docs/assets/hardware/boards/pico-plus-2-w.webp differ diff --git a/docs/assets/hardware/boards/pico-w.webp b/docs/assets/hardware/boards/pico-w.webp new file mode 100644 index 000000000..6ddee4d2d Binary files /dev/null and b/docs/assets/hardware/boards/pico-w.webp differ diff --git a/docs/assets/hardware/boards/pico2-w.webp b/docs/assets/hardware/boards/pico2-w.webp new file mode 100644 index 000000000..a3d687741 Binary files /dev/null and b/docs/assets/hardware/boards/pico2-w.webp differ diff --git a/docs/assets/hardware/boards/rp2040-zero.webp b/docs/assets/hardware/boards/rp2040-zero.webp new file mode 100644 index 000000000..b4719d7aa Binary files /dev/null and b/docs/assets/hardware/boards/rp2040-zero.webp differ diff --git a/docs/assets/hardware/boards/rp2350-zero.webp b/docs/assets/hardware/boards/rp2350-zero.webp new file mode 100644 index 000000000..b9968215d Binary files /dev/null and b/docs/assets/hardware/boards/rp2350-zero.webp differ diff --git a/docs/assets/hardware/boards/sn74ahct125n.webp b/docs/assets/hardware/boards/sn74ahct125n.webp new file mode 100644 index 000000000..62a5b71be Binary files /dev/null and b/docs/assets/hardware/boards/sn74ahct125n.webp differ diff --git a/docs/assets/hardware/boards/teensy-audio-shield.webp b/docs/assets/hardware/boards/teensy-audio-shield.webp new file mode 100644 index 000000000..f3fa0193a Binary files /dev/null and b/docs/assets/hardware/boards/teensy-audio-shield.webp differ diff --git a/docs/assets/hardware/boards/teensy41-ethernet-kit.webp b/docs/assets/hardware/boards/teensy41-ethernet-kit.webp new file mode 100644 index 000000000..1a457010d Binary files /dev/null and b/docs/assets/hardware/boards/teensy41-ethernet-kit.webp differ diff --git a/docs/assets/hardware/boards/teensy41.webp b/docs/assets/hardware/boards/teensy41.webp new file mode 100644 index 000000000..18093d0f9 Binary files /dev/null and b/docs/assets/hardware/boards/teensy41.webp differ diff --git a/docs/assets/hardware/boards/w5500-evb-pico2.webp b/docs/assets/hardware/boards/w5500-evb-pico2.webp new file mode 100644 index 000000000..16238f59c Binary files /dev/null and b/docs/assets/hardware/boards/w5500-evb-pico2.webp differ diff --git a/docs/assets/hardware/boards/xiao-nrf52840-plus.webp b/docs/assets/hardware/boards/xiao-nrf52840-plus.webp new file mode 100644 index 000000000..8c2fdf32d Binary files /dev/null and b/docs/assets/hardware/boards/xiao-nrf52840-plus.webp differ diff --git a/docs/assets/hardware/boards/xiao-rp2040.webp b/docs/assets/hardware/boards/xiao-rp2040.webp new file mode 100644 index 000000000..30692fbab Binary files /dev/null and b/docs/assets/hardware/boards/xiao-rp2040.webp differ diff --git a/docs/assets/hardware/teensy41-breadboard.svg b/docs/assets/hardware/teensy41-breadboard.svg new file mode 100644 index 000000000..5aed269e1 --- /dev/null +++ b/docs/assets/hardware/teensy41-breadboard.svg @@ -0,0 +1 @@ +JIHGFEDCBA151015202530354045505560−+ off−+ 3.3 V (Teensy)MB102both rail jumpersoffpower in: USBor 7–12 V DC5 V outUSBTeensy 4.1 (top view)VINGNDGND03V31232223214205196187178169151014111312GND3V3412440253926382737283629353034313332RJ45W5500MPU6050 (GY-521)VCCGNDSCLSDAXDAXCLAD0INTINMP441L/RWSSCKSDVDDGNDVCCOUTGND→ PIR cableGNDGNDGND3V3MOSI3V3SCLKNCSCSnRSTINTMISO+5VGNDDINWS2812B strip (data direction →), +5V from the Teensy’s VIN,the USB 5 V: a short strip at brightness 20 or lessbridge if the rail is split heremagenta at pins 22–23 (J16 to J15): the loopback jumper, left in placered dashed, MB102 5 V to VIN (I12): standalone only, plugged in with the Teensy’s USB unpluggedthe INMP441’s pin order varies by maker: follow its silkscreen \ No newline at end of file diff --git a/docs/contributing/documentation-standards.md b/docs/contributing/documentation-standards.md index 545c6828d..104208e97 100644 --- a/docs/contributing/documentation-standards.md +++ b/docs/contributing/documentation-standards.md @@ -256,7 +256,7 @@ A card is read across a row; a member comment is read beside the thing it descri | One line of a file or class lead | 400 characters | | A whole `///` run, lead or class comment | 2500 characters | | A sentence in a comment | 1 line, never wrapped | -| A paragraph in a comment | 1 line while it fits the line cap | +| A paragraph in a comment | 1 line, cut to fit the line cap | | Every public member | carries one | **A lead's line is wider, and a run is capped as a block.** A lead is the summary that opens a header or a class. One of its lines carries what the whole thing is for: what it is, which hardware it runs on, where the shared body lives. Held to the member cap the driver headers each lost about a third of that, and what went was content. The run budget is what stops a lead sprawling instead. It also closes the cheapest way to satisfy a line cap: splitting one long line into two shorter ones leaves the text identical and every per-line rule passing. @@ -269,7 +269,7 @@ The first five cut and the last adds, deliberately: the result is a short line o **The appendix budget counts a SECTION, not the whole appendix.** A single total punishes a file for having several distinct topics. The cheapest way to satisfy one is to delete a section rather than tighten the prose. Ten lines per `## ` section asks each one to be disciplined and lets a file carry as many as it genuinely has. That is what an implementation file needs: the platform backends each hold a handful of separately diagnosed findings, and one shared budget could only force them out of the tree. A fenced block does not count, the same exemption the no-wrap rule makes. A protocol listing is as long as the thing it describes, and counting its lines would ask an author to delete wire format to fit a prose budget. -**No hard wrap in a comment, for the reason markdown gives.** Let the editor soft-wrap, so a one-word edit is a one-word diff rather than a reflowed paragraph. The one-line budget already forbids this on a header's member or code comment. So the rule bites where a block is allowed to be multi-line. That is the class comment, the `@moreinfo` appendix, and any multi-line run in an implementation file. A list item, a heading, a table row and a fenced block are structure rather than a wrapped sentence, and each is left alone. This one holds everywhere, because its reason is the diff rather than the page. A split sentence reflows every line it spans when a word changes, which costs a reviewer the same in either file. A paragraph goes the same way: its sentences share one line while that line fits the cap. Markdown joins them into one paragraph anyway, so the split only spends a line. Past the cap, each sentence keeps a line of its own. +**No hard wrap in a comment, for the reason markdown gives.** Let the editor soft-wrap, so a one-word edit is a one-word diff rather than a reflowed paragraph. The one-line budget already forbids this on a header's member or code comment. So the rule bites where a block is allowed to be multi-line. That is the class comment, the `@moreinfo` appendix, and any multi-line run in an implementation file. A list item, a heading, a table row and a fenced block are structure rather than a wrapped sentence, and each is left alone. This one holds everywhere, because its reason is the diff rather than the page. A split sentence reflows every line it spans when a word changes, which costs a reviewer the same in either file. A paragraph goes the same way: the sentences of one topic are one paragraph, and a paragraph is one line. Markdown joins them anyway, and a reader takes separate lines as separate topics. Past the cap the paragraph is cut until it fits, never split into sentence lines or line-sized chunks. **Use `//` sparingly.** A comment restating what the code does is a naming failure, and the fix is a better name rather than a better sentence. What survives is the WHY a reader cannot recover from the code. diff --git a/docs/explanation/architecture/mooninstaller.md b/docs/explanation/architecture/mooninstaller.md index 7306aaaae..717ec910c 100644 --- a/docs/explanation/architecture/mooninstaller.md +++ b/docs/explanation/architecture/mooninstaller.md @@ -46,6 +46,17 @@ The words firmware, deviceModel, board and device are defined once, in [the word **deviceModel** is the physical hardware: chip + PCB + on-board peripherals (PHY, USB-serial, PSRAM, antenna), identified by its product name. Examples: `Olimex ESP32-Gateway Rev G`, `LOLIN D32`, `Generic ESP32 Dev`. A unit cannot identify its own deviceModel (no readable PCB ID on classic ESP32), so MoonDeck deduces it from the firmware where unambiguous (`esp32-eth*` ⇒ Olimex) and otherwise lets the user pick. It is stored on the unit as SystemModule's `deviceModel` Text control (display-only in the UI; HTTP `/api/control` writes still apply). MoonDeck mirrors the picked / deduced value to the unit via `PATCH /api/state` after each discover and after every dropdown change. The catalog of valid deviceModels lives at [mooninstaller/deviceModels.json](../../mooninstaller/deviceModels.json), shared between MoonDeck and the web installer. MoonDeck reads it for its dropdown and HTTP push over plain REST on the LAN. The web installer reads it for its picker and pushes the entry's `state` document, deviceModel included, over serial during provisioning, one document per top-level container, applied by the same engine as `PATCH /api/state` (**"Improv = REST over serial"**, the `APPLY_OP` vendor RPC; see [ImprovProvisioningModule.md](../../moonmodules/core/moxygen/ImprovProvisioningModule.md)). Pushing over serial sidesteps the mixed-content block that stops an HTTPS installer page from POSTing to an `http://` device; an already-running device is re-configured via MoonDeck on the LAN. **`SET_BOARD` carries only the board name**, and every other field ships over HTTP after WiFi association. Do not extend its wire format: that couples unrelated controls to the board-name lifecycle and hides the timing constraint. A pre-association control gets either its own vendor RPC dispatched before the credentials, or a board-specific sdkconfig fragment when the value is truly board-static. A deviceModel can run multiple firmwares (the Olimex Gateway runs both `esp32-eth` and the default `esp32`); a firmware can run on multiple deviceModels (`esp32` runs on any classic ESP32 dev kit). The `esp32s3-n16r8` firmware is S3-only and does not run on the Olimex Gateway or other classic-ESP32 hardware. The codebase reserves "deviceModel" exclusively for the physical product and "firmware" exclusively for the compiled binary. +## One manifest, a writer per chip family + +Every firmware's manifest has the ESP Web Tools shape, and its `chipFamily` picks the writer before anything touches the device. +An ESP32 family goes to esptool-js over Web Serial; `Teensy 4.1` goes to `teensy-writer.js`, which speaks PJRC's HalfKay bootloader protocol over WebHID, with one part, the flat image at offset 0. +Detection follows the same split: a serial port with PJRC's USB ids names itself a Teensy, with no ESP32 handshake. +A board without a serial port, held in its bootloader by its program button, is reached from a canceled port pick, the one moment it is needed. +Each next platform adds its writer here and nothing else, as the UF2 boards will. + +The device UI updates a device the same two ways: it hands the release URL to the device, and a device without TLS answers that route with 501. +The browser then downloads the image from the release's copy on the installer's site, which allows a cross-origin read, and uploads it. + ## Config provenance: MCU → deviceModel Firmware-vs-deviceModel is a **two-level** model for **where a pin or setting default comes from**. The installer and MoonDeck use it so a user picks their hardware instead of hand-typing every GPIO. A default belongs at the level that *fixes* it: diff --git a/docs/explanation/architecture/moonlight.md b/docs/explanation/architecture/moonlight.md index e307abfc6..977247544 100644 --- a/docs/explanation/architecture/moonlight.md +++ b/docs/explanation/architecture/moonlight.md @@ -232,7 +232,7 @@ Each driver child reads from the Drivers container's output buffer. Everything b Two rules separate those channels from color, and both matter: - **Brightness never scales them.** Brightness is a light-output setting; scaling pan by it would swing a moving head toward 0/0 as the rig dims. -- **They interpolate but never accumulate** (the rule; the additive half is not implemented, see below). A blend op that INTERPOLATES (opacity, a crossfade) is meaningful on any channel. On pan it is a genuine feature: the head sweeps smoothly from the old aim to the new one as a layer fades in. A blend op that ACCUMULATES (additive) is meaningful only on emissive channels, where summing two lights models two sources lighting one surface. Summing two aims models nothing: it points at neither, and saturates at hard-over as soon as both layers are positioned. So an accumulating op should fall back to assignment on a motion channel, the topmost writer winning. **`blendMap` treats a light as opaque bytes and adds motion channels along with color.** It only bites with two enabled layers on a fixture that carries motion, and the fix is [backlogged](../../work/future/backlog-light.md). +- **They interpolate but never accumulate** (the rule; the combining half is not implemented, see below). A blend op that INTERPOLATES (opacity, a crossfade) is meaningful on any channel. On pan it is a genuine feature: the head sweeps smoothly from the old aim to the new one as a layer fades in. A blend op that COMBINES two values is meaningful only on emissive channels: additive accumulates, and the separable modes such as multiply and screen compute a new value. Summing two lights models two sources lighting one surface. Summing two aims models nothing: it points at neither, and saturates at hard-over as soon as both layers are positioned. So an accumulating op should fall back to assignment on a motion channel, the topmost writer winning. **`blendMap` treats a light as opaque bytes and combines motion channels along with color.** It only bites with two enabled layers on a fixture that carries motion, and the fix is [backlogged](../../work/future/backlog-light.md). **DMX fixtures are addressed as a daisy chain of IDENTICAL fixtures**, the same model addressable LEDs already impose. A strip is N identical pixels at a fixed stride, and a DMX run is N identical fixtures at a fixed stride. One fixture profile describes one fixture, its channel count is the stride, and fixture *n* starts at `start + n x channelCount`. Twenty-five channels per fixture puts them at DMX 1, 26, 51, and so on, and the driver's `count` says how many are on the chain. diff --git a/docs/explanation/architecture/moonlive.md b/docs/explanation/architecture/moonlive.md index 1cab89f66..dc9c77a9b 100644 --- a/docs/explanation/architecture/moonlive.md +++ b/docs/explanation/architecture/moonlive.md @@ -79,4 +79,6 @@ The host-binding surface follows the [MoonLight effects tutorial](https://ewowi. **A scripted module differs from a compiled one in one thing only: where its behavior comes from.** Everything else is the same mechanism: the same base class, the same `prepare()`/`release()` lifecycle, the same controls, the same status and memory reporting, the same container contract. A `MoonLiveLayout` is a `LayoutBase` that answers `lightCount()` and `placeLights()` like any other; it answers them by running compiled machine code instead of arithmetic over its members. When a scripted binding needs a mechanism its compiled sibling does not, that is a finding: either the mechanism belongs in the base for everyone, or the divergence needs its reason stated where it is introduced. A binding that drifts into its own lifecycle stops being a module and becomes a second system to maintain. +**The vocabulary follows the compiled modules, never leads them.** Every call a script makes is a call into a function compiled modules use too: `addRGB` is `draw::addPixel`, `mulRGB` is `draw::scalePixel`, and a builtin handler only converts the script's arguments. So anything a script can do, a compiled effect or modifier can do through the same function, and the two cannot drift. A capability starts in the compiled vocabulary, in the light domain's power functions, and reaches scripts when a script needs it. + The one place this is not yet clean: `applyState()` prepares parent-before-child, so a container asks its children for their extent before those children have prepared. A compiled layout computes its count from its members and does not notice. A scripted one has nothing to answer with until it compiles, so it compiles on demand from a `const` method. The `const_cast` and `mutable` members in `MoonLiveLayout` exist for that and nothing else. Removing them means giving core a way for children to prepare before a container aggregates them, which is a lifecycle change for every module. diff --git a/docs/explanation/security.md b/docs/explanation/security.md new file mode 100644 index 000000000..ebde9c161 --- /dev/null +++ b/docs/explanation/security.md @@ -0,0 +1,80 @@ +--- +title: Security +--- + +# Security + +MoonLight trusts its local network. Every client that reaches a device can read and change everything on it, the way a light switch obeys every hand in the room. The guards described below sit inside that model, and the last section says how to keep an installation safe with it. + +## The trust boundary is the network + +A device serves one plain HTTP port: 80 on a board, 8080 on a desktop. The REST API, the `/ws` state socket and the `/wsp` preview socket all live there, and every route answers every client. A client can set any control, add or delete modules, read and write files, install firmware, reboot the device and reset it to factory settings. The routes: [integrating](../reference/integrating.md). + +Every response carries `Access-Control-Allow-Origin: *`, so a web page on another origin can call the API too. That lets the [web installer](architecture/mooninstaller.md) and MoonDeck talk to devices. It also lets any page open in a browser on the same network do the same. + +The UDP listeners follow the same model and accept any sender: + +| Listener | Port | Open while | +|---|---|---| +| Device presence | 65506 and multicast 239.255.77.77 | the network is up | +| Art-Net, E1.31, DDP | 6454, 5568, 4048 | a Network Receive effect exists | +| OSC | 9000 | OSC is switched on (off by default) | +| RTP-MIDI | 5004, 5005 | the MIDI source is the network | +| Audio sync | 11988 | audio sync receives | +| RTSP (TCP) | 554 | an RTSP driver exists | +| Captive DNS | 53 | the access point runs | + +The MQTT client connects out to the broker the user names, and the commands it receives drive controls and firmware updates like the API does. Its trust is the broker's: a broker with accounts and TLS guards the device, an open one opens it. + +## Transport + +The device serves plain HTTP. Its own connections out use HTTPS wherever the platform has TLS, checked against the ESP-IDF root certificate bundle with no option to skip the check. A board without TLS, such as the Teensy, hands those requests to the browser showing its page. MoonCloud goes through the [browser relay](mooncloud.md), and a firmware URL becomes a browser download. + +## Secrets + +Passwords live in plain text in the device's `/.config` files, where the firmware needs them to join a network. The API sends them XOR-ed and base64-encoded, which keeps them from being read at a glance and is no protection against anyone who looks: [the UI spec](../moonmodules/core/ui.md). + +A client on the device's own access point (subnet 4.3.2.0/24) sees every password empty, and its requests for `/.config` files are refused. While such a client is connected, every client gets the state with passwords empty. This keeps the home network's password from a visitor who joins the access point to set the device up. + +The access point uses the first known WiFi network's password, so it is as private as that network. The rule, the `opens` setting and the power-cycle gesture that opens it for one boot without a password: [the access point](../moonmodules/core/system.md#access-point-details). + +A [backup](../how-to/backup-and-restore.md) holds every file, `/.config` included, so it carries the WiFi passwords in plain text. A state document and a [preset](../moonmodules/core/system.md#what-a-preset-holds) carry no secret, and applying one keeps the stored secrets. + +## Firmware + +Firmware images are unsigned, and secure boot and flash encryption are off. Each image is vetted before it is written: an ESP32 image must carry the right magic byte, chip id and project name, and its checksum must match. A Teensy image must carry a valid boot header and the mark of the variant the board runs. These checks stop a wrong or damaged image, which is what they are for; anyone on the network can still install any MoonLight build. + +[MoonBase](architecture/moonbase.md), the recovery image in the factory slot, serves its own unauthenticated update page on port 80. + +## Files and scripts + +File paths are decoded first, then refused if they contain `..`, so every path stays inside the device's file system. A mount root cannot be deleted. + +[MoonLive](architecture/moonlive.md) scripts compile to native code. To run it, the ESP32 variants switch memory protection off, so the chip lets a page be written and then executed. A script calls only the builtins the firmware registers, its array reads and writes are clamped to the array, and a call-depth limit stops deep recursion. A script that loops forever is ended by the 12-second task watchdog, and two failed boots in a row start safe mode with scripts off. A script is a file, so anyone who can write files can install one. + +## Provisioning and recovery + +[Improv](../moonmodules/core/system.md#improv-provisioning) over USB serial sets the WiFi credentials and applies a state document. It needs physical access to the USB port, and it trusts whoever has it. Bluetooth is compiled out. + +Safe mode, factory reset and the access point's one-boot open gesture are recovery paths for the person holding the device. The [troubleshooting page](../how-to/troubleshooting.md) shows each. + +## What leaves the device + +Nothing leaves the device until the user switches a [MoonCloud](mooncloud.md) member on, each with its own consent. What each member sends and what the server keeps: [privacy policy](../legal/privacy-policy.md). MoonTalk's public message board has no accounts, so a message is as public as its text. + +## Source + +MoonLight is GPL-3.0 and its whole source is public, so anyone can audit every line described here. The UI links to it from the side nav. + +## Reporting a vulnerability + +Report it privately, as [SECURITY.md](https://github.com/MoonModules/MoonLight/blob/main/SECURITY.md) describes, so it is fixed before it is public. + +## Securing an installation + +- Put devices on a network only trusted people and devices reach: a separate WiFi network or VLAN for lighting is the standard setup. +- Keep devices off the internet: no port forward to port 80, and a firewall rule that keeps the lighting network's devices from being reached from outside. +- Leave OSC off unless a controller needs it. +- Use an MQTT broker with accounts, and TLS if it sits outside the lighting network. +- Treat a backup file like the WiFi password it contains. +- Give a show network a WiFi password of 8 to 63 characters, so the device's own access point is protected by it too. diff --git a/docs/friend-repos/FastLED-FastLED.md b/docs/friend-repos/FastLED-FastLED.md index f1a2bed02..e9544ff05 100644 --- a/docs/friend-repos/FastLED-FastLED.md +++ b/docs/friend-repos/FastLED-FastLED.md @@ -7,6 +7,7 @@ What landed on [FastLED](https://github.com/FastLED/FastLED)'s main branch, mont No release cut this month (3.10.4, 2026-06-16, remains the published release; master's manifest and the unreleased 3.10.5 tag are unchanged in meaning), so the month is not split. The month is dominated by the color pipeline reaching the output path, a family of "slim bridge" driver conversions across many boards, and the Raspberry Pi Pico family being hardened on real hardware. **New** + - The profiled color pipeline now runs when a strip is shown: a channel bound to a color profile gets datasheet-aware color, gamut mapping for RGB, RGBW and two-white (RGBWW) strips, and an RGBW target-white override. - Managed strips get temporal dithering and chipset-aware quantization: APA102 and SK9822 solve their 5-bit brightness field and color together, LPD8806 and LPD6803 quantize once at wire width, and HD108 and the 16-bit encoders now take the full device drive instead of an 8-bit value widened by a gamma table. - The power limiter understands the new pipeline: it charges for the drive actually solved, for the extra white diode on RGBW strips, and can report when it lowers brightness. @@ -24,6 +25,7 @@ No release cut this month (3.10.4, 2026-06-16, remains the published release; ma - A `bash release` workflow tags and publishes releases on merge. **Fixed** + - 1-D Perlin noise now has a gradient in every hash bucket (open since 2018, #1114). - Raspberry Pi Pico: strips on independent PIO blocks transmit concurrently, data pins idle low, PIO/DMA/pin contention is reported rather than failing silently, resources are released when a controller is destroyed (open since 2020, #1471), and the UART baud ceiling follows the real peripheral clock. - Pico 2 W: the hang in WiFi connect on the 7th peer cycle (#4173) and several steps of the over-the-air update flow (#3956) are fixed. @@ -36,6 +38,7 @@ No release cut this month (3.10.4, 2026-06-16, remains the published release; ma - The ATmega8/8A are put in the tiny-memory tier, and ATtiny85 builds compile out the dither power reserve. **Watching** + - The proposed MIT to "FastLED Reciprocal License" change (#4046) is still open; the `LICENSE` file on master is still MIT. - The color pipeline phases are largely closed out this month (P1, P6, P7, P8, P9 issues #4035, #4040 through #4043 all closed), with a review burndown (#4156) closed too. - CI cost is under study: a 20-minute Linux unit job (#4529) and cutting routine Actions usage (#4543) are open, and the full example sweep moved to nightly. @@ -47,6 +50,7 @@ _Auditability: 375 commits on `origin/master` with author-date 2026-09-01..2026- No release cut this month (3.10.4, 2026-06-16, remains the published release, with the 3.10.5 tag on master unreleased), so the month is not split. Three threads dominate: filling in long-standing chipset and board gaps from very old issues, bringing the Raspberry Pi Pico 2 W online with WiFi and Bluetooth, and replacing the MP3 decoder. **New** + - On-chip flash storage works at last: sketches can read and write files on an ESP32 without an SD card, through a new `fl::getEmbeddedFs()`. - Raspberry Pi Pico 2 W gains WiFi (CYW43) and Bluetooth LE, plus a device-to-device over-the-air update flow. - ESP32 sketches can now pick which SPI bus (SPI2 or SPI3) each strip uses, so two clocked strips can run on separate buses. @@ -60,6 +64,7 @@ No release cut this month (3.10.4, 2026-06-16, remains the published release, wi - MP3 playback switches to the minimp3 decoder and now decodes in fixed point by default, which is faster on chips without a floating-point unit. The old Helix decoder is gone, along with its license restrictions. **Fixed** + - `FastLED.clear()` and `clearData()` only cleared the first strip of a parallel controller, which also made power limiting under-count. Open since 2020. - `rgb2hsv_approximate()` no longer overflows and returns wrong hues on some colors. Open since 2020. - ESP32: software SPI clock toggling no longer stomps on other pins changing at the same time. @@ -76,6 +81,7 @@ No release cut this month (3.10.4, 2026-06-16, remains the published release, wi - Browser/WASM preview: asset loading is bounded and verified, so a stalled download no longer hangs the sketch forever. **Watching** + - A proposed license change from MIT to a new "FastLED Reciprocal License 1.0" is under discussion (#4046, #4047, #4048). Nothing has changed yet: master is still MIT. Under the proposal, shipping a product with modified FastLED would require publishing those modifications. - A large "profiled color pipeline" design is being planned in ten tracked phases (#4032 through #4044), aiming at datasheet-accurate color, gamut mapping, and float-free fixed-point output. - The open report that `show()`'s refresh throttle takes an unconditional deep yield (#3762) is still unresolved. @@ -87,6 +93,7 @@ _Auditability: 195 first-parent commits on `master` with author-date 2026-08-01. No release cut this month (3.10.4, 2026-06-16, remains the latest), so the month is not split. Two big threads: finishing the Raspberry Pi Pico driver family, and cutting the ESP32 platform loose from the Arduino core. **New** + - Raspberry Pi Pico / RP2040: automatic parallel PIO output finally works for real, 2/4/8 strips driven from one PIO program, with a single-lane fallback for mixed layouts. - Raspberry Pi Pico gains fixed-function SPI+DMA drivers, a UART DMA driver, and a public hardware-SPI routing API. - WS2814 RGBW strips are now a first-class chipset with datasheet timing. @@ -98,6 +105,7 @@ No release cut this month (3.10.4, 2026-06-16, remains the latest), so the month - `fl::printf`/`snprintf` accept a generic `{}` placeholder. **Fixed** + - ESP8266: `addLeds()` no longer watchdog-resets when GPIO12 (D6) is used with P9813. - `rgb2hsv_approximate()` no longer turns orange into green; CHSV values now compare by field instead of by their RGB rendering. - TM1829 timing (FLIP + wait time) restored after a refactor dropped it. @@ -108,6 +116,7 @@ No release cut this month (3.10.4, 2026-06-16, remains the latest), so the month - WASM/browser preview: microphone capture recovers after the user cancels access, and the default renderer works again. **Watching** + - Report that RGBW output has been broken since 3.10.3 (#3622, closed) fed the month's RGBW colorimetry cleanups. - An open thread (#3762) blames an unconditional deep yield in `show()`'s refresh throttle for a long-standing frame-timing regression, no fix shipped yet. - A port to the WCH CH32V003 (48 MHz, 2 KB RAM) is proposed (#3755). @@ -119,6 +128,7 @@ _Auditability: 212 first-parent commits on `master` with author-date 2026-07-01. Released **3.10.4** (2026-06-16), cut from `master`. **New** + - STM32: Arduino UNO Q board support. - New NXP LPC8xx family drivers land (LPC804 PLU, LPC845 bit-bang + PWM/DMA-to-GPIO, LPC11xx), early bring-up, bench-validated. - Unified `fl::Watchdog` API with real hardware implementations across platforms (ESP32, Teensy 4, AVR, Apollo3, RP2040, STM32) plus a non-allocating reset/crash-classification helper. @@ -127,6 +137,7 @@ Released **3.10.4** (2026-06-16), cut from `master`. - FFT now auto-detects ESP-DSP on ESP32 (no opt-in macro needed). **Fixed** + - RGBW colorimetric path reworked: native LED gamut + D65 as the default source, improved strict/boosted solvers, corrected dual- and 3-channel solving. - ESP32: reliable streaming for SPI strips over ~680 LEDs; ESP32-C6 routes Serial to HWCDC with non-blocking writes and rejects USB-serial pins for LED output. - Teensy Audio selection fixed on low-memory boards; nRF52 now honors configured SPI data rates. @@ -135,11 +146,13 @@ Released **3.10.4** (2026-06-16), cut from `master`. ## June 2026 (post-3.10.4) **New** + - Teensy 4.x LED driver bring-up: ObjectFLED and FlexIO parallel output engines plus a new LPUART-based WS2812 driver (inverted-TX + eDMA), and FlexPWM-based RX capture. - New WS2812-style RX capture path on classic ESP32 (RMT4) and LPC845 (SCT+DMA). - ARM Cortex-M DSP-extension SIMD backend wired up for Teensy 3.x/4.x (faster scale/blend on those chips). **Fixed** + - SM16824E chipset timing corrected to match the datasheet. - RGBW gamut configuration is now kept per strip. - Fixed a memory leak in the chunked `fl::deque` container. diff --git a/docs/friend-repos/MoonModules-WLED-MM.md b/docs/friend-repos/MoonModules-WLED-MM.md index cff673563..ced92d048 100644 --- a/docs/friend-repos/MoonModules-WLED-MM.md +++ b/docs/friend-repos/MoonModules-WLED-MM.md @@ -33,10 +33,12 @@ Near-dormant month, a single small change and a build-number bump. *Summarised from 38 commits on `mdev`, 2026-06-01 … 2026-06-25 (no versioned release cut this month, so the month is not split; the `nightly` prerelease is not a release).* **New** + - Waveshare ESP32-S3 Matrix Driver board profile added. - Audio-reactive is now an out-of-tree usermod (pulled in as a dependency) rather than baked into the tree, no user-facing change to how it works, but a cleaner build. **Fixed** + - Output settings no longer revert to defaults after a reboot (regression in 14.7.2 nightly, reported on ESP32-D0WDQ5 and ESP32-S3; issue #367). - LEDs no longer flicker on startup and during GIF playback. - Custom palettes reload immediately after you upload a palette file (previously needed a manual refresh; relates to long-standing issue #105). diff --git a/docs/friend-repos/PlummersSoftwareLLC-NightDriverStrip.md b/docs/friend-repos/PlummersSoftwareLLC-NightDriverStrip.md index b1967b816..760bbd905 100644 --- a/docs/friend-repos/PlummersSoftwareLLC-NightDriverStrip.md +++ b/docs/friend-repos/PlummersSoftwareLLC-NightDriverStrip.md @@ -47,6 +47,7 @@ _Auditability: 3 commits on `main` author-dated 2026-07-01..2026-07-31 (1 first- The big one: **NightDriverStrip 2.0.0** shipped on June 14, a major release cut from `main`. **New** + - Brand-new Web UI replacing the old one, plus a new browser-based web installer for flashing devices. - Runtime-selectable output drivers: settings that used to be compile-time (like strip type) can now be changed on the device without rebuilding. - APA102 / SK9822 strips are now a first-class output type alongside WS281x and HUB75. @@ -57,6 +58,7 @@ The big one: **NightDriverStrip 2.0.0** shipped on June 14, a major release cut - New and revised effects, plus smarter memory handling (targeted mix of internal RAM and PSRAM). **Fixed** + - Render hiccup/stutter when settings auto-saved: SPIFFS/JSON writes no longer block the render thread. - Effect-manager crash shortly after load when the effect set changed. - Serpentine-matrix visualization corrected. @@ -70,6 +72,7 @@ The big one: **NightDriverStrip 2.0.0** shipped on June 14, a major release cut - Fixed 64x32 (wide-and-short) displays that were rotating and doubling their content instead of scaling, effect previews in the Web UI and CLI now match the active output driver's pixel mapping (issue #878). **Watching** + - Issue #877 ("networking seems broken") drew heavy discussion (38 comments) around the WebUI being unreachable after a recent merge on some setups; closed in June. Auditability: ~40 first-parent merges/commits on `main` with author-date in 2026-06-01..2026-06-30 (`commits?sha=main&since=…until=…`); two versioned releases published June 14, v2.0.0 (commit ce00eaa) and v2.0.1 (commit 835015b), both ancestors of `main`, so the month is split at v2.0.0. Issues checked via `search/issues` for `created:2026-06-01..2026-06-30` (0 opened in range) and `closed:2026-06-01..2026-06-30` (#877, #878, #825 closed; #878 and #877 user-facing). diff --git a/docs/friend-repos/blazoncek-WLED.md b/docs/friend-repos/blazoncek-WLED.md new file mode 100644 index 000000000..66e423026 --- /dev/null +++ b/docs/friend-repos/blazoncek-WLED.md @@ -0,0 +1,176 @@ +# blazoncek/WLED: monthly activity digest + +What landed on [blazoncek/WLED](https://github.com/blazoncek/WLED)'s `dev` branch, month by month. External-context reference, a factual log of a friend repo's activity, not MoonLight's own history or roadmap. Newest month on top. The reusable prompt that generates these lives in [README.md](index.md). + +This is the personal fork of upstream [wled/WLED](https://github.com/wled/WLED) by Blaž Kristan (blazoncek), a long-time WLED core developer; the fork's builds call themselves "WLED-BB" and track the 0.15 line. It carries his own work ahead of or apart from upstream: segment blending modes and transitions, an alpha (transparency) color channel, bus-manager and LED-driver rewrites, HUB75 matrix support, memory tuning for large installations, and many effect rewrites. Several of these are later offered upstream as pull requests. + +**Branch note.** The fork's default branch is `stable`, a release line that has 6 commits in the whole window (2025-12-31 and 2026-01-03, the "WLED-BB 0.15.2" release and four follow-ups). All day-to-day work lands on `dev`, so the months below summarize `dev` (first-parent view). Two side branches also saw work: `bus-memory` (HUB75 and bus memory calculation, January 2026) and `fastled_replacement` (a FastLED replacement brought in from upstream, March to June 2026). No versioned GitHub release is published (the releases list is empty and the issue tracker is disabled), so months are kept whole. + +## September 2026 + +35 commits on `dev`, most of them effects, LED current limiting and bus work. + +- New **LED current limiting** for APA102, SK9822 and HD108 strips, and for TM1814 and SM16825 (set in mA per LED), which gives finer brightness steps at low brightness. +- Gamma correction is now applied before brightness, and the old custom raw gamma table is removed. +- New and reworked effects: **Twinkles 2D** (with wrapping, then drifting like snowflakes), a filled DNA spiral with a variable trail, smoother Colored Bursts, and several effects fixed for the RGBW (white channel) color type. +- Effect search hides 2D effects on segments that are not 2D. +- Timer presets are simplified, and usermod settings sections can be collapsed. +- SD card files can be served over the web, and the code is tuned for PSRAM, for a "final tuning before 0.15.3 release" commit (2026-09-12). +- The active preset is no longer cleared by JSON requests during a brightness transition (from upstream, #5856). +- Library dependencies are all resolved by URL so corporate proxies do not block the build, and the old IDF 3 environment is dropped. + +_Checked: 35 first-parent commits on `origin/dev` with author-date 2026-09-01..2026-09-30 (newest 36c82dfb, oldest 2194d02a), via a local blobless clone and `git log --first-parent`. The default branch `stable` has no September commits (tip b8c96235, 2026-01-03). Releases published: none (`repos/blazoncek/WLED/releases`). Issues: the tracker is disabled on this fork (`has_issues` false), so there are no issues to search. Release-pipeline commits, build bumps, comment edits and const tweaks are omitted._ + +## August 2026 + +65 commits, the busiest month of the window: a drawing-primitives and transparency pass, HUB75 rework, and memory tuning. + +**New** + +- Circles and ellipses are redrawn with finer fixed-point math, plus a blurred ellipse, gradient lines and smoother lines; effects Blobs and Ripple are updated to use them. +- Effects can use **transparency**: segments can fade out to transparent, clear to transparent black, and fades use a parabolic curve instead of a linear one. +- The segment blending fast path is now on by default, and it honors rotation and per-pixel opacity. +- **HUB75 simplified** (breaking, needs the config saved again): no reboot after a config change, simpler panel selection, panels 40, 48 and 80 pixels high, and a build for ESP32-S3. +- "PixelForge Lite" (the image and scrolling-text interface) gets fixes and optimizations. +- Memory work for very large matrices: lower output bit depth, PSRAM tuning and cache pre-loading, and an attempt at RTC RAM. + +**Fixed** + +- Legacy sunrise and sunset timers load correctly again, and a timer bug is fixed. +- Rotation can go both clockwise and counter-clockwise. +- MAC address inputs are improved (upstream #5797), and a small `drawCircle` fix follows upstream #5798. +- A crash in the Scrolling Text effect from a null reference is fixed. + +_Checked: 65 first-parent commits on `origin/dev` with author-date 2026-08-01..2026-08-31 (newest e8957110, oldest 70e33be0), via `git log --first-parent` on a local blobless clone. One commit dated 2026-02-11 (04ea3fc8) merged in August and is counted in February by author-date. Releases published: none. Issues: tracker disabled on this fork. Build bumps, comment and const-only commits omitted._ + +## July 2026 + +35 commits: SD card support, HUB75 boards and bufferless segments. + +- **Full SD card support** in the file editor (SPI SD card, upload and edit files from the UI), replacing an older LittleFS library. +- **Bufferless segments** for very large installations: draw straight to the strip buffer, ignore segment layering, and use simpler transitions limited to color and palette. +- More HUB75 boards and options: Waveshare ESP32-S3-RGB-Matrix, a Seengreat v1 pinout, 2x3 and 2x4 panel layouts, a chain orientation option, and fixes for DMA memory and I2S speed. +- New **Import Image** effect (from Aircoookie), and a fix in the Stream effect. +- A usermod JavaScript inject API lets usermods change the UI; resources load sequentially in the UI (adapted from a contributor's change). +- Turning WLED off with a non-Fade transition no longer causes a one-frame blackout, and no white pixel is painted on RGBW strips. + +_Checked: 35 first-parent commits on `origin/dev` with author-date 2026-07-01..2026-07-31 (newest 6fdf8f19, oldest ccc6be43), via `git log --first-parent`. Releases published: none. Issues: tracker disabled on this fork. Internal refactors and the merge of the `more-timers` branch are omitted._ + +## June 2026 + +19 commits, audio-reactive, effects and network fixes. + +- New **Flow 2D** effect, and a rewritten Perlinscape effect with rotation again. +- Audio-reactive usermod changes: simulation choice moved into the info dialog, palette recreation improved, and microphone type "none" means network receive mode. +- A Seengreat v2 HUB75 pinout for ESP32-S3. +- UDP sync is applied to all selected segments (upstream #5705), gamma is applied consistently to live data (upstream #5661), and IR and ESP-NOW remote handling no longer depends on the global JSON buffer. +- An internal pseudo-random-number namespace replaces scattered helpers, and FastLED functions move into a slim replacement library. +- The `fastled_replacement` branch picks up the same changes (tip 79ea6edd, 2026-06-08). + +_Checked: 19 first-parent commits on `origin/dev` with author-date 2026-06-01..2026-06-30 (newest a752e494, oldest 1bbfc619). Releases published: none. Issues: tracker disabled on this fork._ + +## May 2026 + +30 commits: transitions and on/off handling were reworked. + +- **Transitions reworked**: a segment blending fast path, smooth color change even while a transition is running, a faster push transition, and a corrected Off transition (peek preview brightness included). +- On/off and brightness handling is rewritten and a debug statistics option is added; the rarely used `WLED_SAVE_RAM` build option is removed. +- Nightlight no longer interferes with transitions (upstream #5620) and restores the original brightness when canceled. +- Better network connect: the best SSID is stored for the next boot and a BSSID change forces a reconnect. +- Improv input handling is more robust, and the ledmap is read without the global JSON buffer. +- New boards and effects: Huidu HU-FW2 HUB75, the Perlinscape effect by a contributor, and a color desaturation helper. +- Dual WS2811 is removed as a duplicate of FW1906. + +_Checked: 30 first-parent commits on `origin/dev` with author-date 2026-05-01..2026-05-31 (newest e4f008bc, oldest 47faf977). Releases published: none. Issues: tracker disabled on this fork._ + +## April 2026 + +8 commits, mostly fixes. + +- 2D transition and blending style options no longer show as enabled when they do not apply (upstream #5498). +- New MQTT sync and notification filter, and DDP receive and notification cleanups. +- The gap file is read without ArduinoJSON, which avoids using the global JSON buffer (upstream #5518). +- A build tagged 0.15.3-b2 (2026-04-19). + +_Checked: 8 first-parent commits on `origin/dev` with author-date 2026-04-01..2026-04-30. Releases published: none. Issues: tracker disabled on this fork._ + +## March 2026 + +3 commits. + +- A proof of concept for WS281x RGB+CCT strips (dual chip). +- A transmit-power bug (set too early at init) and a DDP packet-count tweak. +- On `fastled_replacement`, three commits (2026-03-21) bring in upstream's FastLED replacement. + +_Checked: 3 first-parent commits on `origin/dev` with author-date 2026-03-01..2026-03-31. Releases published: none. Issues: tracker disabled on this fork._ + +## February 2026 + +17 commits. + +- **Per-output brightness scaling** (a brightness factor per LED output). +- Exclusive CCT, a DDP invalid-packet filter, and an audio PLL fix for audio-reactive with Ethernet. +- Custom JSON timezone support, with an example file for the Chatham time zone. +- The inverse gamma option is removed, and segment effects no longer speed up during a transition. + +_Checked: 17 first-parent commits on `origin/dev` with author-date 2026-02-01..2026-02-28. Releases published: none. Issues: tracker disabled on this fork._ + +## January 2026 + +36 commits on `dev`, plus 4 on `stable` (2026-01-03). + +- **HUB75 support** and a delay of LED output after a relay switch (a5517478, 2026-01-31); "blending style" is renamed "transition style" in the UI, and a custom NeoPixelBus fork is adopted. +- New HD108 LED type (16-bit, 2-pin), and bit-banging output is dropped on ESP8266. +- Gamma correction and brightness scaling move into the bus manager. +- Effect updates: Pride, Colorwaves, Palette, 2D Drift, Drift Rose, Ghost Rider and Rotozoomer. +- Open files are no longer corrupted by writes to an open object (credit to a contributor, upstream #5276), and preset saving is fixed. +- On `stable`: ledmap loading, palette preview, and WebSocket broadcasts that wait while the JSON buffer is locked. + +_Checked: 36 first-parent commits on `origin/dev` and 4 on `origin/stable` with author-date 2026-01-01..2026-01-31. The `bus-memory` branch carries the same HUB75 work (tip f768e07b, 2026-01-27) and `simpler-tz` carries the timezone work. Releases published: none. Issues: tracker disabled on this fork._ + +## December 2025 + +42 commits on `dev`, plus 2 on `stable` (2025-12-31). + +- **Segment zoom and rotate** (by a contributor, upstream #5202) with wrapping and mirroring options. +- **Particle system update**: 1D particle effects and the particle fire are removed, particles carry their own size and mass, collisions use real radii, and fireworks explosions become round. +- New LED type: dual WS281x chip RGBW strips; new effect Moving Octopus. +- Circle and ellipse drawing use fixed-point coordinates, and Wu pixel drawing is optimized. +- The global AWM (auto white mode) setting is removed in favor of a runtime override in JSON and HTTP. +- "WLED-BB 0.15.2" lands on `stable` on 2025-12-31 (f96941f0), with a bus-wrapper rewrite, RMT fix, variable buttons, the CRGBA alpha channel, segment rotate and zoom, and non-gamma-adjusted palettes. A 0.15.3-b1 build follows on `dev`. + +_Checked: 42 first-parent commits on `origin/dev` and 2 on `origin/stable` with author-date 2025-12-01..2025-12-31. Releases published: none on GitHub; the versions are named in commit messages only. Issues: tracker disabled on this fork._ + +## November 2025 + +18 commits. + +- The `rmt-hi` branch (the high-priority RMT driver) merges into `dev` on 2025-11-28. +- A dropdown for LED output pins, unique LED types with reserved GPIO, and UI memory calculation. +- Fire 2012 bug fix and effect tuning; assorted UI and flash-size reductions. + +_Checked: 18 first-parent commits on `origin/dev` with author-date 2025-11-01..2025-11-30. Releases published: none. Issues: tracker disabled on this fork._ + +## October 2025 + +24 commits. + +- **LED outputs can be re-arranged by drag and drop**, and 1D arc expansion is faster. +- Effects consolidated: Blink and Strobe (with a color time/space swap), Running and Saw, Chase, Gradient and Loading, Twinklefox and Twinklecat, Spots and Spots Fade, Phased and Phased Noise. +- Bouncing Balls uses integer math: about 4x faster on ESP8266 and 2x on ESP32. +- The custom palette editor is aware of local files, and upstream's wled-tools are added. +- The CRGBA alpha-channel branch merges with the particle-system import branch. + +_Checked: 24 first-parent commits on `origin/dev` with author-date 2025-10-01..2025-10-31. Releases published: none. Issues: tracker disabled on this fork._ + +## September 2025 + +20 commits, the alpha channel arrives. + +- **Alpha (transparency) color channel**, CRGBA (067b23ff, 2025-09-09), which can replace FastLED's CRGB; it adds the **HSV and Stencil segment blending modes** and updates effects to be alpha-aware. A **Chroma** blending mode came just before the window (ce04f1bd, 2025-08-31). +- **True variable custom palettes**: more than 10 allowed through a build setting (ce7e0499, 2025-09-11; a5a24fd2, 2025-09-15). +- Virtual buses are no longer limited in count (upstream #4924), and temperature resolution is variable (credit to a contributor, upstream #4916). +- Better handling behind a reverse proxy, a fix for more than 10 buses, and bus-creation fallbacks. +- Shimmer effect (upstream #4923, authored 2025-09-24, merged into the fork's `dev` in November). + +_Checked: 20 first-parent commits on `origin/dev` with author-date 2025-09-01..2025-09-30, via `git log --first-parent` on a local blobless clone. Releases published: none. Issues: tracker disabled on this fork._ diff --git a/docs/friend-repos/index.md b/docs/friend-repos/index.md index 60c61d88b..5da2385ea 100644 --- a/docs/friend-repos/index.md +++ b/docs/friend-repos/index.md @@ -6,6 +6,7 @@ Monthly logs of what shipped on related open-source LED projects, the live lands - [wled-WLED.md](wled-WLED.md): upstream WLED firmware. - [MoonModules-WLED-MM.md](MoonModules-WLED-MM.md): MoonModules' WLED fork (the direct lineage). - [troyhacks-WLED.md](troyhacks-WLED.md): troyhacks' personal fork of WLED-MM (PixelForge, RMTHI, audio-reactive hardening). +- [blazoncek-WLED.md](blazoncek-WLED.md): blazoncek's (Blaž Kristan, a WLED core developer) personal fork of WLED (segment blending and transitions, alpha channel, bus and HUB75 rework); the work lives on `dev`, not the default `stable`. - [Funkelfetisch-MoonLight.md](Funkelfetisch-MoonLight.md): a fork of THIS project building a commercial product on it (HELIO, a physical infinity-sphere lamp), now at `Funkelfetisch/projectMM` on GitHub; the work lives in feature branches, not on its default branch. - [PlummersSoftwareLLC-NightDriverStrip.md](PlummersSoftwareLLC-NightDriverStrip.md): Dave Plummer's LED matrix/strip firmware. - [hpwit-I2SClocklessLedDriver.md](hpwit-I2SClocklessLedDriver.md): hpwit's I2S/LCD DMA clockless LED driver (parallel multi-strip output). @@ -38,7 +39,6 @@ Observational: where the landscape is ahead of MoonLight. These are *not* commit Concrete ideas the digests surfaced, waiting for a decision. Each leaves this list when it is decided: into the [backlog](../work/future/index.md), or dropped. Study to think, write fresh, never copy. - **Run compiled MoonLive code under QEMU in CI** ([hpwit-new-parser](hpwit-new-parser.md), September 2026): a tier that executes the compiled Xtensa bytes found three compiler bugs in its first week, the class host tests cannot see. -- **Test MoonLive against hpwit's three compiler bug patterns** ([hpwit-new-parser](hpwit-new-parser.md), September 2026): a loop variable declared before its `for` freed too early, a short script heavy in external calls whose size estimate drops the final return, and an optimizer pass that removes the only definition of a register read later. - **Run every effect with the clock started near a wraparound** ([wled-WLED](wled-WLED.md), September 2026): WLED's Stream effect broke after an hour at top speed from a 32-bit time compared against a 16-bit one. - **Temporal dithering and RGBW gamut mapping at output** ([FastLED-FastLED](FastLED-FastLED.md), September 2026): applied at show time, with a power limiter that charges for the extra white diode; overlaps the parked 16-bit Layer work. - **Read WLED's "Version 17 - key features" issue (#5867)** ([wled-WLED](wled-WLED.md), September 2026): the topics a maintainer proposes for WLED's next major version, worth comparing against our own. @@ -56,7 +56,7 @@ Adding a month or a new friend repo is the [friend-repos](index.md) workflow, an > 2. **Split a month at any release boundary, but only if the release was cut from the branch you're summarising.** If a *versioned* release was published mid-month (check `git for-each-ref refs/tags` / the GitHub releases API; ignore rolling tags like `nightly` and prereleases), AND the tag is an ancestor of the digest branch (`git merge-base --is-ancestor `), split that month at the release date into `## (up to v)` / `## (post-v)`. If the tag is NOT an ancestor (the project cuts releases from a separate release branch, e.g. upstream WLED tags off `0_15`/release branches, not `main`), do NOT split: keep the month whole and just note which release shipped that month as context, since the trunk you're summarising feeds future releases rather than being the release line. Whole months with no in-branch release stay one section. > 3. Write an **end-user-readable** summary: what changed that a *user of the library* would notice or care about, new features, new hardware/platform support, notable fixes, breaking changes. Skip internal refactors, CI, test-only, and dependency bumps unless they affect users. > 4. Format as **short bullet points**, each one line, plainest language, minimal jargon. Group only if there's a natural split (e.g. "New" / "Fixed"); otherwise a flat list. -> 4b. Follow [the documentation standards](../contributing/documentation-standards.md): American spelling, **no em-dashes** (use a comma, colon, parentheses or a full stop), and one line per paragraph with no hard wraps, since `check_docgen` reports all three as errors on the file you write. +> 4b. Follow [the documentation standards](../contributing/documentation-standards.md): American spelling, **no em-dashes** (use a comma, colon, parentheses or a full stop), and one line per paragraph with no hard wraps, since `check_docgen` reports all three as errors on the file you write. Leave a blank line before every list, including one under a bold group label such as **New**: the site's Markdown otherwise renders the bullets as one paragraph of dashes. > 5. Add it as a `## ` section to `docs/friend-repos/.md`, newest month on top. Don't editorialise or compare to MoonLight, just report what they shipped. > 6. State the commit range / count **and the issue query** summarised so the digest is auditable. > 7. Then add any idea worth deciding on to **Candidates for MoonLight** in [index.md](index.md): one line each, naming the friend repo and month, linked to its digest. Here, and only here, the comparison with MoonLight belongs. diff --git a/docs/friend-repos/wled-WLED.md b/docs/friend-repos/wled-WLED.md index e193dcbba..56d822316 100644 --- a/docs/friend-repos/wled-WLED.md +++ b/docs/friend-repos/wled-WLED.md @@ -9,6 +9,7 @@ Months are **not** split at release dates: upstream WLED cuts releases from sepa A modest month on `main`: new effect and usermod options, a handful of preset, WiFi and Ethernet fixes, and many maintainer updates on how AI-assisted contributions are handled. No versioned release was published in September (v16.0.1, 2026-07-07, is the latest, cut from a release branch), so the month is not split. **New** + - Sparse LED maps can now be followed by a trailing strip, a request open since early 2026 (#5529, #5745). - A new "Dissolve Plus" user effect extends Dissolve with separate fill speed, a "last one stays lit" mode, and a fade rate. - The timers page gets a "remove" button for each timer. @@ -19,6 +20,7 @@ A modest month on `main`: new effect and usermod options, a handful of preset, W - The log warns when more effects are registered than the effect list can hold (#5827). **Fixed** + - HUB75 matrices of 128x128 are handled correctly and a divide-by-zero in the automatic brightness limiter is gone (#5826). - The active preset is no longer cleared by JSON requests that arrive during a brightness transition (#5856). - The device now reconnects when the access-point name changes, and no longer restarts the AP when the name is unchanged after saving WiFi settings (#5866). @@ -27,12 +29,14 @@ A modest month on `main`: new effect and usermod options, a handful of preset, W - Audio-reactive documentation now correctly lists the volume value as an integer, not a float. **Fixed (reported by users)** + - Peek preview no longer shows as 32x32 on a 64x64 matrix (#5852). - The MQTT topic length limit is raised (#5854). - Uploading `cfg.json` and `presets.json` through PlatformIO's filesystem image upload works (#5839). - Several long-running reports were closed: ESP8266 Wemos D1 mini unable to join the router on 14.1 (#3690), the "WLED-AP" network not appearing on NodeMCU (#5100), flicker on the second output (#2268), and Alexa sync problems on 0.15.1 (#4875). **Watching** + - A maintainer's "Version 17 - key features" issue (#5867) lists the topics proposed for the next major version: a realtime review, a new audio-reactive model, the LED bus, and a new settings UI. - The Presets UI sorting and the Time Controlled Presets dropdowns are both reported as confusing or empty on some devices (#5841, #5840, #5836). - ESP-NOW toggling freezes the CPU on 16.0 after the peer list is populated (#5871). @@ -47,11 +51,13 @@ _Auditability: 35 commits on `main` with author-date 2026-09-01..2026-09-30 via A quieter, consolidation month on `main` after July's V5 platform switch: the toolchain moved forward again, drawing and segment bugs got fixed, and a set of long-open field reports were finally closed. No versioned release was published in August, so the month is not split. **New** + - Trunk builds move to ESP-IDF 5.5.4, and the ESP32-C5 target now builds on the same platform as the rest (its NTP workaround is gone, so time settings behave normally there). - The Waveshare ESP32-S3 HUB75 build gains the SHTC3 v2 temperature and humidity sensor usermod. - Effects can now load palettes from their own code, so an effect can pick or cycle palettes itself. **Fixed** + - Circle drawing is more accurate: outlines no longer come out slightly flat, and filled circles are rounder. - Renaming a segment while effects are running no longer risks a crash on dual-core ESP32. - If the device runs out of memory at startup, it now creates a small default segment instead of showing "no segments" with most controls disabled. @@ -59,12 +65,14 @@ A quieter, consolidation month on `main` after July's V5 platform switch: the to - ESP32 chip revision is reported correctly again on the V5 platform. **Fixed (reported by users)** + - FW1906 strips no longer light the cool-white and warm-white channels on certain solid colors (#5812). - The effects list no longer loads incomplete or broken over a slow connection (#5813). - Waveshare ESP32-S3-RGB-Matrix HUB75 boards: boot failures (#5776) and swapped green and blue channels (#5815) both resolved. - Long-running strip flicker and strobe reports from the 0.15 line were closed out, including the LEDs-flashing-every-10-30-seconds thread (#4805), DRGB realtime strobing (#5512), and the effect jumping to "Copy Segment" (#5506). Also closed: white flash at full brightness before the boot preset loads (#5468), a permanently locked OTA on QuinLED Dig-Uno (#5158), and Gledopto Ethernet dropouts (#5431). **Watching** + - Adding a second LED output crashes on 17.0.0-dev; confirmed and marked major (#5770). - Two proposals are gauging community interest: PPP-over-serial as a network transport, turning the USB cable into a full network link (#5811), and a DDP compression extension for low-bandwidth links (#5810), the busiest thread of the month. - An RFC proposes rewriting the settings web UI to be schema-driven, generated from the code rather than hand-written HTML (#5792). @@ -78,6 +86,7 @@ _Auditability: 52 commits on `main` with author-date 2026-08-01..2026-08-31 (ran The month `main` switched to the **V5** platform: WLED's trunk moved from the ESP-IDF 4.4 / arduino-esp32 v2 build to ESP-IDF 5.3 / arduino-esp32 v3, and the long-running `V5` branch became the development trunk (merged July 19). Maintainers warned publicly that `main` would be unstable for a while, and the web UI now shows a "development build" banner. v16.0.1 shipped July 7 from a release branch, so the month is not split. **New** + - Trunk builds move to ESP-IDF 5.3 / arduino-esp32 v3, opening the door to the newer chips (ESP32-C5, C6 and P4 build targets ride along). - ESP-NOW now uses WLED's own code instead of the QuickESPNow library, faster, and roughly 10 KB more free memory on ESP32 (1.5 KB on ESP8266). - New `esp32_eth_V4` build for Ethernet boards; ESP32-C6 boards get 4 MB and 8 MB builds. @@ -85,12 +94,14 @@ The month `main` switched to the **V5** platform: WLED's trunk moved from the ES - Audio-reactive now compiles on all the newer chips. **Fixed** + - ESP32-C3 and S3 no longer boot-loop on the new platform (device-ID and audio-reactive builds rescued). - Usermod settings: non-pin dropdowns no longer reserve GPIO pins, which was blocking pin choices elsewhere. - Several DMX-input crashes and a robustness fix in its configuration; a possible array overrun in the Improv response. - ESP8266 minimum build shrank by 1.5 KB; the unmaintained `WLED_SAVE_RAM` build option was removed. **Watching** + - The loudest thread of the month is #5746, asking the project to do unstable work on a `dev` branch rather than breaking `main`. - Ethernet users want the WiFi access point to switch off entirely once the wired link is up (#5762). - Open v16 field reports: Animated Staircase segments switching instead of fading (#5731), APA102 on GPIO19 misbehaving (#5728), HUB75 colour-order options hidden and mostly unimplemented (#5723), and a request for current-limited LED support in the brightness limiter (#5715). @@ -102,11 +113,13 @@ _Auditability: 40 first-parent commits on `main` with author-date 2026-07-01..20 Post-16.0 stabilisation month: no new version tag (v16.0.0 shipped 2026-05-03 off a release branch, so the month is not split), just a steady stream of bugfixes and small additions landing on `main`. **New** + - HUB75 matrix panels: added a "Seengreat" pinout, plus fixes for 4-scan and chained-panel setups. - Renamed the "CW" LED type to "CCT" for clarity when configuring CCT white strips. - V5-C6 boards are now covered by the V5 build. **Fixed** + - "Rainbow" and other color-wheel effects no longer mis-drive the white channel on RGBW strips. - Restored the pre-16.0 look of several effects that had changed appearance after the 16.0 upgrade (also fixes the DJ Light intensity regression). - HUB75: restored missing pixel trails on some 2D effects (Black Hole, Lissajous, Spaceships). @@ -119,6 +132,7 @@ Post-16.0 stabilisation month: no new version tag (v16.0.0 shipped 2026-05-03 of - Better brownout detection and extended error codes aligned with WLED-MM. **Watching** + - Discussion opened on switching from plain gamma to an sRGB transfer function for better low-brightness accuracy (#5707), and on improving the Nodes/Instances page (#5711), no shipped outcome yet. - Several v16.0 field reports still open: multi-controller sync losing color (#5705), UDP sync failing in AP mode (#5709), and OTA-update trouble on some boards (#5682, #5702). diff --git a/docs/how-to/building.md b/docs/how-to/building.md index 883fb3da1..8c54f5139 100644 --- a/docs/how-to/building.md +++ b/docs/how-to/building.md @@ -4,7 +4,7 @@ How to get the system running on a desktop, an ESP32, a Teensy, or a Raspberry P ## MoonDeck — the dev console -Everything that builds, flashes, runs, tests, monitors, or checks the project — for every target — lives as a script under `moondeck/`. The full per-script reference is [moondeck/MoonDeck.md](../moondeck/MoonDeck.md). +Everything that builds, flashes, runs, tests, monitors, or checks the project, for every target, lives as a script under `moondeck/`. The full per-script reference is [moondeck/MoonDeck.md](../../moondeck/MoonDeck.md). @@ -57,9 +57,10 @@ The distinction matters because a shipped binary is launched from a download fol ### Editor setup (clangd) -Diagnostics appear **as you type**, from the same [`.clang-tidy`](../.clang-tidy) config CI uses, so a finding shows up while the code is still in your head, not ten minutes later in a pipeline. +Diagnostics appear **as you type**, from the same [`.clang-tidy`](../../.clang-tidy) config CI uses. +A finding shows up while the code is fresh in your head, not ten minutes later in a pipeline. -Once per machine: install the **clangd** extension (`llvm-vs-code-extensions.vscode-clangd`) and **disable Microsoft's C/C++ IntelliSense**, running both produces duplicated and contradictory diagnostics. Nothing else to configure: [`.clangd`](../.clangd) at the repo root points at the compilation database, and `CMAKE_EXPORT_COMPILE_COMMANDS` (set in `CMakeLists.txt`) means any normal build refreshes it. +Once per machine: install the **clangd** extension (`llvm-vs-code-extensions.vscode-clangd`) and **disable Microsoft's C/C++ IntelliSense**, running both produces duplicated and contradictory diagnostics. Nothing else to configure: [`.clangd`](../../.clangd) at the repo root points at the compilation database, and `CMAKE_EXPORT_COMPILE_COMMANDS` (set in `CMakeLists.txt`) means any normal build refreshes it. Two things worth knowing: @@ -255,7 +256,7 @@ Moving to a different release is never automatic: bump `PINNED_IDF_COMMIT` / `PI #### Adopting the v6.x ecosystem changes -v6.0 introduced ecosystem-level changes beyond the API surface. The stance, under [§ Principles → Industry standards](../CLAUDE.md#principles), is to **embrace these as the ESP32 standard**. If the IDF makes something the recognized way to build, install, provision, or ship, that is the path we want rather than a bespoke one we maintain alone. We adopt them **step by step**, each its own commit and hardware re-test, rather than all at once, and only after they clear the **v6.0-floor rule** above. The default is *yes, adopt*, with the burden of argument on *why not*. +v6.0 introduced ecosystem-level changes beyond the API surface. The stance, under [§ Principles → Industry standards](../../CLAUDE.md#principles), is to **embrace these as the ESP32 standard**. If the IDF makes something the recognized way to build, install, provision, or ship, that is the path we want rather than a bespoke one we maintain alone. We adopt them **step by step**, each its own commit and hardware re-test, rather than all at once, and only after they clear the **v6.0-floor rule** above. The default is *yes, adopt*, with the burden of argument on *why not*. Two guardrails bound the "embrace everything" stance: @@ -267,10 +268,10 @@ Where we are on each. The adoption plan and per-item triggers are filed in [back | Change | Where we are now | |---|---| | **EIM** (ESP-IDF Installation Manager) — the new default, cross-platform installer; Espressif says `install.sh` / `idf_tools.py` are "no longer needed" | `setup_esp_idf.py` drives the legacy `install.sh` / `install.bat`. Works, but is the *old* documented path. | -| **PSA Crypto** — legacy mbedTLS crypto APIs deprecated in favour of the PSA API | No direct exposure: we never call mbedTLS ourselves; OTA uses `esp_https_ota` + `esp_crt_bundle_attach` ([platform_esp32_ota.cpp](../src/platform/esp32/platform_esp32_ota.cpp)), which wrap crypto internally. | -| **`network_provisioning`** — Espressif's Unified Provisioning subsystem, renamed from `wifi_provisioning` in v6.0. Transports: **BLE (GATT)** + **Wi-Fi SoftAP**. Clients: official iOS/Android apps for both, plus `esp_prov` (a Python CLI on Linux/macOS/Windows). Transport-agnostic but ships no web/serial client. | We provision over [Improv](../src/core/system/ImprovProvisioningModule.h) — serial (USB) + BLE, driven from the **browser** (ESP Web Tools) or a serial CLI, which covers mooninstaller / no-app onboarding. The IDF-native **phone-app + SoftAP** flow is a coverage gap rather than a duplicate: the two standards meet only on BLE and own different front-ends. | +| **PSA Crypto**: legacy mbedTLS crypto APIs deprecated in favor of the PSA API | No direct exposure: we never call mbedTLS ourselves; OTA uses `esp_https_ota` + `esp_crt_bundle_attach` ([platform_esp32_ota.cpp](../../src/platform/esp32/platform_esp32_ota.cpp)), which wrap crypto internally. | +| **`network_provisioning`**: Espressif's Unified Provisioning subsystem, renamed from `wifi_provisioning` in v6.0. Transports: **BLE (GATT)** + **Wi-Fi SoftAP**. Clients: official iOS/Android apps for both, plus `esp_prov` (a Python CLI on Linux/macOS/Windows). Transport-agnostic but ships no web/serial client. | We provision over [Improv](../../src/core/system/ImprovProvisioningModule.h): serial (USB) + BLE, driven from the **browser** (ESP Web Tools) or a serial CLI, which covers mooninstaller / no-app onboarding. The IDF-native **phone-app + SoftAP** flow is a coverage gap rather than a duplicate: the two standards meet only on BLE and own different front-ends. | | **CMake Build System v2** — the named successor to the current build system; technical preview in v6.0/6.1, has its own migration guide | Standard `idf.py` build (v1). Our component is a thin `idf_component_register()` wrapper, so the migration surface is small. | -| **Built-in MCP server** (`idf.py mcp-server`) — lets an AI assistant drive build/flash/monitor/debug directly | Not used. Agents and humans both go through the `moondeck//*.py` layer (the uniform interface in [moondeck/MoonDeck.md](../moondeck/MoonDeck.md)), which wraps pin-drift checks, per-firmware build dirs, and KPI collection. | +| **Built-in MCP server** (`idf.py mcp-server`): lets an AI assistant drive build/flash/monitor/debug directly | Not used. Agents and humans both go through the `moondeck//*.py` layer (the uniform interface in [moondeck/MoonDeck.md](../../moondeck/MoonDeck.md)), which wraps pin-drift checks, per-firmware build dirs, and KPI collection. | The general rule: **anything already in v6.0 we adopt proactively** (it clears the floor), while **preview / not-yet-in-v6.0 features wait** until they are stable *and* in our floor. Each adoption is its own commit with its own hardware re-test, and none may regress the Teensy / desktop paths. @@ -278,7 +279,7 @@ The general rule: **anything already in v6.0 we adopt proactively** (it clears t `build_esp32.py --firmware` selects one of the shipping variants. The key combines chip name + feature flags + (for SKU-sensitive chips) module. ("Firmware" here is the compiled binary; the physical product (deviceModel) is a separate concept: see [MoonInstaller, firmware vs deviceModel vs board](../explanation/architecture/mooninstaller.md#the-three-words).) `build_esp32.py --help` lists the full set. -The canonical list is the **`FIRMWARES` dict** in [`moondeck/build/build_esp32.py`](../moondeck/build/build_esp32.py) — the single source of truth, carrying each variant's `chip`, sdkconfig `fragments`, `eth_only`, `ships`, and `description`. Its machine-readable projection is [`mooninstaller/firmwares.json`](../mooninstaller/firmwares.json) (generated by `generate_firmwares.py`, drift-guarded by `check_firmwares.py`), which the CI release matrix, the ESP Web Tools manifest loops, and MoonDeck all read — so the list lives in exactly one place. `esp32p4rev1-eth-wifi` has `ships: false` (its C6-slave Kconfig isn't reproducible in CI yet), so it builds from the CLI but stays out of the release matrix. +The canonical list is the **firmware registry**, [`moondeck/build/firmwares.py`](../../moondeck/build/firmwares.py). It joins the `FIRMWARES` dicts in [`build_esp32.py`](../../moondeck/build/build_esp32.py) and [`build_teensy.py`](../../moondeck/build/build_teensy.py) and tags every row with its `platform`. An ESP32 slice also keeps its sdkconfig `fragments`. Its machine-readable projection is [`mooninstaller/firmwares.json`](../../mooninstaller/firmwares.json), generated by `generate_firmwares.py` and drift-guarded by `check_firmwares.py`. The CI release matrix, the manifest loops, the Teensy builds and MoonDeck all read it, so the list lives in exactly one place. A row with `ships: false` builds from the CLI but stays out of the release matrix. ESP-IDF v6.x has no `CONFIG_ESP_WIFI_ENABLED` switch (the symbol is forced on for WiFi-capable SoCs), so dropping WiFi at compile time happens via `EXCLUDE_COMPONENTS` plus `MM_NO_WIFI` (set when `MM_ETH_ONLY=1`, applied in `esp32/main/CMakeLists.txt`). The `esp32-eth` variant takes this path; the default `esp32` keeps both stacks compiled in and uses the runtime cascade in `NetworkModule` (Ethernet first, WiFi fallback when no PHY responds). @@ -308,7 +309,7 @@ curl --http1.1 -H "Expect:" --data-binary @build/esp32-/.bin \ http:///api/firmware/upload ``` -`.bin` is the app image in that build directory; [flash_esp32](../moondeck/MoonDeck.md#flash_esp32) names it. +`.bin` is the app image in that build directory; [flash_esp32](../../moondeck/MoonDeck.md#flash_esp32) names it. The second request ends with **no HTTP status** (curl reports 000): the device reboots into the new image as the write completes, so the socket closes before a response arrives. That is success, not failure. Confirm by reading the build back: @@ -334,9 +335,19 @@ older layout adopts a new one only through a full serial flash (see the note und ## Teensy -Teensy 4.x is in the supported target list. Buffers and pipeline configuration scale to 1 MB of internal RAM; OctoWS2811 gives excellent DMA-based LED output. Ethernet is built in on Teensy 4.1 and optional on 4.0. +One firmware runs on a Teensy 4.1, `teensy41`: it uses the board's own Ethernet, or on the 4.1-NE, which has none, a W5500 module, found at boot. +Pins, wiring and power are on [the Teensy 4.1 page](../reference/hardware/teensy-4-1.md). +It builds with CMake over an SDK at pinned versions, and MoonDeck's [Teensy tab](../../moondeck/MoonDeck.md#teensy-tab) wraps every step: -Build flow is via the root `CMakeLists.txt` with a Teensy toolchain file. The platform layer for Teensy is added when the first hardware target is wired in. +```bash +uv run moondeck/build/setup_teensy.py # once: the SDK into ~/teensy +uv run moondeck/build/build_teensy.py # build/teensy41/MoonLight.hex +uv run moondeck/build/flash_teensy.py # over USB, no button press +uv run moondeck/run/monitor_teensy.py # USB serial +``` + +Settings live in 1 MB of the program flash and survive a flash and a network update. A running board also updates over the network: upload the `.bin` on the Firmware card, or `curl -H "Expect:" --data-binary @MoonLight.bin http:///api/firmware/upload`. +Each release also carries the image as `firmware-teensy41-v.hex`, for `teensy_loader_cli` or PJRC's Teensy app, and flat as a `.bin` for the network update and the web installer. ## Pre-compilation steps @@ -353,4 +364,4 @@ Both are defined in the root `CMakeLists.txt` (desktop) and `esp32/main/CMakeLis The system serves the web UI from its embedded HTTP server. Open `http:///` in a browser; on desktop that's typically `http://localhost:8080/`. From the UI you can change effects and modifiers, configure controls, see the 3D preview. The settings persist across reboot. -To run the test suite or any of the checks (platform boundary, specs, KPI), see the MoonDeck reference linked above. The release-readiness gates that wrap these into a checklist live in [CLAUDE.md § The Process](../CLAUDE.md#the-process). +To run the test suite or any of the checks (platform boundary, specs, KPI), see the MoonDeck reference linked above. The release-readiness gates that wrap these into a checklist live in [CLAUDE.md § The Process](../../CLAUDE.md#the-process). diff --git a/docs/index.md b/docs/index.md index 9dfc39693..5ef020c33 100644 --- a/docs/index.md +++ b/docs/index.md @@ -56,7 +56,7 @@ One source tree drives ESP32, Teensy, Raspberry Pi, macOS, Windows and Linux. Measured frame rates per device, how the project works, why the code is ours, and who inspired what. - [Performance](reference/performance.md) · [Why our own code](explanation/why-we-write-our-own.md) · [How we work](https://github.com/MoonModules/MoonLight#how-we-work) · [Credits](https://github.com/MoonModules/MoonLight#credits) + [Performance](reference/performance.md) · [Why our own code](explanation/why-we-write-our-own.md) · [Security](explanation/security.md) · [How we work](https://github.com/MoonModules/MoonLight#how-we-work) · [Credits](https://github.com/MoonModules/MoonLight#credits) diff --git a/docs/moonmodules/core/services.md b/docs/moonmodules/core/services.md index 302112507..162f52c04 100644 --- a/docs/moonmodules/core/services.md +++ b/docs/moonmodules/core/services.md @@ -175,7 +175,7 @@ A board's microSD slot, where one is wired: its wiring, a usage gauge and a form - `bus`: under `Custom`, the SD bus or the SD spec's SPI mode. - `clkPin` … `pwrPin`: the pins under `Custom`; −1 unused, `d1`-`d3` unset is 1-bit. - `format`: a button, press twice; erases and reformats the card as FAT32 in place. -- `card (KiB)`: read-only usage bar, used / total KiB, so a card past 4 GiB still reports correctly. +- `card`: read-only usage bar, counted in KiB so a card past 4 GiB still reports correctly. Detail: [technical](moxygen/SdCardService.md) @@ -344,7 +344,7 @@ The status line reports setup state ("set pin to receive" / "ready"), the learn ## SD Card, details -The presets: `P4 SDMMC` is the slot every P4 board with one wires, and a P4 opens on it. `Classic SDMMC (1-bit)` is the classic's own SD host pins, which the Olimex ESP32-Gateway wires, and `Classic SPI` the QuinLED Dig-Octa's. A 4 MB image carries no SD at all, so the 4 MB Olimex has none. SPI mode maps `clkPin` to SCK, `cmdPin` to MOSI, `d0Pin` to MISO and `d3Pin` to CS. Over SPI, reads hold but a write over about 50 KB can stop the card until a power cycle, so a card mounted that way says so on its status. +The presets: `P4 SDMMC` is the slot every P4 board with one wires, and a P4 opens on it. `Classic SDMMC (1-bit)` is the classic's own SD host pins, which the Olimex ESP32-Gateway wires, and `Classic SPI` the QuinLED Dig-Octa's. A 4 MB image carries no SD at all, so the 4 MB Olimex has none. `Teensy 4.1 SD socket` is the Teensy 4.1's built-in slot on its own SDIO host, and a Teensy opens on it; it mounts FAT and exFAT, and its format runs in the call, pausing the render for its few seconds, since the Teensy has no task to hand it to. SPI mode maps `clkPin` to SCK, `cmdPin` to MOSI, `d0Pin` to MISO and `d3Pin` to CS. Over SPI, reads hold but a write over about 50 KB can stop the card until a power cycle, so a card mounted that way says so on its status. Mounts live, in `prepare()` rather than `setup()`: editing a pin unmounts and remounts on the new wiring, no reboot needed, the same contract the [I2C bus](system.md#i2c-bus)'s `sda`/`scl` have. A pulled card is noticed within a second. A missing or unreadable card shows its own status instead of retrying, since a mount attempt on an empty slot stalls the render thread: insert the card, then switch the module off and on, edit a pin, or reboot. diff --git a/docs/moonmodules/core/system.md b/docs/moonmodules/core/system.md index 61379ad18..b7b697eff 100644 --- a/docs/moonmodules/core/system.md +++ b/docs/moonmodules/core/system.md @@ -17,7 +17,7 @@ The device's identity and vitals: name (behind mDNS `.local`, the SoftAP S - `mode`: how much of the UI is shown, `user`, `expert` (🎚️) or `developer` (🔧), cumulative. - `logLevel`: serial verbosity, defaulting to Warn. The first 60 s always logs at Info. - read-only rate: `uptime`, `fps`, `tickTimeUs`. -- read-only memory: `heap`, `psram`, `maxBlock`, `flash`. +- read-only memory: `heap`, `psram`, `maxBlock`, `flash`; `psramType` and `flashMode` in expert mode. - read-only identity: `mac`, `chip`, `cpu`, `sdk`, `bootReason`. - `factory reset`, pressed twice: back to freshly installed, scripts kept. @@ -52,7 +52,7 @@ The wired interface, a child of Network: which board's wiring it uses and how it Ethernet module controls -- `ethBoard`: the board's Ethernet wiring by name; `Custom` exposes every pin. +- `ethBoard`: the Ethernet wiring by name; `Custom` shows every pin, a fixed interface none. - `ipSettings`: DHCP or Static; Static reveals `ip`, `gateway`, `subnet` and `dns`. Gateway and DNS are optional. A Static setting that cannot work, such as a gateway outside the subnet, is not used: the interface keeps DHCP and the card says why. Changing the addressing of the interface the page reaches the device through asks first, naming where the device will be. @@ -240,7 +240,7 @@ Browse and manage the device filesystem: a folder tree with an inline text edito Known renames from [MIGRATING.md](../../reference/MIGRATING.md) apply before upload, then a report lists what needs an eye. Every file applies as it lands, bar network settings and the web server's `port`, which the dialog names. - `show hidden`: reveal dot-prefixed files and folders, such as `.config`. -- `filesystem`: read-only usage bar (used / total bytes, from the platform). +- `flash`: read-only usage bar (used / total bytes, from the platform). - `lastSaved`: read-only; how long ago config was persisted (read from the Filesystem engine). Detail: [technical](moxygen/FileManagerModule.md) diff --git a/docs/moonmodules/core/ui.md b/docs/moonmodules/core/ui.md index 322a44b60..49733c4cb 100644 --- a/docs/moonmodules/core/ui.md +++ b/docs/moonmodules/core/ui.md @@ -48,7 +48,7 @@ Fixed top, 44 px, left-to-right: 5. **System stats**, `uptime · NN K free` (uptime `Xd Yh Zm Ws`, free heap KB), read from the SystemModule controls in the WS state push; no separate polling endpoint. 6. **WebSocket dot**, green = connected, gray = disconnected. The socket auto-reconnects with - exponential backoff (500 ms → 5 s) on close, and a page reload covers the rare half-dead socket. + exponential backoff from 200 ms to a 5 s ceiling on close, and a page reload covers the rare half-dead socket. 7. **Reboot button (⏻)**, red border via `data-crashed="true"` when `SystemModule.bootReason` is PANIC / INT_WDT / TASK_WDT / BROWNOUT. Press-twice to confirm: the first click arms it (solid red), a second sends `POST /api/reboot`. Disarms after 3 s or on pointer-leave, no `confirm()` popup. 8. **Theme toggle (☀/🌙)**, flips `[data-theme]` on ``; preference in `localStorage['mm_theme']`. @@ -67,7 +67,7 @@ A left column listing the root modules, one entry per top-level MoonModule. - **Footer** pinned to the bottom of the nav: social links (GitHub, Discord, Reddit, YouTube, inline SVG) and a `© MoonLight` line. -The WS state push carries the whole module tree; only the selected root is rendered, and `updateValues()` patches just the visible cards (non-rendered roots have no DOM nodes, so their data is silently ignored). Filtering per root server-side buys little: the full tree arrives once on connect and each second after that carries only what changed, built through a streaming sink, so tree size is not a buffer-limit concern. +The page subscribes to what it shows: the selected root, plus by type the modules it reads outside that root (`ALWAYS_SUBSCRIBED`: System for the status bar and mode, FirmwareUpdate for the update badge, MoonCloud for the relay, PreviewDriver for the frame rate, MidiService for the MIDI bridge). The device answers with those modules in full and an outline of the rest, names and shape without controls, which is all the nav reads. Opening another root sends a new subscription, and its cards appear when the answer arrives; until then the previous cards stay. The control-surface target picker offers every module's controls, so assign mode fetches the whole tree from `/api/state`. ## Module card @@ -120,7 +120,7 @@ Auto-rendered by `controls[].type`, adding a MoonModule with these types needs n | `display` (read-only) | static text | WS push updates in place | n/a | | `display-int` (read-only int + unit) | formatted text (`-58 dBm`) | unit suffix set device-side at `addReadOnlyInt` time, carried in the descriptor's `aux` slot | n/a | | `time` (read-only seconds) | formatted text (`1d 4h 27m 13s`) | WS push updates | n/a | -| `progress` | bar + numeric `X / max` | WS push updates | n/a | +| `progress` | bar + `used / total`, sized B, KB, MB or GB | WS push updates | n/a | | `ipv4` | text input (dotted-quad) | server validates (`parseDottedQuad`), 400 on malformed; stored as 4 octets device-side | n/a | | `button` | clickable button | sends value = 1 on click | none | @@ -177,7 +177,7 @@ The light-domain renderer (WebGL point cloud, the `0x03`/`0x02` frame format, or ## State updates, the no-rebuild contract -When a WS state message arrives, the full tree on connect or a patch after it, for each module in the payload: +When a WS state message arrives, a full state answering a subscription or a patch after it, for each module in the payload: - If `controls.deviceName` exists, update the status-bar device name and `document.title`. - For each `controls[key]`: look up the matching ``/``/``/`