(0.15.x) wait for LEDs output to finish before OS calls that potentially suspend interrupts#5435
(0.15.x) wait for LEDs output to finish before OS calls that potentially suspend interrupts#5435softhack007 wants to merge 15 commits into0_15_xfrom
Conversation
f.close() writes buffered data to flash, and shortly suspends all other interrupts during writing. This can lead to LEDs flickering, especially on 8266 or -C3 that don't have the RMTHI driver.
reduces code duplication
two more places where flickering can be reduced
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
only in async paths (webserver context)
to prevent repeated attempts to close a closed file handle.
I overlooked that this method already existed -> merged both definitions.
| doCloseFile = false; // consume flag early, to reduce the time window for concurrent closing attempts from several tasks. | ||
|
|
||
| // f.close() flushes to flash and briefly disables cache-related interrupts on ESP8266, | ||
| // which can corrupt WS281x LED timing (bit-bang / UART) if it coincides with strip.service(). |
There was a problem hiding this comment.
This can never happen on ESP8266. It's impossible to be handling file IO during bus operations because the system is using cooperative multitasking -- it cannot be pre-empted. The strip operation must complete before any web handling/file IO can proceed.
There was a problem hiding this comment.
@willmmiles i thought that LEDs output is interrupt driven, also on 8266?
Edit: I agree that "if f.close() coincides with strip.service()." is misleading (AI wording....). Can the 8266 LEDs driver continue to send data in the background (ISR) ?
There was a problem hiding this comment.
I'm just triple-checking this now..
For digital LEDs:
- Bigbang buffer is blocking (obviously)
- DMA methods (pin 3) uses full up-front conversion instead; it's memory intensive
- The UART method (pins 1 or 2) do use an ISR, but they're configured to operate entirely out of SRAM, so it will work with flash disabled
For analog outputs, the system implements soft PWM using an NMI; it's also entirely SRAM and safe to use during flash IO.
I have to take a break for a bit but I've got my test board up and running, I'll see what I can find later today.
There was a problem hiding this comment.
There is a possible issue if someone's trying to use soft-PWM analog and bit-banging simultaneously, but that's firmly in the category of "has never and probably will never work".
if strip.suspend() was received in the middle of effect rendering, the final strip.show() is skipped => trigger request should not reset if suspended.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
wled00/FX_fcn.cpp (1)
1423-1433:⚠️ Potential issue | 🟠 MajorA late
suspend()can still drop the pending redraw.Line 1423 only handles suspends that are already visible before the reset. If another task suspends during the
yield()/ palette work window, Line 1432 skipsshow()after_triggeredhas already been cleared, and Line 1431 still advances_lastServiceShowfor a frame that never went out.💡 Minimal fix
if (doShow) { yield(); Segment::handleRandomPalette(); // slowly transition random palette; move it into for loop when each segment has individual random palette - _lastServiceShow = nowUp; // update timestamp, for precise FPS control - if (!_suspend) show(); + if (!_suspend) { + _lastServiceShow = nowUp; // update timestamp only for frames that were actually sent + show(); + } else { + _triggered = true; // re-run the skipped frame immediately after resume() + } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@wled00/FX_fcn.cpp` around lines 1423 - 1433, The pending redraw can be lost if suspend() is called during yield()/Segment::handleRandomPalette(): don't clear _triggered or advance _lastServiceShow before the final suspend check and show(); instead re-evaluate _suspend after yield() and Segment::handleRandomPalette(), only call show() and then set _lastServiceShow and clear _triggered when show() actually ran (or keep _triggered set if suspended), ensuring the sequence in the block that handles doShow uses the post-yield suspend state to decide whether to call show(), update _lastServiceShow, and reset _triggered.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@wled00/FX_fcn.cpp`:
- Around line 1423-1433: The pending redraw can be lost if suspend() is called
during yield()/Segment::handleRandomPalette(): don't clear _triggered or advance
_lastServiceShow before the final suspend check and show(); instead re-evaluate
_suspend after yield() and Segment::handleRandomPalette(), only call show() and
then set _lastServiceShow and clear _triggered when show() actually ran (or keep
_triggered set if suspended), ensuring the sequence in the block that handles
doShow uses the post-yield suspend state to decide whether to call show(),
update _lastServiceShow, and reset _triggered.
Add a short wait time before calling file.close() and ESP.getFreeHeap(), to prevent LED glitches caused by interrupt stalls.
potential fix for #5427
Summary by CodeRabbit
New Features
Bug Fixes
Refactor