Skip to content

fix(led): stop WS2812 refresh when frame is unchanged so 'off' stays dark - #66

Open
mitchross wants to merge 2 commits into
StuckAtPrototype:masterfrom
mitchross:fix/led-blink-when-off
Open

fix(led): stop WS2812 refresh when frame is unchanged so 'off' stays dark#66
mitchross wants to merge 2 commits into
StuckAtPrototype:masterfrom
mitchross:fix/led-blink-when-off

Conversation

@mitchross

@mitchross mitchross commented Jul 11, 2026

Copy link
Copy Markdown

Fixes #65

Problem

With brightness set to off, the LEDs blink every ~10-17 seconds (visible in a dark room). Serial console showed the real root cause — a reboot loop:

I (13633) zigbee: Waiting for coordinator (ESP_FAIL), attempt 4/5, retrying
E (16993) zigbee: Zigbee init failed 5 times – rebooting to reset radio
ESP-ROM:esp32h2-eco5-20240613
rst:0xc (SW_CPU),boot:0x1c (SPI_FAST_FLASH_BOOT)

A commissioned device whose coordinator is unreachable (powered off, out of range, network gone) fails ESP_ZB_BDB_MODE_INITIALIZATION 5 times and then calls esp_restart() — which can't help, so it boot-loops forever. Each boot flashes the LED before button_init() loads the saved brightness from NVS, so a device set to off blinks all night. Likely also related to #54 and #42.

Fixes

zigbee.c — stop the reboot loop

  • Reboot at most once per power cycle to clear a genuinely wedged radio (skip when the last reset was already ESP_RST_SW — it didn't help).
  • After that, keep retrying initialization with exponential backoff (1s → 2s → 4s … capped at REJOIN_BACKOFF_MAX_MS = 5 min) instead of restarting.

led.c — no boot flash when brightness is off

  • Start led_intensity at 0 so the LEDs stay dark until button_init() loads the saved brightness.

led.c — idle data line when off

  • Only write to the WS2812 strip when the frame changes (smooth animations still update at 50 Hz), repeat writes for 500 ms after a change so a corrupted frame self-heals, refresh at 1 Hz while lit, and keep the line fully idle once settled at off.

Hardware verification (ESP32-H2, built with IDF v5.5.2)

Flashed and watched serial for 3 minutes with the coordinator unreachable:

E (16793) zigbee: Zigbee init failed 5 times – rebooting once to reset radio
rst:0xc (SW_CPU)   <- single reboot, then no further restarts
I (20219) zigbee: Waiting for coordinator (ESP_FAIL), attempt 6, retrying in 2000 ms
I (24579) zigbee: Waiting for coordinator (ESP_FAIL), attempt 7, retrying in 4000 ms
I (41299) zigbee: Waiting for coordinator (ESP_FAIL), attempt 9, retrying in 16000 ms
I (160379) zigbee: Waiting for coordinator (ESP_FAIL), attempt 12, retrying in 128000 ms

Sensors, serial JSON, and BLE keep running throughout. Overnight dark-room test of the off-LED behavior in progress; will report back.

Flashing for testing

Built with espressif/idf:v5.5.2, idf.py set-target esp32h2 build, then merged to a single image:

esptool.py --chip esp32h2 merge_bin -o AirCube_firmware_led_fix.bin @flash_args
esptool.py --chip esp32h2 --port /dev/cu.usbmodemXXXX --baud 460800 write-flash 0x0 AirCube_firmware_led_fix.bin

(Equivalent to the ESP Launchpad DIY flow at address 0x0 from FIRMWARE_UPDATE.md.)

🤖 Generated with Claude Code

mitchross and others added 2 commits July 11, 2026 00:55
…dark

The LED task rewrote an identical frame to the WS2812 strip every 20ms,
keeping the data line permanently active. The Zigbee + BLE sensor push
every 10 seconds (radio TX burst, with DFS enabled) can corrupt one of
those frames, so the pixels briefly latch a random color - visible as a
blink every ~10s even with brightness set to off.

Only write to the strip when the frame actually changes. After a change,
repeat the frame for 500ms so a corrupted transition write self-heals.
While visibly lit, refresh once per second as a safety net. Once settled
at off, keep the data line fully idle so nothing can flash at night.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A commissioned device that cannot reach its coordinator (powered off,
out of range, network gone) failed initialization 5 times and then
called esp_restart() - which changes nothing, so the device boot-looped
every ~17 seconds forever. Each boot also flashed the LED before the
saved brightness loaded from NVS, so a device with brightness set to
off blinked all night.

- Reboot at most once per power cycle (skip when the last reset was
  already our own SW restart), then keep retrying initialization with
  exponential backoff capped at REJOIN_BACKOFF_MAX_MS (5 min).
- Start led_intensity at 0 so boots stay dark until button_init() loads
  the saved brightness.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

LED blinks every ~10s when brightness is set to off

1 participant