Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5cc289a
wait for LEDs output to complete before file writing
softhack007 Mar 21, 2026
decd338
wait before ESP.getFreeHeap() - main loop
softhack007 Mar 21, 2026
2940601
adding strip.waitForLEDs(waitMS)
softhack007 Mar 21, 2026
9f9e440
use strip.waitForLEDs()
softhack007 Mar 21, 2026
11304af
wait before file close (upload) and before getFreeHeap() (json info)
softhack007 Mar 21, 2026
9e7f553
restore lost comment
softhack007 Mar 21, 2026
b2a27d9
typo
softhack007 Mar 21, 2026
05aeae3
comment clarification
softhack007 Mar 21, 2026
29aff99
prevent that strip.service() runs again after waiting
softhack007 Mar 21, 2026
4495133
closeFile() should always consume doCloseFile flag
softhack007 Mar 21, 2026
d835254
typo's in comments
softhack007 Mar 21, 2026
1b752ad
comment fix
softhack007 Mar 21, 2026
fe2cae6
Merge branch '0_15_x' into 8266_wait_b4_close
softhack007 Mar 22, 2026
d9bef17
remove duplicate non-const method
softhack007 Mar 22, 2026
01520cb
avoid losing "trigger" events due to strip.suspend
softhack007 Mar 22, 2026
3ab8302
clarify comment
softhack007 Mar 22, 2026
4566052
suspends effects during OTA upload
softhack007 Mar 22, 2026
e1123ec
bugfix: OTA suspend
softhack007 Mar 22, 2026
023e348
revert OTA request handler changes
softhack007 Mar 23, 2026
d0c63be
add short wait after OTA has suspended effects
softhack007 Mar 23, 2026
a74d11f
add flicker protection before potentially longer file operations
softhack007 Mar 23, 2026
83e1d52
don't suspend effects in serializeInfo()
softhack007 Mar 24, 2026
8d263ac
OTA: reset segments _after_ config backup
softhack007 Mar 24, 2026
4ffddd5
Merge branch '0_15_x' into 8266_wait_b4_close
softhack007 Mar 28, 2026
6c0f48c
fix typo in comment
softhack007 Mar 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion wled00/FX.h
Original file line number Diff line number Diff line change
Expand Up @@ -816,8 +816,9 @@ class WS2812FX { // 96 bytes
inline void setShowCallback(show_callback cb) { _callback = cb; }
inline void setTransition(uint16_t t) { _transitionDur = t; } // sets transition time (in ms)
inline void appendSegment(const Segment &seg = Segment()) { if (_segments.size() < getMaxSegments()) _segments.push_back(seg); }
inline void suspend() { _suspend = true; } // will suspend (and canacel) strip.service() execution
inline void suspend() { _suspend = true; } // will suspend (and cancel) strip.service() execution
inline void resume() { _suspend = false; } // will resume strip.service() execution
inline bool isSuspended() { return _suspend; } // true if strip.service() execution is suspended

bool
paletteFade,
Expand All @@ -827,6 +828,11 @@ class WS2812FX { // 96 bytes
isUpdating() const, // return true if the strip is being sent pixel updates
deserializeMap(uint8_t n=0);

// be nice, but not too nice - wait until LEDs are idle, or maxWaitMS have passed
// on 8266 this call will _not_ wait outside of the main loop context
// returns isUpdating() status after waiting
bool waitForLEDs(unsigned maxWaitMS) const;

inline bool isServicing() const { return _isServicing; } // returns true if strip.service() is executing
inline bool hasWhiteChannel() const { return _hasWhiteChannel; } // returns true if strip contains separate white chanel
inline bool isOffRefreshRequired() const { return _isOffRefreshRequired; } // returns true if strip requires regular updates (i.e. TM1814 chipset)
Expand Down
21 changes: 21 additions & 0 deletions wled00/FX_fcn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1476,6 +1476,27 @@ bool WS2812FX::isUpdating() const {
return !BusManager::canAllShow();
}

/**
* Be nice, but not too nice - wait util LEDs are idle, or maxWaitMS milliseconds have passed
Comment thread
softhack007 marked this conversation as resolved.
Outdated
* On 8266 this call will _not_ wait outside the main loop context
* Function returns isUpdating() status after waiting
*/
bool WS2812FX::waitForLEDs(unsigned maxWaitMS) const {
#ifdef ARDUINO_ARCH_ESP32
unsigned long waitStart = millis();
while (strip.isUpdating() && (millis() - waitStart < maxWaitMS)) delay(1);
#else
if (can_yield()) {
// If we are in a yieldable context (main loop), wait until the LEDs output finishes
yield();
unsigned long waitStart = millis();
while (strip.isUpdating() && (millis() - waitStart < maxWaitMS)) delay(1);
yield();
}
#endif
return isUpdating();
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

/**
* Returns the refresh rate of the LED strip. Useful for finding out whether a given setup is fast enough.
* Only updates on show() or is set to 0 fps if last show is more than 2 secs ago, so accuracy varies
Expand Down
15 changes: 14 additions & 1 deletion wled00/file.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,26 @@ static File f; // don't export to other cpp files

//wrapper to find out how long closing takes
void closeFile() {
if (!doCloseFile || !f) { doCloseFile = false; return; } // file not open, or no request to close -> nothing to do, nothing to wait
#ifdef WLED_DEBUG_FS
DEBUGFS_PRINT(F("Close -> "));
uint32_t s = millis();
#endif
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().
Comment thread
softhack007 marked this conversation as resolved.
Outdated
bool haveSuspended = false;
#if defined(WLED_USE_SHARED_RMT) || defined(__riscv) || !defined(ARDUINO_ARCH_ESP32)
if (!strip.isSuspended()) { strip.suspend(); haveSuspended = true; }// prevent that a new strip.show() starts after waiting
strip.waitForLEDs(15); // be nice, but not too nice. Waits up to 15ms
#endif

// In 8266 async webserver / lwIP interrupt context can_yield() is false and we cannot wait,
// so we proceed immediately — the close is still required to prevent file corruption.
f.close(); // "if (f)" check is aleady done inside f.close(), and f cannot be nullptr -> no need for double checking before closing the file handle.
if (haveSuspended) strip.resume(); // end of critical section - new LEDs updates are allowed again
Comment thread
coderabbitai[bot] marked this conversation as resolved.
DEBUGFS_PRINTF("took %d ms\n", millis() - s);
doCloseFile = false;
Comment thread
softhack007 marked this conversation as resolved.
}

//find() that reads and buffers data from file stream in 256-byte blocks.
Expand Down
8 changes: 8 additions & 0 deletions wled00/json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,13 @@ void serializeInfo(JsonObject root)
root[F("lwip")] = LWIP_VERSION_MAJOR;
#endif

bool haveSuspended = false;
#if defined(WLED_USE_SHARED_RMT) || defined(__riscv) || !defined(ARDUINO_ARCH_ESP32)
// calling ESP.getFreeHeap() during led update causes glitches on C3 and possibly on 8266, too
if (!strip.isSuspended()) { strip.suspend(); haveSuspended = true; } // prevent that a new strip.show() starts after waiting
Comment thread
softhack007 marked this conversation as resolved.
Outdated
strip.waitForLEDs(15); // be nice, but not too nice. Waits up to 15ms
#endif

root[F("freeheap")] = ESP.getFreeHeap();
#if defined(ARDUINO_ARCH_ESP32)
if (psramSafe && psramFound()) root[F("psram")] = ESP.getFreePsram();
Expand All @@ -781,6 +788,7 @@ void serializeInfo(JsonObject root)
getTimeString(time);
root[F("time")] = time;

if (haveSuspended) strip.resume(); // end of critical section - new LEDs updates are allowed again
UsermodManager::addToJsonInfo(root);

uint16_t os = 0;
Expand Down
5 changes: 5 additions & 0 deletions wled00/wled.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@ void WLED::loop()

// reconnect WiFi to clear stale allocations if heap gets too low
if (millis() - heapTime > 15000) {
#if defined(WLED_USE_SHARED_RMT) || defined(__riscv) || !defined(ARDUINO_ARCH_ESP32)
// calling ESP.getFreeHeap() during led update causes glitches on C3 and possibly on 8266, too
strip.waitForLEDs(15); // wait up to 15ms for LEDs sendout to complete - we are in the main loop, so a new strip.show() cannot start while waiting
#endif

uint32_t heap = ESP.getFreeHeap();
if (heap < MIN_HEAP_SIZE && lastHeap < MIN_HEAP_SIZE) {
DEBUG_PRINTF_P(PSTR("Heap too low! %u\n"), heap);
Expand Down
7 changes: 7 additions & 0 deletions wled00/wled_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,14 @@ static void handleUpload(AsyncWebServerRequest *request, const String& filename,
request->_tempFile.write(data,len);
}
if (final) {
bool haveSuspended = false;
#if defined(WLED_USE_SHARED_RMT) || defined(__riscv) || !defined(ARDUINO_ARCH_ESP32)
if (!strip.isSuspended()) { strip.suspend(); haveSuspended = true; } // prevent that a new strip.show() starts after waiting
strip.waitForLEDs(25); // calling file.close() during LEDs sendout can cause glitches on C3 and on 8266
#endif
request->_tempFile.close();
if (haveSuspended) strip.resume();

if (filename.indexOf(F("cfg.json")) >= 0) { // check for filename with or without slash
doReboot = true;
request->send(200, FPSTR(CONTENT_TYPE_PLAIN), F("Configuration restore successful.\nRebooting..."));
Expand Down
Loading