Skip to content

[16.0.x port] prevent glitches on -C3 by waiting for LEDs updates to finish#5691

Draft
softhack007 wants to merge 10 commits into
mainfrom
backport_5435
Draft

[16.0.x port] prevent glitches on -C3 by waiting for LEDs updates to finish#5691
softhack007 wants to merge 10 commits into
mainfrom
backport_5435

Conversation

@softhack007

@softhack007 softhack007 commented Jun 19, 2026

Copy link
Copy Markdown
Member

16.0.0 port of #5435

temporary solution for LEDs glitches on RISC-V boards, as discussed in #5683

previous discussion see #5683 and #5688

Summary by CodeRabbit

Summary by CodeRabbit

  • Bug Fixes

    • Improved LED output synchronization during uploads, file operations, GIF image loading, WebSocket info sending, and OTA update flows to reduce timing conflicts, flicker, and related glitches.
    • Ensured LED activity completes before sensitive actions like retrieving free memory and closing/resuming files, improving stability during transfers and updates.
  • Improvements

    • Added a standardized “wait for LED idle/settle” mechanism with selectable very-short/short/medium timing, improving consistency across common device workflows.

softhack007 and others added 4 commits June 19, 2026 20:26
…lly suspend interrupts (#5435)

* adding strip.waitForLEDs(waitMS) function
* wait for LEDs output to complete before file writing
* wait before ESP.getFreeHeap() - main loop
* wait before file close (upload) and before  getFreeHeap() (json info)
* avoid losing "trigger" events due to strip.suspend
---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* use constants instead of raw timeouts
* simplify usage - in many cases we don't need #ifdef any more
* missed one "wait" on image_loader
comment should match real timeout
@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f6becf5f-e3ba-4595-a566-9f84c857704e

📥 Commits

Reviewing files that changed from the base of the PR and between 01e81c2 and 779cfa1.

📒 Files selected for processing (4)
  • wled00/FX_fcn.cpp
  • wled00/file.cpp
  • wled00/image_loader.cpp
  • wled00/wled.cpp
🚧 Files skipped from review as they are similar to previous changes (3)
  • wled00/FX_fcn.cpp
  • wled00/image_loader.cpp
  • wled00/file.cpp

Walkthrough

Introduces WS2812FX::waitForLEDs(unsigned maxWaitMS, bool always = false) const along with STRIP_WAIT_VERYSHORT, STRIP_WAIT_SHORT, and STRIP_WAIT_MEDIUM constants in FX.h, implements the helper with driver/target-conditional logic in FX_fcn.cpp, and replaces scattered platform-specific isUpdating() polling and yield() patterns with calls to this new API across file I/O, image loading, JSON serialization, OTA update, the main loop, setup initialization, upload handling, and WebSocket transmission.

Changes

waitForLEDs API and call-site integration

Layer / File(s) Summary
waitForLEDs API contract and constants
wled00/FX.h
Adds STRIP_WAIT_VERYSHORT, STRIP_WAIT_SHORT, and STRIP_WAIT_MEDIUM constexpr timeout constants, declares waitForLEDs(unsigned maxWaitMS, bool always = false) const on WS2812FX, relocates isSuspended() inline adjacent to the new declaration, and removes the stale duplicate isSuspended() definition.
waitForLEDs implementation
wled00/FX_fcn.cpp
Implements WS2812FX::waitForLEDs() with conditional paths: skips waiting on RMTHI builds when always is false, loops on strip.isUpdating() with millisecond timeout on ESP32, and uses yield()-bracketed polling on other targets when can_yield() is true; returns the post-wait isUpdating() state.
File I/O synchronization
wled00/file.cpp
closeFile() adds early-return guards, eagerly clears doCloseFile, and optionally suspends/resumes the strip around f.close() after waiting for LED transmission. handleFileRead(), copyFile(), compareFiles(), and restoreFile() each prepend waitForLEDs() with appropriate timeout and always parameters before filesystem access.
Image loader synchronization
wled00/image_loader.cpp
fileReadBlockCallback replaces the ESP32C3-specific yield() loop with strip.waitForLEDs(STRIP_WAIT_MEDIUM). renderImageToSegment() inserts strip.waitForLEDs(STRIP_WAIT_MEDIUM) before closing any open file and starting GIF decode.
OTA, initialization, housekeeping, and network operations
wled00/ota_update.cpp, wled00/json.cpp, wled00/wled.cpp, wled00/wled_server.cpp, wled00/ws.cpp
beginOTA() and initBootloaderOTA() add waitForLEDs() calls before OTA setup. serializeInfo() inserts waitForLEDs(STRIP_WAIT_SHORT) before heap query. WLED::setup() adds waitForLEDs(STRIP_WAIT_MEDIUM) before beginStrip() to prevent flickering and corrects the brownout detector comment. WLED::loop() replaces C3-specific isUpdating() polling with waitForLEDs(STRIP_WAIT_MEDIUM) during heap housekeeping. handleUpload() finalization conditionally suspends, waits, closes the temp file, and resumes. sendDataWs() adds a short wait before heap sizing and buffer allocation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • wled/WLED#5435: Introduces the WS2812FX::waitForLEDs(...) API and wires it into several of the same call sites targeted by this PR (closeFile(), serializeInfo() heap query, beginOTA/initBootloaderOTA).
  • wled/WLED#4939: Modifies wled00/wled.cpp's heap-checking logic during LED updates on ESP32-C3, the same code path that this PR updates to use the new waitForLEDs(...) API.
  • wled/WLED#4793: Modifies wled00/file.cpp file-operation utilities such as copyFile() and compareFiles(), which this PR synchronizes with the new strip.waitForLEDs(...) calls.

Suggested labels

keep

Suggested reviewers

  • willmmiles
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title directly addresses the main objective: preventing LED glitches on ESP32-C3 by waiting for LED updates to complete. It accurately reflects the core technical change across all modified files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

coderabbitai[bot]

This comment was marked as resolved.

prevents flickering at startup
* space before comment
* use isUpdating() - not strip.isUpdating() - in waitForLEDs()
@softhack007 softhack007 added enhancement hardware (Likely) Setup-specific issue, e.g. flickering LEDs optimization re-working an existing feature to be faster, or use less memory labels Jun 19, 2026
coderabbitai[bot]

This comment was marked as resolved.

getFreeHeapSize() can cause glitches.
@softhack007

Copy link
Copy Markdown
Member Author

@bigfella5000 this is the -C3 flicker fix, brought to main (mentioned during discussion of your PR #5683). Can you check if this fixes the flickering you observed on your -C3 setup?

@softhack007 softhack007 self-assigned this Jun 19, 2026
Comment thread wled00/image_loader.cpp Outdated
Comment thread wled00/wled.cpp Outdated
Comment thread wled00/FX_fcn.cpp
* On 8266 this call will _not_ wait outside the main loop context.
* Function returns isUpdating() status after waiting.
**/
bool WS2812FX::waitForLEDs(unsigned maxWaitMS, bool always) const {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add (or replace always) "bool suspend" and put the suspend code in here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's a bit tricky ... need to think about it.

Comment thread wled00/file.cpp Outdated
Comment thread wled00/file.cpp

// f.close() may enter flash critical sections (interrupts/cache paused), so we wait for LED transmission to finish first to avoid WS281x glitches
// This is most relevant on ESP32-C3/C5/C6, where the RMT driver is very sensitive to interrupt timing.
bool haveSuspended = false;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suspend will skip this frame if it already isServicing() but maybe that is the price to pay, I don't see an easy way out.

@softhack007 softhack007 Jun 20, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point ... its really a hard measure for every file update (saving presets and saving cfg.json).
The problem is that the main loop might run the next strip.service() when closefile() gets called from the async_tcp task context. This could create new flickering.

Comment thread wled00/FX_fcn.cpp Outdated
* clarify #ifdef condition in waitForLEDs()
* move strip.waitForLEDs() before file exists checks  (exists() can cause flickering)
* use MEDUIM timeout in image loader
* correct misunderstood comment in wled.cpp
@softhack007

softhack007 commented Jun 20, 2026

Copy link
Copy Markdown
Member Author

esp01_1m_full_160 build has failed - need to check why we're now 3 bytes over program size 🤔

Error: The program size (892915 bytes) is greater than maximum allowed (892912 bytes)
RAM:   [======    ]  56.8% (used 46504 bytes from 81920 bytes)
Flash: [==========]  100.0% (used 892915 bytes from 892912 bytes)

edit: my last commit seems to have added 16 bytes that are now pushing us over-the-limit.

Checking size .pio/build/esp01_1m_full_160/firmware.elf
Advanced Memory Usage is available via "PlatformIO Home > Project Inspect"
RAM:   [======    ]  56.8% (used 46504 bytes from 81920 bytes)
Flash: [==========]  100.0% (used 892899 bytes from 892912 bytes)

@softhack007

softhack007 commented Jun 20, 2026

Copy link
Copy Markdown
Member Author

It looks like the esp01_1m_full_160 buildenv is totally closed to the flash size limit. All other esp01_1m_full buildenvs don't have AR support which gives them around 7Kbytes or safety margin.

I've removed AR from esp01_1m_full_160 in main. Wondering if we should also remove it in 0_16 - its a regression for this specific build compared to the 16.0.0 release.

@DedeHai @netmindz @willmmiles what do you think? OK to remove AR from esp01_1m_full_160 also in the 0_16 branch?

@softhack007 softhack007 marked this pull request as draft June 21, 2026 17:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Awaiting testing enhancement hardware (Likely) Setup-specific issue, e.g. flickering LEDs optimization re-working an existing feature to be faster, or use less memory

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants