Files
WLED_MM_Infinity/docs/rfp-node-flashing.md
jan ebc4498d89
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
Add BPM speed control and OTA update workflow
2026-04-25 22:48:13 +02:00

15 KiB

RFP Infinity Flashing

This document covers flashing for both the Infinity master and the ESP32-S3 render nodes.

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

Use a cold-boot target when a board only starts reliably after pressing RESET following a long power loss.

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:

  • 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 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
    • 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:

curl -X POST http://192.168.178.10/json/infinity \
  -H 'Content-Type: application/json' \
  -d '{"enabled":false}'
  1. Or disable Infinity on one node for local testing:
curl -X POST http://192.168.178.11/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}'

For hotspot testing, replace the IPs with the current addresses, for example 10.42.0.213.

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

Conservative master cold-boot build:

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:

.pio/build/rfp_esp32s3_wroom1_n16r8_master_coldboot/firmware.bin

Build: Nodes

Standard build:

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

Cold-boot test build:

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_coldboot

Node firmware outputs:

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

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:

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:

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:

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:

cd /home/jan/Documents/RFP/WLED-MM/repo

.venv/bin/python tools/rfp_update_all_ota.py

Dry-run without building or flashing:

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:

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:

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

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

Check the serial port:

ls /dev/ttyACM* /dev/ttyUSB* 2>/dev/null

Flash the master via USB:

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/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:

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:

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

Check the serial port:

ls /dev/ttyACM* /dev/ttyUSB* 2>/dev/null

Standard firmware:

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:

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

If upload does not start immediately:

  1. Hold BOOT
  2. Tap RESET
  3. Release BOOT

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

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

OTA group-flash template

cd "$RFP_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

Minimal hand-off checklist

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

Rollback / removal plan for shared instructions

  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.