Files
WLED_MM_Infinity/docs/rfp-node-flashing.md
jan 4bc4e1257e
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
Backup RFP Infinity controller state before Resolume changes
2026-05-14 12:31:13 +02:00

15 KiB

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

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:

.pio/build/rfp_esp32s3_wroom1_n16r8_master/firmware.bin
build_output/release/WLEDMM_14.7.2-mdev_RFP_N16R8_MASTER_V20260511E.bin

Build: Nodes

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:

.pio/build/rfp_esp32s3_wroom1_n16r8_3x106/firmware.bin
build_output/release/WLEDMM_14.7.2-mdev_RFP_N16R8_NODE3x106_V20260511E.bin

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.

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:

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:

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:

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:

cd /home/jan/Documents/RFP/WLED-MM/repo
./flash_master.sh

Manual flow:

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:

cd /home/jan/Documents/RFP/WLED-MM/repo
./flash_node.sh

Manual flow:

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:

cd /home/jan/Documents/RFP/WLED-MM/repo
.venv/bin/python tools/rfp_update_all_ota.py --preflight-only --no-build

Single node:

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.

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:

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:

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:

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:

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:

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:

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:

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.

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:

curl -X POST http://192.168.178.10/json/infinity \
  -H 'Content-Type: application/json' \
  -d '{"enabled":false}'

Re-enable later:

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.

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:

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.