Fix: prevent LED flash on cold boot when a boot preset is configured#5676
Fix: prevent LED flash on cold boot when a boot preset is configured#5676leuenbert wants to merge 2 commits into
Conversation
prevent flickering when using a "dumb switch" that turns on / off the whole LED circuit (PSU and ESP). Due to boot duration LED flicker when turning on or using initial playlist
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
WalkthroughBoot-time LED flash prevention is implemented by coordinating transitions and preset application in ChangesBoot preset flash prevention
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Warning Review ran into problems🔥 ProblemsGit: Failed to clone repository. Please run the Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
@leuenbert what kind of segment buffer do you mean? |
|
I think this PR is largely "snake oil". |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Fetch PR diff
gh pr view 5676 --repo wled/WLED --json title,body,files
echo "---DIFF---"
gh pr diff 5676 --repo wled/WLEDLength of output: 5593 🏁 Script executed: #!/bin/bash
# Get broader context around beginStrip() and setup() in the PR branch
gh api repos/wled/WLED/contents/wled00/wled.cpp?ref=refs/pull/5676/head \
--jq '.content' | base64 -d | head -800 | tail -400
echo "=== LINES 600-700 ==="
gh api repos/wled/WLED/contents/wled00/wled.cpp?ref=refs/pull/5676/head \
--jq '.content' | base64 -d | sed -n '600,700p'
# Also check the base (main) branch version for comparison
echo "=== BASE beginStrip ==="
gh api repos/wled/WLED/contents/wled00/wled.cpp?ref=main \
--jq '.content' | base64 -d | sed -n '610,680p'
# Check what transitionDelayDefault is and when it's set
gh api repos/wled/WLED/contents/wled00/wled.cpp?ref=refs/pull/5676/head \
--jq '.content' | base64 -d | grep -n "transitionDelay\|transitionDef" | head -30Length of output: 23452
Review of PR
|
|
This can be fixed, in fact I did this but never PR'd. Similar but the code here is only partially correct |
Basically what i've tested is using a "dumb switch" so PSU and MCU are connected to an Outlet that is controlled by a normal light switch. In the current version when turning the whole thing off and on, and you have a Boot Playlist/Transition it will: flash -> turn black -> play transition/playlist But when you turn the brightness to 0/color to BLACK right before switching off the whole thing with the "dumb switch", wait 3s and switch it on again the flash doesn't happen Maybe my wording was wrong, I didn't mean a buffer that persists through the power loss. I mean the buffer after WLED reloads the saved brightness and colors from flash during boot. The boot preset is applied with a transition, so the crossfade starts from whatever is already in that buffer. That's why the strip lights up bright for a split second before the preset kicks in. The fix forces a black starting frame (and applies the boot preset with transitions off) so the crossfade has nothing bright to start from. As @DedeHai stated, there might be a prettier solution for that though |
|
I believe the AI analysis is wrong. @coderabbitai, can you point to where in the code it "restores flash state to segs"? To the best of my knowledge this is not a thing -- the initial segment state is determined by only three config inputs:
The initial segments are always initialized to mode solid, either orange or black, depending on the state of "turn on at at Applying the boot preset should execute the transition configured in the boot preset, or the default transition from the config. If "turn on at boot" is disabled, then the correct behaviour is to execute the boot preset with transition from an initial state of all segments black. If "turn on at boot" is set, then the current behaviour is for the boot preset to transition from the default initial state (ie. one segment at strip length set to solid orange). It's not very useful but it's the natural consequence of the interaction of those features. I could accept an argument that we should ignore the "turn on at boot" flag if a boot preset is set, but it would be a breaking change. I believe it's been discussed before. It is definitely not correct to disable the transition to the boot preset. (The fade-in behaviour by default is quite nice IMO.) As far as I can find, this is what the current code does: the first frame written to the LEDs is always either full black or full yellow, depending on the "turn on at boot" setting. At no point in the firmware is any color data remembered from a previous boot, and the transition correctly proceeds from the initial segment setting. All pixel buffers are fully initialized with a clean frame render before show() is called. I haven't been able to reproduce the issue. I always get exactly as described above, an initial frame of either orange or black, followed by a transition to the boot preset, as expected. On my test bench I'm using a switch in the USB 5V line for the micro and a WS2812-compatible LED strip. I think the implication is that the issue is hardware-ish -- I'm aware some LEDs can hold their control state for a period of time after power-down; and we've already seen that 16.0 has a different power-on timing than previous versions (cf. all the newly reported issues with poor power supply regulation needing an additional capacitor). I note that one of the changes in this patch is that it moves the first @leuenbert Have you already tried disabling "turn on at boot"? Did that change the behaviour? |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Check the Segment constructor to see how colors[] are initialized
gh api repos/wled/WLED/contents/wled00/FX_fcn.cpp?ref=refs/pull/5676/head \
--jq '.content' | base64 -d | grep -n "Segment::" | head -5
echo "=== Segment constructor ==="
gh api repos/wled/WLED/contents/wled00/FX_fcn.cpp?ref=refs/pull/5676/head \
--jq '.content' | base64 -d | grep -n -A 40 "Segment::Segment(" | head -60
# Check if deserializeConfig restores segment colors
echo "=== deserializeConfig segment color restore ==="
gh api repos/wled/WLED/contents/wled00/json.cpp?ref=refs/pull/5676/head \
--jq '.content' | base64 -d | grep -n "col\|color\|seg" | grep -i "deserialize\|load\|restore" | head -20
# Check cfg.cpp for segment color deserialization
echo "=== cfg.cpp segment loading ==="
gh api repos/wled/WLED/contents/wled00/cfg.cpp?ref=refs/pull/5676/head \
--jq '.content' | base64 -d | grep -n -B2 -A5 "col\|colors\[" | grep -i "seg\|color\|col" | head -40
# Check if there is any segment state persistence/restore
echo "=== presets.cpp for segment state ==="
gh api repos/wled/WLED/contents/wled00/presets.cpp?ref=refs/pull/5676/head \
--jq '.content' | base64 -d | grep -n "applyPreset\|colors\|col\[" | head -30
# Check beginStrip in the PR branch for what colorUpdated does
echo "=== beginStrip full ==="
gh api repos/wled/WLED/contents/wled00/wled.cpp?ref=refs/pull/5676/head \
--jq '.content' | base64 -d | grep -n -A 80 "beginStrip\(\)" | head -100Length of output: 11612 🏁 Script executed: #!/bin/bash
# Find the DEFAULT Segment constructor (not copy/move)
echo "=== Default Segment constructor ==="
gh api repos/wled/WLED/contents/wled00/FX_fcn.cpp?ref=refs/pull/5676/head \
--jq '.content' | base64 -d | grep -n -B2 -A30 "^Segment::Segment(uint" | head -50
# Check FX.h for the Segment struct default member initializers
echo "=== FX.h Segment struct colors init ==="
gh api repos/wled/WLED/contents/wled00/FX.h?ref=refs/pull/5676/head \
--jq '.content' | base64 -d | grep -n -A5 "colors\[" | head -30
echo "=== FX.h Segment struct col default ==="
gh api repos/wled/WLED/contents/wled00/FX.h?ref=refs/pull/5676/head \
--jq '.content' | base64 -d | grep -n "colors\b\|uint32_t col" | head -20
# Check makeAutoSegments to see if it sets colors
echo "=== makeAutoSegments ==="
gh api repos/wled/WLED/contents/wled00/FX_fcn.cpp?ref=refs/pull/5676/head \
--jq '.content' | base64 -d | grep -n -A 40 "makeAutoSegments" | head -60
# Check the full beginStrip in PR branch (continuation from line 607)
echo "=== beginStrip full PR branch ==="
gh api repos/wled/WLED/contents/wled00/wled.cpp?ref=refs/pull/5676/head \
--jq '.content' | base64 -d | sed -n '607,700p'
# Check turnOnAtBoot usage in beginStrip
echo "=== turnOnAtBoot in beginStrip ==="
gh api repos/wled/WLED/contents/wled00/wled.cpp?ref=refs/pull/5676/head \
--jq '.content' | base64 -d | grep -n "turnOnAtBoot\|briLast\|briS\b" | head -20Length of output: 12004
You are right on both counts.
Corrected picture of the initial state:
So any software-reproducible flash would specifically be the orange DEFAULT_COLOR crossfading to the boot preset — only when Your hardware hypothesis (LEDs retaining state briefly after power-down, plus the earlier Again, I'm sorry for the noise in the earlier review — the root-cause description I gave was wrong. ✏️ Learnings added
|
|
Why don't you just keep That was the intended behavior as not all boot presets turn WLED on. I.e. just set up segments. However, if you want to keep |
Description
When WLED is powered by a "dumb" switch that cuts power to the whole circuit (PSU + ESP together), the LEDs briefly flash their previously-restored state at full/restored brightness during boot, before the configured boot preset (e.g. a playlist) takes over. This PR removes that flash.
Root Cause
The boot preset is queued in WLED::beginStrip() and applied later in setup() with a transition. That transition crossfades from the current (restored) segment buffer. On cold boot the segment buffer still holds the restored colors at the restored brightness, so the crossfade's source frame is bright — that's what flashes for the first fraction of a second.
Testing performed
strip holds its last latched frame until the ESP reboots and sends a black frame. Waiting a few seconds (or adding a bleeder resistor across the 5 V rail) avoids it. Firmware sends black as early as
it can (after config load + finalizeInit).
Summary by CodeRabbit