another attempt to avoid LED glitches during file writing
* mark synchronization variables volatile (thread-safe) * additional logic in closeFile() to avoid file write while leds are pushed out by NPB driver * some comments and analysis * replace "yield()" after file ops with "delay(0)" before operations.
This commit is contained in:
@@ -8,6 +8,7 @@
|
|||||||
#if WLED_FS != LITTLEFS && ESP_IDF_VERSION_MAJOR < 4
|
#if WLED_FS != LITTLEFS && ESP_IDF_VERSION_MAJOR < 4
|
||||||
#include "esp_spiffs.h"
|
#include "esp_spiffs.h"
|
||||||
#endif
|
#endif
|
||||||
|
//#define yield() {delay(0);} // WLEDMM yield() is completely unnecessary on esp32, but delay(0) can reduce task contention
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//WLEDMM seems that 256 is indeed the optimal buffer length
|
//WLEDMM seems that 256 is indeed the optimal buffer length
|
||||||
@@ -37,7 +38,20 @@ static File f; // don't export to other cpp files
|
|||||||
void closeFile() {
|
void closeFile() {
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
// WLEDMM: file.close() triggers flash writing. While flash is writing, the NPB RMT driver cannot fill its buffer which may create glitches.
|
// WLEDMM: file.close() triggers flash writing. While flash is writing, the NPB RMT driver cannot fill its buffer which may create glitches.
|
||||||
|
// WLEDMM more precisely (thanks to a web research done by AI):
|
||||||
|
// the RMT peripheral itself doesn’t stall, but the refill path often does. In Arduino-ESP32/WLED
|
||||||
|
// typical builds, close() that commits flash writes frequently causes enough blocking that the LED pipeline under-runs, resulting in visible glitches.
|
||||||
|
// So the assumption is practically correct for this project context.
|
||||||
|
// --> with neopixelBus 2.7.5, the practical ISR stall budget is about 0.08–0.12 ms — far less than LittleFS flash commit times.
|
||||||
|
// typical flash write "commit" times are between 0.5ms and 10ms, but they can be a few 100ms in worst case
|
||||||
|
// --> file reads rarely cause refill stalls compared to writes, but large/fragmented reads can still exceed the ~0.08–0.12 ms budget.
|
||||||
|
// esp32 recommendations: use f.setBufferSize() (512–1024 for reads is reasonable); use delay(0) after file reads, to reduce task contention
|
||||||
|
|
||||||
|
if (!f) {doCloseFile = false; return;} // WLEDMM only do all this hick-hack when f is an open file
|
||||||
|
|
||||||
unsigned long t_wait = millis();
|
unsigned long t_wait = millis();
|
||||||
|
bool oldLock = suspendStripService;
|
||||||
|
if (strip.isUpdating()) suspendStripService = true; // WLEDMM schedule short pause to prevent LEDs glitching during flash write
|
||||||
while(strip.isUpdating() && (millis() - t_wait < 72)) delay(1); // WLEDMM try to catch a moment when strip is idle
|
while(strip.isUpdating() && (millis() - t_wait < 72)) delay(1); // WLEDMM try to catch a moment when strip is idle
|
||||||
while(strip.isUpdating() && (millis() - t_wait < 96)) delay(0); // try harder
|
while(strip.isUpdating() && (millis() - t_wait < 96)) delay(0); // try harder
|
||||||
//if (strip.isUpdating()) USER_PRINTLN("closeFile: strip still updating.");
|
//if (strip.isUpdating()) USER_PRINTLN("closeFile: strip still updating.");
|
||||||
@@ -47,7 +61,12 @@ void closeFile() {
|
|||||||
DEBUGFS_PRINT(F("Close -> "));
|
DEBUGFS_PRINT(F("Close -> "));
|
||||||
uint32_t s = millis();
|
uint32_t s = millis();
|
||||||
#endif
|
#endif
|
||||||
|
if ((suspendStripService == false) && (oldLock == true)) oldLock = false; // update in case of parallel lock release by another task
|
||||||
f.close();
|
f.close();
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
delay(1); // might help
|
||||||
|
#endif
|
||||||
|
suspendStripService = oldLock; // restore previous lock
|
||||||
DEBUGFS_PRINTF("took %d ms\n", millis() - s);
|
DEBUGFS_PRINTF("took %d ms\n", millis() - s);
|
||||||
doCloseFile = false;
|
doCloseFile = false;
|
||||||
}
|
}
|
||||||
@@ -61,7 +80,7 @@ static bool bufferedFind(const char *target, bool fromStart = true) {
|
|||||||
uint32_t s = millis();
|
uint32_t s = millis();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!f || !f.size()) return false;
|
if (!f || !f.size()) return false; // fast return when current file closed, or file size is zero
|
||||||
size_t targetLen = strlen(target);
|
size_t targetLen = strlen(target);
|
||||||
|
|
||||||
size_t index = 0;
|
size_t index = 0;
|
||||||
@@ -466,11 +485,11 @@ static const uint8_t *getPresetCache(size_t &size) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// WLEDMM
|
// WLEDMM
|
||||||
static bool haveLedmapFile = true;
|
static volatile bool haveLedmapFile = true;
|
||||||
static bool haveIndexFile = true;
|
static volatile bool haveIndexFile = true;
|
||||||
static bool haveSkinFile = true;
|
static volatile bool haveSkinFile = true;
|
||||||
static bool haveICOFile = true;
|
static volatile bool haveICOFile = true;
|
||||||
static bool haveCpalFile = true;
|
static volatile bool haveCpalFile = true;
|
||||||
void invalidateFileNameCache() { // reset "file not found" cache
|
void invalidateFileNameCache() { // reset "file not found" cache
|
||||||
haveLedmapFile = true;
|
haveLedmapFile = true;
|
||||||
haveIndexFile = true;
|
haveIndexFile = true;
|
||||||
|
|||||||
@@ -99,7 +99,11 @@ void WLED::reset()
|
|||||||
#endif
|
#endif
|
||||||
long dly = millis();
|
long dly = millis();
|
||||||
while (millis() - dly < 450) {
|
while (millis() - dly < 450) {
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
delay(2); // yield => BS
|
||||||
|
#else
|
||||||
yield(); // enough time to send response to client
|
yield(); // enough time to send response to client
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
applyBri();
|
applyBri();
|
||||||
USER_PRINTLN(F("\nWLED RESTART\n"));
|
USER_PRINTLN(F("\nWLED RESTART\n"));
|
||||||
@@ -191,6 +195,9 @@ void WLED::loop()
|
|||||||
reset();
|
reset();
|
||||||
|
|
||||||
if (doCloseFile) {
|
if (doCloseFile) {
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
delay(0); // yield => BS
|
||||||
|
#endif
|
||||||
closeFile();
|
closeFile();
|
||||||
yield();
|
yield();
|
||||||
}
|
}
|
||||||
@@ -203,7 +210,11 @@ void WLED::loop()
|
|||||||
#endif
|
#endif
|
||||||
handleNightlight();
|
handleNightlight();
|
||||||
handlePlaylist();
|
handlePlaylist();
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
delay(0); // yield => BS
|
||||||
|
#else
|
||||||
yield();
|
yield();
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef WLED_DISABLE_HUESYNC
|
#ifndef WLED_DISABLE_HUESYNC
|
||||||
handleHue();
|
handleHue();
|
||||||
@@ -211,7 +222,11 @@ void WLED::loop()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
handlePresets();
|
handlePresets();
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
delay(0); // yield => BS
|
||||||
|
#else
|
||||||
yield();
|
yield();
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(_MoonModules_WLED_) && defined(WLEDMM_FASTPATH)
|
#if defined(_MoonModules_WLED_) && defined(WLEDMM_FASTPATH)
|
||||||
#ifdef WLED_DEBUG
|
#ifdef WLED_DEBUG
|
||||||
|
|||||||
@@ -714,9 +714,9 @@ WLED_GLOBAL uint16_t olen _INIT(0);
|
|||||||
// General filesystem
|
// General filesystem
|
||||||
WLED_GLOBAL size_t fsBytesUsed _INIT(0);
|
WLED_GLOBAL size_t fsBytesUsed _INIT(0);
|
||||||
WLED_GLOBAL size_t fsBytesTotal _INIT(0);
|
WLED_GLOBAL size_t fsBytesTotal _INIT(0);
|
||||||
WLED_GLOBAL unsigned long presetsModifiedTime _INIT(0L);
|
WLED_GLOBAL volatile unsigned long presetsModifiedTime _INIT(0L);
|
||||||
WLED_GLOBAL JsonDocument* fileDoc;
|
WLED_GLOBAL JsonDocument* fileDoc;
|
||||||
WLED_GLOBAL bool doCloseFile _INIT(false);
|
WLED_GLOBAL volatile bool doCloseFile _INIT(false);
|
||||||
|
|
||||||
// presets
|
// presets
|
||||||
WLED_GLOBAL byte currentPreset _INIT(0);
|
WLED_GLOBAL byte currentPreset _INIT(0);
|
||||||
|
|||||||
Reference in New Issue
Block a user