Backup RFP Infinity controller state before Resolume changes
Some checks failed
WLED CI / wled_build (push) Has been cancelled
Deploy Nightly / wled_build (push) Has been cancelled
Deploy Nightly / Deploy nightly (push) Has been cancelled

This commit is contained in:
jan
2026-05-14 12:31:13 +02:00
parent ebc4498d89
commit 4bc4e1257e
33 changed files with 3482 additions and 695 deletions

View File

@@ -6,11 +6,22 @@ Build target:
- `rfp_esp32s3_wroom1_n16r8_3x106`
Firmware release name:
- `RFP_N16R8_NODE3x106_V20260511E`
Default output pins:
- Output 1: `GPIO4`
- Output 2: `GPIO5`
- Output 3: `GPIO6`
- No relay pin is used in the tracked node target (`RLYPIN=-1`)
Boot stabilization:
- The node target uses the production N16R8 boot settings from `platformio.ini`.
- `WLED_BOOTUPDELAY=1200` gives the power rail time to settle on cold start.
- There is no separate `coldboot` target anymore.
Pins intentionally avoided:
@@ -37,17 +48,55 @@ Build only with the helper script:
.\tools\flash_rfp_s3.ps1 -BuildOnly
```
Linux setup (one-time, includes build toolchain + frontend dependencies):
```bash
./tools/setup_rfp_env.sh
```
Linux manual build (if setup is already done):
```bash
PATH=/home/jan/Documents/RFP/Finanz_App/node/current/bin:$PATH \
NPM_CONFIG_CACHE=$PWD/.npm-cache \
PLATFORMIO_CORE_DIR=$PWD/.piohome \
PLATFORMIO_PACKAGES_DIR=$PWD/.piohome/packages \
PLATFORMIO_PLATFORMS_DIR=$PWD/.piohome/platforms \
PLATFORMIO_CACHE_DIR=$PWD/.piohome/.cache \
PLATFORMIO_BUILD_CACHE_DIR=$PWD/.piohome/buildcache \
.venv/bin/python -m platformio run -e rfp_esp32s3_wroom1_n16r8_3x106
```
Discover all WLED nodes in the local network and save host list:
```bash
.venv/bin/python tools/rfp_network_flash.py discover
```
Flash all discovered nodes sequentially over OTA:
```bash
.venv/bin/python tools/rfp_network_flash.py flash \
--firmware build_output/release/WLEDMM_14.7.2-mdev_RFP_N16R8_NODE3x106_V20260511E.bin \
--targets-file tools/discovered_wled_hosts.txt \
--expect-release RFP_N16R8_NODE3x106_V20260511E \
--skip-validation
```
Alternative: discover and flash in one command:
```bash
.venv/bin/python tools/rfp_network_flash.py flash \
--discover \
--firmware build_output/release/WLEDMM_14.7.2-mdev_RFP_N16R8_NODE3x106_V20260511E.bin \
--expect-release RFP_N16R8_NODE3x106_V20260511E \
--skip-validation
```
Local Wi-Fi defaults:
- Keep SSID and password in the ignored file `wled00/my_config.h`.
- If the file does not exist yet, create it with your local values:
```cpp
#pragma once
#define CLIENT_SSID "your-ssid"
#define CLIENT_PASS "your-password"
```
- SSID `RFPLicht` and the RFP password are compiled into the tracked RFP node target.
- A full `erase_flash` removes saved runtime settings, but the firmware defaults can still join the show Wi-Fi.
Important:

View File

@@ -0,0 +1,95 @@
# Infinity Controller auf WLED-MM-Basis
## Zielbild
Die Installation wird als dedizierter Master-ESP plus sechs WLED-MM Nodes betrieben. Die sechs Nodes rendern lokal auf je drei Ausgaengen mit je 106 LEDs. Der Master empfaengt Web-UI und DMX/grandMA-Steuerung und verteilt nur synchronisierte Szenenparameter, keine laufenden Pixelstreams.
## Bestehende Basis
- Node-Firmware-Target: `rfp_esp32s3_wroom1_n16r8_3x106`
- Node-Ausgaenge: `GPIO4`, `GPIO5`, `GPIO6`
- Pixel pro Ausgang: `106`, `106`, `106`
- Logische Node-Segmente: `top = 0-105`, `middle = 106-211`, `bottom = 212-317`
- DDP bleibt Debug/Fallback und wird nicht als Show-Sync verwendet.
## Implementierungsplan
- Node-Target beibehalten und um Infinity-Node-Rolle erweitern.
- Neues Master-Target `rfp_esp32s3_wroom1_n16r8_master` anlegen.
- Master-IP-Default als `192.168.178.10` dokumentieren und im Infinity-Modul verwenden.
- Node-IP-Defaults als `192.168.178.11` bis `192.168.178.16` verwenden.
- Physische DMX-Eingabe am Master ueber `WLED_ENABLE_DMX_INPUT` aktivieren.
- DMX-Defaultpins: `RX=16`, `TX=17`, `EN=18`, `UART=2`.
- Eigenes UDP-Protokoll `Infinity Sync v1` getrennt von WLED-Notifier und DDP implementieren.
- Web-UI unter `/infinity` und JSON API unter `/json/infinity` bereitstellen.
- DMX-Modus `DMX_MODE_INFINITY` mit 32 Kanaelen einbauen.
## Sync-Modell
Der Master sendet `ClockSync`, `SceneState` und `BeatTrigger` per UDP-Unicast. Nodes senden `NodeStatus` zurueck. Nodes rendern lokal gegen `master_time_us`, dadurch bestimmt nicht die Paketankunft den sichtbaren Frame.
SceneState enthaelt:
- Effekt-ID
- Preset-ID
- Brightness
- Speed
- Intensity
- Palette
- Primaer-/Sekundaer-/Tertiaerfarbe
- Group-Mask
- Direction
- Seed
- Phase
- Transition
- `apply_at_us`
## DMX/grandMA Personality v1
| Kanal | Funktion |
| ---: | --- |
| 1 | Dimmer |
| 2 | Enable / Blackout |
| 3 | Preset |
| 4 | Effect |
| 5 | Speed |
| 6 | Intensity |
| 7 | Palette |
| 8 | Hue |
| 9 | Saturation |
| 10 | Value |
| 11 | Direction / Flags |
| 12 | Transition |
| 13 | Group Mask |
| 14 | Top Dimmer |
| 15 | Middle Dimmer |
| 16 | Bottom Dimmer |
| 17 | Beat Trigger |
| 18 | Sync Reset |
| 19 | Custom 1 |
| 20 | Custom 2 |
| 21 | Custom 3 |
| 22 | Seed |
| 23 | Reserved |
| 24 | Safety / Fade |
| 25-32 | Reserved |
## Testplan
- Node-Target bauen.
- Master-Target bauen.
- Fresh-Config: drei Busse mit je 106 LEDs pruefen.
- Segment-Test: Top, Middle, Bottom einzeln blinken lassen.
- Sync-Test: zwei Nodes, danach sechs Nodes mit gleichem `apply_at_us`.
- DMX-Test: physisches DMX steuert Master-State und Nodes folgen ohne DDP.
- Reconnect-Test: Node rebootet und uebernimmt aktuellen SceneState.
- Failsafe-Test: Master weg, DMX weg, Netzwerk weg, Paketverlust.
- Show-Test: mindestens 8 Stunden Dauerbetrieb.
## Annahmen
- Ein siebter ESP32-S3 wird als Master verwendet.
- Node-Reihenfolge bleibt `node-01` bis `node-06`.
- Node-IPs bleiben `.11` bis `.16` im Netz `192.168.178.0/24`.
- LED-Reihenfolge bleibt `top/GPIO4`, `middle/GPIO5`, `bottom/GPIO6`.
- Lokale bestehende Aenderungen im WLED-MM-Repo werden nicht zurueckgesetzt.

View File

@@ -0,0 +1,84 @@
# RFP Local Infinity Visualizer
This visualizer runs on the laptop and uses the master only as a JSON source.
It is the preferred development tool when you only have the master controller
available and want to simulate the six render nodes.
## Layout
The visualizer mirrors the physical installation:
- `6` columns are the six ESP nodes, left to right: `ESP1` to `ESP6`
- `3` rows are the three outputs per ESP
- top row: `UART6`
- middle row: `UART5`
- bottom row: `UART4`
- each output is drawn as one square frame with `106` LEDs
- LED order per frame follows the drawing: top edge left-to-right, right edge
top-to-bottom, bottom edge right-to-left, left edge bottom-to-top
- edge split is `25` LEDs on top, `27` LEDs right, `27` LEDs bottom,
`27` LEDs left
## Start
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
.venv/bin/python tools/infinity_visualizer_server.py \
--master 10.42.0.213 \
--port 8765
```
Open the URL printed by the tool, normally:
```text
http://127.0.0.1:8765/?master=10.42.0.213
```
If port `8765` is already in use, the tool automatically tries the next free
port and prints that URL instead, for example:
```text
Port 8765 is busy, using 8766 instead.
Infinity visualizer: http://127.0.0.1:8766/?master=10.42.0.213
```
## Why Local
The ESP32 master webserver should stay focused on WLED and Infinity control.
The local visualizer avoids loading extra HTML, JavaScript, canvas rendering,
or polling logic from the ESP itself.
The local server proxies:
```text
http://10.42.0.213/json/infinity
```
to:
```text
http://127.0.0.1:8765/api/infinity
```
This also avoids browser CORS issues.
## Controls
- `Master IP`: change the master target without restarting the server
- `Connect`: fetch state immediately
- `Pause`: freeze polling and animation
- `Master UI`: open the master `/infinity` page
## Notes
- This is not yet a bit-exact WLED renderer. It reads `/json/eff` from the
master and maps effect names to browser preview classes.
- Currently supported preview classes include `Solid`, `Blink`, `Strobe`,
`Breathe/Fade`, `Wipe`, `Scan`, `Chase/Running/Theater`, `Rainbow/Colorloop`,
`Fire`, `Twinkle/Sparkle/Glitter`, and `Noise/Plasma/Waves/Ripple`.
- Unknown effects are shown as a generic moving color blend so they remain
visible while we decide which effects need exact custom previews.
- It is a lightweight development preview for scene state, colors, row dimmers,
effect IDs, brightness, and virtual node layout.
- The simulated layout is six nodes, each with three rows and 106 LEDs.

