# RFP Infinity Flashing 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` - Node target: `rfp_esp32s3_wroom1_n16r8_3x106` ## 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 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 runtime-only settings that were stored through the WLED UI must be re-applied unless they are also encoded as firmware defaults. ## Roles - Master: - Usually `192.168.178.10` - 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 the real WLED status pixel exclusively on `GPIO48` - Nodes: - Usually `192.168.178.11` to `192.168.178.16` - Render the LED output locally - Receive Infinity Sync from the master - Use three LED outputs: `GPIO4/5/6`, each with `106` LEDs ## Build: Master ```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 ``` Master firmware output: ```text .pio/build/rfp_esp32s3_wroom1_n16r8_master/firmware.bin build_output/release/WLEDMM_14.7.2-mdev_RFP_N16R8_MASTER_V20260511E.bin ``` ## Build: Nodes ```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_3x106 ``` 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 \ 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" .venv/bin/python .piohome/packages/tool-esptoolpy/esptool.py \ --chip esp32s3 \ --port "$PORT" \ --baud 460800 \ --before default_reset \ --after hard_reset \ write_flash -z \ --flash_mode qio \ --flash_freq 80m \ --flash_size 16MB \ 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 build_output/release/WLEDMM_14.7.2-mdev_RFP_N16R8_MASTER_V20260511E.bin ``` If upload does not start immediately: 1. Hold `BOOT` 2. Tap `RESET` 3. Release `BOOT` 4. Run the upload again ## USB Clean Flash: Node Recommended helper: ```bash cd /home/jan/Documents/RFP/WLED-MM/repo ./flash_node.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_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 192.168.178.11 \ --firmware .pio/build/rfp_esp32s3_wroom1_n16r8_3x106/firmware.bin \ --expect-release RFP_N16R8_NODE3x106_V20260511E \ --preflight-only ``` ## OTA Update: Whole Installation From RFP Wi-Fi 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. ```bash cd /home/jan/Documents/RFP/WLED-MM/repo .venv/bin/python tools/rfp_update_all_ota.py ``` 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.