View File

@@ -1,28 +1,42 @@
# RFP Infinity Flashing
This document covers flashing for both the Infinity master and the ESP32-S3 render nodes.
This document covers the two production firmware targets for the Infinity installation.
There is intentionally no separate `coldboot` firmware anymore: the reliable N16R8
boot settings are part of the normal master and node targets.
## Targets
- Master target: `rfp_esp32s3_wroom1_n16r8_master`
- Conservative master cold-boot target: `rfp_esp32s3_wroom1_n16r8_master_coldboot`
- Standard node target: `rfp_esp32s3_wroom1_n16r8_3x106`
- Conservative cold-boot test target: `rfp_esp32s3_wroom1_n16r8_3x106_coldboot`
- Node target: `rfp_esp32s3_wroom1_n16r8_3x106`
Use a cold-boot target when a board only starts reliably after pressing `RESET`
following a long power loss.
## Firmware Release Names
These names are compiled into the firmware as `WLED_RELEASE_NAME` and show up in
WLED update metadata, OTA validation, serial boot logs, and `/json/info`.
- Master: `RFP_N16R8_MASTER_V20260511E`
- Node: `RFP_N16R8_NODE3x106_V20260511E`
Both targets use the same robust ESP32-S3-WROOM-1 N16R8 boot configuration:
- Flash: QIO / 16 MB
- Flash frequency: 80 MHz
- PSRAM: OPI / 8 MB octal
- Partition table: `tools/WLED_ESP32_16MB.csv`
- USB CDC on boot: disabled
- Boot delay: `WLED_BOOTUPDELAY=1200`
## Clean-Flash Warning
`erase_flash` removes the complete flash contents, including WLED's saved
`cfg.json`, Wi-Fi credentials, static IP settings, presets, and filesystem data.
The RFP master and node targets now compile the show Wi-Fi as firmware defaults:
The RFP master and node targets compile the show Wi-Fi as firmware defaults:
- SSID: `RFPLicht`
- Password: configured in the RFP build flags
After a full erase the board can therefore join Wi-Fi again, but any runtime-only
After a full erase the board can therefore join Wi-Fi again, but runtime-only
settings that were stored through the WLED UI must be re-applied unless they are
also encoded as firmware defaults.
@@ -33,66 +47,13 @@ also encoded as firmware defaults.
- Runs the `/infinity` web UI
- Accepts DMX and web commands
- Sends Infinity Sync packets to the nodes
- Keeps one dummy WLED pixel on `GPIO21` so the regular WLED UI remains
valid
- Keeps one dummy WLED pixel on `GPIO21` so the regular WLED UI remains valid
- Keeps the real WLED status pixel exclusively on `GPIO48`
- Repairs old master `cfg.json` LED-bus entries so `GPIO48` is not reused as
a normal LED output
- Nodes:
- Usually `192.168.178.11` to `192.168.178.16`
- Render the LED output locally
- Receive Infinity Sync from the master
The flash procedure is similar for both roles, but the PlatformIO target and `firmware.bin` are different.
## WLED Backup Mode
The regular WLED UI is intentionally kept available as a fallback.
Important behavior:
- The master uses the WLED UI only for a dummy backup pixel on `GPIO21`; the
actual onboard status pixel remains WLED's normal status pixel on `GPIO48`.
- The master does not render the show LEDs directly.
- The nodes can still be controlled through their regular WLED UI.
- If Infinity Sync is enabled, the master sends scene state about every `100 ms`.
- While those packets arrive, the node UI may appear to ignore changes because
the next Infinity packet overwrites the local WLED state.
Use regular WLED control as backup in one of these ways:
1. Preferred: open `/infinity` on the master and use the mode button in the
top bar:
- `Show Mode: ON` means Infinity Sync is active.
- `WLED Backup: ON` means Infinity Sync is stopped and regular WLED control
can be used.
2. Or stop Infinity on the master by API:
```bash
curl -X POST http://192.168.178.10/json/infinity \
-H 'Content-Type: application/json' \
-d '{"enabled":false}'
```
3. Or disable Infinity on one node for local testing:
```bash
curl -X POST http://192.168.178.11/json/infinity \
-H 'Content-Type: application/json' \
-d '{"enabled":false}'
```
Re-enable later:
```bash
curl -X POST http://192.168.178.10/json/infinity \
-H 'Content-Type: application/json' \
-d '{"enabled":true}'
```
For hotspot testing, replace the IPs with the current addresses, for example
`10.42.0.213`.
- Use three LED outputs: `GPIO4/5/6`, each with `106` LEDs
## Build: Master
@@ -113,33 +74,11 @@ Master firmware output:
```text
.pio/build/rfp_esp32s3_wroom1_n16r8_master/firmware.bin
```
Conservative master cold-boot build:
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
PATH=/home/jan/Documents/RFP/Finanz_App/node/current/bin:$PATH \
NPM_CONFIG_CACHE=$PWD/.npm-cache \
PLATFORMIO_CORE_DIR=$PWD/.piohome \
PLATFORMIO_PACKAGES_DIR=$PWD/.piohome/packages \
PLATFORMIO_PLATFORMS_DIR=$PWD/.piohome/platforms \
PLATFORMIO_CACHE_DIR=$PWD/.piohome/.cache \
PLATFORMIO_BUILD_CACHE_DIR=$PWD/.piohome/buildcache \
.venv/bin/python -m platformio run -e rfp_esp32s3_wroom1_n16r8_master_coldboot
```
Master cold-boot firmware output:
```text
.pio/build/rfp_esp32s3_wroom1_n16r8_master_coldboot/firmware.bin
build_output/release/WLEDMM_14.7.2-mdev_RFP_N16R8_MASTER_V20260511E.bin
```
## Build: Nodes
Standard build:
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
@@ -153,10 +92,74 @@ PLATFORMIO_BUILD_CACHE_DIR=$PWD/.piohome/buildcache \
.venv/bin/python -m platformio run -e rfp_esp32s3_wroom1_n16r8_3x106
```
Cold-boot test build:
Node firmware output:
```text
.pio/build/rfp_esp32s3_wroom1_n16r8_3x106/firmware.bin
build_output/release/WLEDMM_14.7.2-mdev_RFP_N16R8_NODE3x106_V20260511E.bin
```
## Recommended Update: Master USB First, Nodes Via Master
Use this for normal show updates. The laptop only needs USB access to the
master; it does not need to join the RFP Wi-Fi. The workflow is:
1. Build master and node firmware locally.
2. Flash the master app by USB.
3. Hard-reset the master and verify its RFP release over the serial relay.
4. Stream node OTA updates through the master to nodes `.11` to `.16`.
The master USB flash deliberately does **not** run `erase_flash` and does
**not** run `uploadfs`. This avoids rewriting the large LittleFS area during a
normal firmware update and keeps runtime config/filesystem data intact.
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
.venv/bin/python tools/rfp_update_master_usb_then_nodes.py --port /dev/ttyACM0
```
Use existing build artifacts without rebuilding:
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
.venv/bin/python tools/rfp_update_master_usb_then_nodes.py --port /dev/ttyACM0 --no-build
```
Resume node updates after a failure:
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
.venv/bin/python tools/rfp_update_master_usb_then_nodes.py --port /dev/ttyACM0 --no-build --nodes-only --start-from 192.168.178.14
```
Flash only the master with the same safe USB app-flash path:
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
./flash_master.sh
```
`flash_master.sh` is now intentionally an app/bootloader/partition update only:
no full erase and no filesystem upload. Use a full clean flash only when the
partition table or filesystem must be deliberately reset.
## USB App Flash: Master
Recommended helper:
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
./flash_master.sh
```
Manual flow:
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
ls /dev/ttyACM* /dev/ttyUSB* 2>/dev/null
PORT=/dev/ttyACM0
ENV=rfp_esp32s3_wroom1_n16r8_master
PATH=/home/jan/Documents/RFP/Finanz_App/node/current/bin:$PATH \
NPM_CONFIG_CACHE=$PWD/.npm-cache \
@@ -165,154 +168,13 @@ PLATFORMIO_PACKAGES_DIR=$PWD/.piohome/packages \
PLATFORMIO_PLATFORMS_DIR=$PWD/.piohome/platforms \
PLATFORMIO_CACHE_DIR=$PWD/.piohome/.cache \
PLATFORMIO_BUILD_CACHE_DIR=$PWD/.piohome/buildcache \
.venv/bin/python -m platformio run -e rfp_esp32s3_wroom1_n16r8_3x106_coldboot
```
.venv/bin/python -m platformio run -e "$ENV"
Node firmware outputs:
```text
.pio/build/rfp_esp32s3_wroom1_n16r8_3x106/firmware.bin
.pio/build/rfp_esp32s3_wroom1_n16r8_3x106_coldboot/firmware.bin
```
## WLAN Flash: Master
Flash the master by OTA:
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
.venv/bin/python tools/rfp_network_flash.py flash \
--targets 192.168.178.10 \
--firmware .pio/build/rfp_esp32s3_wroom1_n16r8_master/firmware.bin
```
## WLAN Flash: Single Node
Standard firmware to one node:
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
.venv/bin/python tools/rfp_network_flash.py flash \
--targets 192.168.178.11 \
--firmware .pio/build/rfp_esp32s3_wroom1_n16r8_3x106/firmware.bin
```
## WLAN Flash: Group
Flash all six nodes in order:
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
.venv/bin/python tools/rfp_network_flash.py flash \
--targets 192.168.178.11,192.168.178.12,192.168.178.13,192.168.178.14,192.168.178.15,192.168.178.16 \
--start-from 192.168.178.11 \
--firmware .pio/build/rfp_esp32s3_wroom1_n16r8_3x106/firmware.bin
```
Resume a failed OTA run from a specific node:
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
.venv/bin/python tools/rfp_network_flash.py flash \
--targets 192.168.178.11,192.168.178.12,192.168.178.13,192.168.178.14,192.168.178.15,192.168.178.16 \
--start-from 192.168.178.14 \
--firmware .pio/build/rfp_esp32s3_wroom1_n16r8_3x106/firmware.bin
```
## WLAN Flash: Full Installation
Build standard OTA firmware locally, flash all six nodes sequentially, then flash
the master last:
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
.venv/bin/python tools/rfp_update_all_ota.py
```
Dry-run without building or flashing:
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
.venv/bin/python tools/rfp_update_all_ota.py --dry-run --no-build
```
Resume after an interrupted run:
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
.venv/bin/python tools/rfp_update_all_ota.py --start-from 192.168.178.14
```
Useful variants:
```bash
.venv/bin/python tools/rfp_update_all_ota.py --nodes-only
.venv/bin/python tools/rfp_update_all_ota.py --master-only
.venv/bin/python tools/rfp_update_all_ota.py --no-build
.venv/bin/python tools/rfp_update_all_ota.py --subnet 192.168.178.0/24
```
The full-update helper is only for standard OTA builds:
- Nodes: `rfp_esp32s3_wroom1_n16r8_3x106`
- Master: `rfp_esp32s3_wroom1_n16r8_master`
Cold-boot targets remain USB clean-flash targets because OTA does not rewrite
the bootloader/flash-mode layout.
Notes:
- OTA only works when the laptop and nodes are already in the same IP network.
- The OTA helper flashes sequentially, verifies reboot, and then continues to the next node.
- The cold-boot test target should be flashed by USB, not by OTA.
- Reason:
- it changes flash/boot related build settings (`flash_mode`, `memory_type`)
- it also uses a different release name for validation
- OTA only updates the application image, not the full USB-style flash layout
- If you try the cold-boot target by OTA, the usual symptom is exactly this:
- upload looks "uncertain"
- device stays reachable
- uptime keeps increasing
- reboot cannot be proven
- Reboot verification is now strict:
- it records firmware version and uptime before upload
- it waits for the node to disappear from the network
- it waits for the node to come back
- if no offline transition is seen, it requires a clear uptime reset before declaring success
- This avoids false positives where a node stays reachable and the script would previously print `OK` too early.
## USB Flash: Master
Recommended clean-flash helper:
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
./flash_master.sh
```
Check the serial port:
```bash
ls /dev/ttyACM* /dev/ttyUSB* 2>/dev/null
```
Flash the master via USB:
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
sudo .venv/bin/python .piohome/packages/tool-esptoolpy/esptool.py \
.venv/bin/python .piohome/packages/tool-esptoolpy/esptool.py \
--chip esp32s3 \
--port /dev/ttyACM0 \
--port "$PORT" \
--baud 460800 \
--before no_reset \
--before default_reset \
--after hard_reset \
write_flash -z \
--flash_mode qio \
@@ -321,88 +183,7 @@ sudo .venv/bin/python .piohome/packages/tool-esptoolpy/esptool.py \
0x0 .pio/build/rfp_esp32s3_wroom1_n16r8_master/bootloader.bin \
0x8000 .pio/build/rfp_esp32s3_wroom1_n16r8_master/partitions.bin \
0xe000 .piohome/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin \
0x10000 .pio/build/rfp_esp32s3_wroom1_n16r8_master/firmware.bin
```
Conservative master cold-boot firmware:
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
sudo .venv/bin/python .piohome/packages/tool-esptoolpy/esptool.py \
--chip esp32s3 \
--port /dev/ttyACM0 \
--baud 460800 \
--before no_reset \
--after hard_reset \
write_flash -z \
--flash_mode qio \
--flash_freq 80m \
--flash_size 16MB \
0x0 .pio/build/rfp_esp32s3_wroom1_n16r8_master_coldboot/bootloader.bin \
0x8000 .pio/build/rfp_esp32s3_wroom1_n16r8_master_coldboot/partitions.bin \
0xe000 .piohome/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin \
0x10000 .pio/build/rfp_esp32s3_wroom1_n16r8_master_coldboot/firmware.bin
```
Use USB for the cold-boot master target. OTA is not sufficient for this test
because the fix changes bootloader/flash-mode related build settings.
## USB Flash: Single Node
Recommended clean-flash helper:
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
./flash_node.sh
```
Check the serial port:
```bash
ls /dev/ttyACM* /dev/ttyUSB* 2>/dev/null
```
Standard firmware:
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
sudo .venv/bin/python .piohome/packages/tool-esptoolpy/esptool.py \
--chip esp32s3 \
--port /dev/ttyACM0 \
--baud 460800 \
--before no_reset \
--after hard_reset \
write_flash -z \
--flash_mode qio \
--flash_freq 80m \
--flash_size 16MB \
0x0 .pio/build/rfp_esp32s3_wroom1_n16r8_3x106/bootloader.bin \
0x8000 .pio/build/rfp_esp32s3_wroom1_n16r8_3x106/partitions.bin \
0xe000 .piohome/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin \
0x10000 .pio/build/rfp_esp32s3_wroom1_n16r8_3x106/firmware.bin
```
Cold-boot test firmware:
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
sudo .venv/bin/python .piohome/packages/tool-esptoolpy/esptool.py \
--chip esp32s3 \
--port /dev/ttyACM0 \
--baud 460800 \
--before no_reset \
--after hard_reset \
write_flash -z \
--flash_mode dio \
--flash_freq 80m \
--flash_size 16MB \
0x0 .pio/build/rfp_esp32s3_wroom1_n16r8_3x106_coldboot/bootloader.bin \
0x8000 .pio/build/rfp_esp32s3_wroom1_n16r8_3x106_coldboot/partitions.bin \
0xe000 .piohome/packages/framework-arduinoespressif32/tools/partitions/boot_app0.bin \
0x10000 .pio/build/rfp_esp32s3_wroom1_n16r8_3x106_coldboot/firmware.bin
0x10000 build_output/release/WLEDMM_14.7.2-mdev_RFP_N16R8_MASTER_V20260511E.bin
```
If upload does not start immediately:
@@ -410,94 +191,284 @@ If upload does not start immediately:
1. Hold `BOOT`
2. Tap `RESET`
3. Release `BOOT`
4. Run the upload again
Use USB for the first flash of `rfp_esp32s3_wroom1_n16r8_3x106_coldboot`.
After that, if you go back to the normal node firmware, OTA is fine again with the standard node target.
## USB Clean Flash: Node
## USB Flash: Group
USB flashing always happens physically one board after another unless several boards are connected at the same time.
Recommended workflow:
1. Build the desired target once.
2. Plug in node 1 and flash it.
3. Unplug node 1, plug in node 2, repeat.
4. Continue until node 6 is done.
If the same serial path is reused each time, the single-node USB command above is the repeatable group-flash procedure.
## Quick Difference: Master vs Node
- Master:
- Target: `rfp_esp32s3_wroom1_n16r8_master`
- Typical IP: `192.168.178.10`
- Binary: `.pio/build/rfp_esp32s3_wroom1_n16r8_master/firmware.bin`
- Node:
- Target: `rfp_esp32s3_wroom1_n16r8_3x106`
- Typical IPs: `192.168.178.11` to `192.168.178.16`
- Binary: `.pio/build/rfp_esp32s3_wroom1_n16r8_3x106/firmware.bin`
- Cold-boot test node:
- Target: `rfp_esp32s3_wroom1_n16r8_3x106_coldboot`
- Binary: `.pio/build/rfp_esp32s3_wroom1_n16r8_3x106_coldboot/firmware.bin`
- Cold-boot test master:
- Target: `rfp_esp32s3_wroom1_n16r8_master_coldboot`
- Binary: `.pio/build/rfp_esp32s3_wroom1_n16r8_master_coldboot/firmware.bin`
## Recommended Order
1. Test the affected board with its cold-boot target:
- master: `rfp_esp32s3_wroom1_n16r8_master_coldboot`
- node: `rfp_esp32s3_wroom1_n16r8_3x106_coldboot`
2. Remove power for at least 20 to 30 seconds
3. Verify whether it now boots without pressing `RESET`
4. If it works, roll the same target to the remaining nodes
5. If it still fails, inspect the hardware power-up path on `EN`, 3.3V rail, and any external loads
## Hand-off für andere (teamfähig)
Use this section when you want to provide the procedure to other people without hard-coding your local paths.
### Quickstart Template
Recommended helper:
```bash
export RFP_REPO="/path/to/WLED-MM/repo"
export NODE_BIN="/path/to/node/bin" # optional if node is already in PATH
cd "$RFP_REPO"
PATH="$NODE_BIN:$PATH" \
NPM_CONFIG_CACHE="$RFP_REPO/.npm-cache" \
PLATFORMIO_CORE_DIR="$RFP_REPO/.piohome" \
PLATFORMIO_PACKAGES_DIR="$RFP_REPO/.piohome/packages" \
PLATFORMIO_PLATFORMS_DIR="$RFP_REPO/.piohome/platforms" \
PLATFORMIO_CACHE_DIR="$RFP_REPO/.piohome/.cache" \
PLATFORMIO_BUILD_CACHE_DIR="$RFP_REPO/.piohome/buildcache" \
.venv/bin/python -m platformio run -e rfp_esp32s3_wroom1_n16r8_3x106
cd /home/jan/Documents/RFP/WLED-MM/repo
./flash_node.sh
```
### OTA group-flash template
Manual flow:
```bash
cd "$RFP_REPO"
cd /home/jan/Documents/RFP/WLED-MM/repo
ls /dev/ttyACM* /dev/ttyUSB* 2>/dev/null
PORT=/dev/ttyACM0
ENV=rfp_esp32s3_wroom1_n16r8_3x106
.venv/bin/python .piohome/packages/tool-esptoolpy/esptool.py \
--chip esp32s3 \
--port "$PORT" \
erase_flash
PATH=/home/jan/Documents/RFP/Finanz_App/node/current/bin:$PATH \
NPM_CONFIG_CACHE=$PWD/.npm-cache \
PLATFORMIO_CORE_DIR=$PWD/.piohome \
PLATFORMIO_PACKAGES_DIR=$PWD/.piohome/packages \
PLATFORMIO_PLATFORMS_DIR=$PWD/.piohome/platforms \
PLATFORMIO_CACHE_DIR=$PWD/.piohome/.cache \
PLATFORMIO_BUILD_CACHE_DIR=$PWD/.piohome/buildcache \
.venv/bin/python -m platformio run -e "$ENV" -t clean
PATH=/home/jan/Documents/RFP/Finanz_App/node/current/bin:$PATH \
NPM_CONFIG_CACHE=$PWD/.npm-cache \
PLATFORMIO_CORE_DIR=$PWD/.piohome \
PLATFORMIO_PACKAGES_DIR=$PWD/.piohome/packages \
PLATFORMIO_PLATFORMS_DIR=$PWD/.piohome/platforms \
PLATFORMIO_CACHE_DIR=$PWD/.piohome/.cache \
PLATFORMIO_BUILD_CACHE_DIR=$PWD/.piohome/buildcache \
.venv/bin/python -m platformio run -e "$ENV" -t upload --upload-port "$PORT"
```
## OTA Preflight
Check whether the current devices look OTA-updatable before flashing:
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
.venv/bin/python tools/rfp_update_all_ota.py --preflight-only --no-build
```
Single node:
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
.venv/bin/python tools/rfp_network_flash.py flash \
--targets <ip1>,<ip2>,<ip3>,<ip4>,<ip5>,<ip6> \
--start-from <ip1> \
--firmware .pio/build/rfp_esp32s3_wroom1_n16r8_3x106/firmware.bin
--targets 192.168.178.11 \
--firmware .pio/build/rfp_esp32s3_wroom1_n16r8_3x106/firmware.bin \
--expect-release RFP_N16R8_NODE3x106_V20260511E \
--preflight-only
```
### Minimal hand-off checklist
## OTA Update: Whole Installation From RFP Wi-Fi
1. Confirm all nodes are reachable in the same network.
2. Build once, then flash.
3. Validate reboot behavior on each node.
4. If one node fails, resume with `--start-from` from that node.
5. Document which target was used (`standard` vs `coldboot`).
This builds locally and flashes nodes `.11` to `.16` first, then the master `.10`.
The master is flashed last so the show controller stays available while the nodes
update.
### Rollback / removal plan for shared instructions
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
.venv/bin/python tools/rfp_update_all_ota.py
```
1. Revert this doc section in git:
- `git checkout -- docs/rfp-node-flashing.md`
2. Or remove only the hand-off section manually if the rest should stay.
3. If a deployment should be rolled back, flash the previous known-good firmware bin with the same OTA/USB commands.
If your PC is not connected to the RFP Wi-Fi and the master is already on the
right release, keep the master connected by USB and let the master relay node
OTA updates through its own Wi-Fi connection:
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
.venv/bin/python tools/rfp_update_all_ota.py --via-master-usb --port /dev/ttyACM0 --no-build
```
This node-only relay is still useful for resumes and tests. For full updates,
prefer `tools/rfp_update_master_usb_then_nodes.py` so the master is flashed and
verified first. The relay streams the node firmware through the master and does
not store the full file on the master filesystem. The default relay baud rate is
`921600` for faster node updates; opening `/dev/ttyACM0` can reset the ESP32-S3,
so the tool waits briefly before sending commands. If a flaky USB cable or hub
causes serial errors, retry with `--relay-baud 115200`.
The updater prefers the named release binaries from `build_output/release/` when
they exist and verifies the expected RFP release name after reboot:
- Nodes: `RFP_N16R8_NODE3x106_V20260511E`
- Master: `RFP_N16R8_MASTER_V20260511E`
During the controlled migration from older generic WLED-MM builds, the updater
sends WLED's `skipValidation=1` upload parameter by default. The update is still
accepted only if the target reboots and then reports the expected RFP release
name in `/json/info`.
If you explicitly want WLED's release-name validation to block mismatches before
upload, use:
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
.venv/bin/python tools/rfp_update_all_ota.py --no-skip-validation
```
Use existing build artifacts without rebuilding:
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
.venv/bin/python tools/rfp_update_all_ota.py --no-build
```
Devices that already report the selected RFP release are skipped by default.
This avoids reflashing the same binary and then trying to prove an update from
an unchanged release string. To force reflashing the same release anyway:
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
.venv/bin/python tools/rfp_update_all_ota.py --no-build --force-current-release
```
Resume from a failed device:
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
.venv/bin/python tools/rfp_update_all_ota.py --no-build --start-from 192.168.178.14
```
## OTA Update: Single Device
Master:
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
.venv/bin/python tools/rfp_network_flash.py flash \
--targets 192.168.178.10 \
--firmware build_output/release/WLEDMM_14.7.2-mdev_RFP_N16R8_MASTER_V20260511E.bin \
--expect-release RFP_N16R8_MASTER_V20260511E \
--skip-validation
```
Node:
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
.venv/bin/python tools/rfp_network_flash.py flash \
--targets 192.168.178.11 \
--firmware build_output/release/WLEDMM_14.7.2-mdev_RFP_N16R8_NODE3x106_V20260511E.bin \
--expect-release RFP_N16R8_NODE3x106_V20260511E \
--skip-validation
```
## Why OTA Can Fail After Build Success
OTA updates only the application image. It does not reliably replace bootloader,
flash mode, or partition table. If a board still has an old partition layout, do
one USB clean flash with the production target first. After that, OTA should be
usable again.
The OTA helper is intentionally strict: upload is considered successful if the
device reboot is proven by an offline transition, an uptime reset, or a WLED
transport reset during upload plus the expected RFP release name in `/json/info`.
When flashing the exact same release again, the transport-reset proof is weaker,
so the whole-installation updater skips already matching devices by default.
## Global 2D Timing Check
The `/infinity` scene controls intentionally expose only one brightness control.
It sits next to `Master Speed`, starts at `100%`, and the old per-row dimmers
are forced to full output by the controller UI.
Global 2D animations derive their beat position from Infinity master time, not
from frame arrival time on each node. `Master Speed` is BPM, with one beat equal
to one visible 2D step. For example, `Checkerd` at `240 BPM` changes pattern
`240` times per minute.
Use `Strobe` in the `/infinity` Global 2D mode selector to check sync visually.
Its `Pulse Width` control is the normal `Size` control renamed for this mode.
The strobe is rendered from the shared beat phase, so all nodes should flash
in parallel at every BPM value from `20` to `240`.
## Master Crash Capture
If the master ever restarts unexpectedly, capture the next run with a serial log.
This keeps the production partition layout unchanged while still decoding ESP32
panic backtraces.
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
mkdir -p /tmp/rfp-master-crash
PATH=/home/jan/Documents/RFP/Finanz_App/node/current/bin:$PATH \
NPM_CONFIG_CACHE=$PWD/.npm-cache \
PLATFORMIO_CORE_DIR=$PWD/.piohome \
PLATFORMIO_PACKAGES_DIR=$PWD/.piohome/packages \
PLATFORMIO_PLATFORMS_DIR=$PWD/.piohome/platforms \
PLATFORMIO_CACHE_DIR=$PWD/.piohome/.cache \
PLATFORMIO_BUILD_CACHE_DIR=$PWD/.piohome/buildcache \
.venv/bin/python -m platformio device monitor \
-e rfp_esp32s3_wroom1_n16r8_master \
--port /dev/ttyACM0 \
-b 115200 \
--filter esp32_exception_decoder \
--filter log2file
```
After a crash, save:
- The monitor log from the current directory or PlatformIO log output
- The first `rst:` line after reboot
- Any `Guru Meditation`, `panic`, or decoded backtrace lines
- The current `/json/info` fields `ver`, `release`, `e32code`, and `e32text`
Do not switch to a coredump partition shortly before the event unless a repeated
crash proves that serial logs are not enough.
## WLED Backup Mode
The regular WLED UI is intentionally kept available as a fallback.
- `Show Mode: ON` means Infinity Sync is active.
- `WLED Backup: ON` means Infinity Sync is stopped and regular WLED control can be used.
Stop Infinity on the master by API:
```bash
curl -X POST http://192.168.178.10/json/infinity \
-H 'Content-Type: application/json' \
-d '{"enabled":false}'
```
Re-enable later:
```bash
curl -X POST http://192.168.178.10/json/infinity \
-H 'Content-Type: application/json' \
-d '{"enabled":true}'
```
## Coldboot Diagnosis
The firmware side is now consolidated into the normal production targets. If a
board still starts only after pressing `RESET`, capture the serial log directly
after real power-on.
```bash
cd /home/jan/Documents/RFP/WLED-MM/repo
PATH=/home/jan/Documents/RFP/Finanz_App/node/current/bin:$PATH \
NPM_CONFIG_CACHE=$PWD/.npm-cache \
PLATFORMIO_CORE_DIR=$PWD/.piohome \
PLATFORMIO_PACKAGES_DIR=$PWD/.piohome/packages \
PLATFORMIO_PLATFORMS_DIR=$PWD/.piohome/platforms \
PLATFORMIO_CACHE_DIR=$PWD/.piohome/.cache \
PLATFORMIO_BUILD_CACHE_DIR=$PWD/.piohome/buildcache \
.venv/bin/python -m platformio device monitor \
-e rfp_esp32s3_wroom1_n16r8_master \
--port /dev/ttyACM0 \
-b 115200
```
Expected directly after power-on:
```text
rst:0x1 (POWERON_RESET)
SPI_FAST_FLASH_BOOT
```
If there is no clean boot log until the reset button is pressed, inspect hardware:
- `EN` / `CHIP_PU` must not float.
- Use a proper pull-up and reset delay, e.g. `10 kOhm` pull-up plus `1 uF` from `EN` to GND.
- If the 3.3 V rail rises slowly or external loads drag it down, use a reset supervisor.
- Check strapping pins and external DMX/LED wiring for backfeeding during power-up.