Skip to content

Commit 60f8648

Browse files
committed
(experimental) glitch-free heap size measurement
only measures heap size when !strip.isUpdating(); otherwise returns last measured value. -> A bit inexact, but still better than regular flickering.
1 parent 1eaad1b commit 60f8648

5 files changed

Lines changed: 67 additions & 25 deletions

File tree

wled00/bus_manager.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -684,7 +684,7 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh
684684
MatrixPanel_I2S_DMA* display = nullptr;
685685
VirtualMatrixPanel* fourScanPanel = nullptr;
686686
HUB75_I2S_CFG mxconfig;
687-
size_t lastHeap = ESP.getFreeHeap();
687+
size_t lastHeap = getFreeHeapSize();
688688

689689
_valid = false;
690690
_len = 0;
@@ -951,7 +951,7 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh
951951
USER_PRINTF("\tLAT = %2d, OE = %2d, CLK = %2d\n\n", mxconfig.gpio.lat, mxconfig.gpio.oe, mxconfig.gpio.clk);
952952
USER_FLUSH();
953953

954-
DEBUG_PRINT(F("Free heap: ")); DEBUG_PRINTLN(ESP.getFreeHeap()); lastHeap = ESP.getFreeHeap();
954+
DEBUG_PRINT(F("Free heap: ")); DEBUG_PRINTLN(getFreeHeapSize()); lastHeap = getFreeHeapSize();
955955

956956
// check if we can re-use the existing display driver
957957
if (activeDisplay) {
@@ -996,7 +996,7 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh
996996
USER_PRINTLN("****** MatrixPanel_I2S_DMA !KABOOM! driver allocation failed ***********");
997997
activeDisplay = nullptr;
998998
activeFourScanPanel = nullptr;
999-
USER_PRINT(F("heap usage: ")); USER_PRINTLN(int(lastHeap - ESP.getFreeHeap()));
999+
USER_PRINT(F("heap usage: ")); USER_PRINTLN(int(lastHeap - getFreeHeapSize()));
10001000
return;
10011001
}
10021002

@@ -1027,19 +1027,19 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh
10271027
_bri = (last_bri > 0) ? last_bri : 25; // try to restore persistent brightness value
10281028

10291029
delay(24); // experimental
1030-
DEBUG_PRINT(F("heap usage: ")); DEBUG_PRINTLN(int(lastHeap - ESP.getFreeHeap()));
1030+
DEBUG_PRINT(F("heap usage: ")); DEBUG_PRINTLN(int(lastHeap - getFreeHeapSize()));
10311031
// Allocate memory and start DMA display
10321032
if (newDisplay && (display->begin() == false)) {
10331033
USER_PRINTLN("****** MatrixPanel_I2S_DMA !KABOOM! I2S memory allocation failed ***********");
1034-
USER_PRINT(F("heap usage: ")); USER_PRINTLN(int(lastHeap - ESP.getFreeHeap()));
1034+
USER_PRINT(F("heap usage: ")); USER_PRINTLN(int(lastHeap - getFreeHeapSize()));
10351035
_valid = false;
10361036
return;
10371037
}
10381038
else {
10391039
if (newDisplay) { USER_PRINTLN("MatrixPanel_I2S_DMA begin, started ok"); }
10401040
else { USER_PRINTLN("MatrixPanel_I2S_DMA begin, using existing display."); }
10411041

1042-
USER_PRINT(F("heap usage: ")); USER_PRINTLN(int(lastHeap - ESP.getFreeHeap()));
1042+
USER_PRINT(F("heap usage: ")); USER_PRINTLN(int(lastHeap - getFreeHeapSize()));
10431043
delay(18); // experiment - give the driver a moment (~ one full frame @ 60hz) to settle
10441044
_valid = true;
10451045
display->setBrightness8(_bri); // range is 0-255, 0 - 0%, 255 - 100% // [setBrightness()] Tried to set output brightness before begin()
@@ -1069,7 +1069,7 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh
10691069
USER_PRINTLN(F("MatrixPanel_I2S_DMA not started - not enough memory for leds buffer!"));
10701070
cleanup(); // free buffers, and deallocate pins
10711071
_valid = false;
1072-
USER_PRINT(F("heap usage: ")); USER_PRINTLN(int(lastHeap - ESP.getFreeHeap()));
1072+
USER_PRINT(F("heap usage: ")); USER_PRINTLN(int(lastHeap - getFreeHeapSize()));
10731073
return; // fail
10741074
}
10751075

@@ -1130,7 +1130,7 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh
11301130
#endif
11311131

11321132
instanceCount++;
1133-
USER_PRINT(F("heap usage: ")); USER_PRINTLN(int(lastHeap - ESP.getFreeHeap()));
1133+
USER_PRINT(F("heap usage: ")); USER_PRINTLN(int(lastHeap - getFreeHeapSize()));
11341134
}
11351135

11361136
void __attribute__((hot)) IRAM_ATTR BusHub75Matrix::setPixelColor(uint16_t pix, uint32_t c) {

wled00/fcn_declare.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -505,8 +505,14 @@ extern "C" {
505505
void p_free(void *ptr);
506506
}
507507
#ifndef ESP8266
508-
inline size_t getFreeHeapSize() { return heap_caps_get_free_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); } // returns free heap (ESP.getFreeHeap() can include other memory types) // WLEDMM can cause LED glitches
509-
inline size_t getContiguousFreeHeap() { return heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); } // returns largest contiguous free block // WLEDMM may glitch, too
508+
//inline size_t getFreeHeapSize() { return heap_caps_get_free_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); } // returns free heap (ESP.getFreeHeap() can include other memory types) // WLEDMM can cause LED glitches
509+
//inline size_t getContiguousFreeHeap() { return heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); } // returns largest contiguous free block // WLEDMM may glitch, too
510+
511+
extern size_t d_measureFreeHeap(void);
512+
extern size_t d_measureContiguousFreeHeap(void);
513+
inline size_t getFreeHeapSize() { return d_measureFreeHeap();} // total free heap - with flicker protection
514+
inline size_t getContiguousFreeHeap() { return d_measureContiguousFreeHeap();} // largest free block - with flicker protection
515+
510516
#else
511517
inline size_t getFreeHeapSize() { return ESP.getFreeHeap(); } // returns free heap
512518
inline size_t getContiguousFreeHeap() { return ESP.getMaxFreeBlockSize(); } // returns largest contiguous free block

wled00/json.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,11 +1128,15 @@ void serializeInfo(JsonObject root)
11281128
#endif
11291129
root[F("getflash")] = ESP.getFlashChipSize(); //WLEDMM and Athom, works for both ESP32 and ESP8266
11301130

1131-
root[F("freeheap")] = ESP.getFreeHeap();
1131+
//root[F("freeheap")] = ESP.getFreeHeap();
1132+
root[F("freeheap")] = getFreeHeapSize();
11321133
//WLEDMM: conditional on esp32
11331134
#if defined(ARDUINO_ARCH_ESP32)
11341135
root[F("freestack")] = uxTaskGetStackHighWaterMark(NULL); //WLEDMM
1135-
root[F("minfreeheap")] = ESP.getMinFreeHeap();
1136+
//root[F("minfreeheap")] = ESP.getMinFreeHeap();
1137+
auto maxFreeBlock = getContiguousFreeHeap();
1138+
root[F("minfreeheap")] = maxFreeBlock;
1139+
root[F("maxalloc")] = maxFreeBlock; // for upstream WLED compatibility
11361140
#endif
11371141
#if defined(ARDUINO_ARCH_ESP32)
11381142
#if defined(BOARD_HAS_PSRAM) || (ESP_IDF_VERSION_MAJOR > 3) // V4 can auto-detect PSRAM

wled00/util.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,30 @@ void *p_realloc_malloc(void *ptr, size_t size) { return d_realloc_malloc(ptr, si
752752
void p_free(void *ptr) { free(ptr); }
753753

754754
#else
755+
756+
static size_t lastHeap = 65535;
757+
static size_t lastMinHeap = 65535;
758+
inline static void d_measureHeap(void) {
759+
#ifdef WLEDMM_FILEWAIT // only when we don't use the RMTHI driver
760+
if (!strip.isUpdating()) // skip measurement while sending out LEDs - prevents flickering
761+
#endif
762+
{
763+
lastHeap = heap_caps_get_free_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
764+
lastMinHeap = heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
765+
}
766+
}
767+
768+
size_t d_measureContiguousFreeHeap(void) {
769+
d_measureHeap();
770+
return lastMinHeap;
771+
} // returns largest contiguous free block // WLEDMM may glitch, too
772+
773+
size_t d_measureFreeHeap(void) {
774+
d_measureHeap();
775+
return lastHeap;
776+
} // returns free heap (ESP.getFreeHeap() can include other memory types) // WLEDMM can cause LED glitches
777+
778+
755779
static void *validateFreeHeap(void *buffer) {
756780
// make sure there is enough free heap left if buffer was allocated in DRAM region, free it if not
757781
// TODO: between allocate and free, heap can run low (async web access), only IDF V5 allows for a pre-allocation-check of all free blocks

wled00/wled.cpp

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -358,8 +358,7 @@ void WLED::loop()
358358
DEBUG_PRINT(F("Name: ")); DEBUG_PRINTLN(serverDescription);
359359
DEBUG_PRINT(F("Runtime: ")); DEBUG_PRINTLN(millis());
360360
DEBUG_PRINT(F("Unix time: ")); toki.printTime(toki.getTime());
361-
DEBUG_PRINT(F("Free heap : ")); DEBUG_PRINTLN(ESP.getFreeHeap());
362-
DEBUG_PRINT(F("Free heap: ")); DEBUG_PRINTLN(ESP.getFreeHeap());
361+
DEBUG_PRINT(F("Free heap: ")); DEBUG_PRINTLN(getFreeHeapSize());
363362
//WLEDMM
364363
#ifdef ARDUINO_ARCH_ESP32
365364
DEBUG_PRINT(F("Avail heap: ")); DEBUG_PRINTLN(ESP.getMaxAllocHeap());
@@ -660,7 +659,7 @@ void WLED::setup()
660659
DEBUG_PRINT(F("esp8266 "));
661660
DEBUG_PRINTLN(ESP.getCoreVersion());
662661
#endif
663-
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap());
662+
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(getFreeHeapSize());
664663
#ifdef ARDUINO_ARCH_ESP32
665664
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0) // unfortunately not available in older framework versions
666665
DEBUG_PRINT(F("\nArduino max stack ")); DEBUG_PRINTLN(getArduinoLoopTaskStackSize());
@@ -763,7 +762,7 @@ void WLED::setup()
763762
DEBUG_PRINTLN(F("Registering usermods ..."));
764763
registerUsermods();
765764

766-
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap());
765+
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(getFreeHeapSize());
767766
#ifdef ARDUINO_ARCH_ESP32
768767
DEBUG_PRINTF("%s min free stack %d\n", pcTaskGetTaskName(NULL), uxTaskGetStackHighWaterMark(NULL)); //WLEDMM
769768
#endif
@@ -813,12 +812,12 @@ void WLED::setup()
813812

814813
DEBUG_PRINTLN(F("Initializing strip"));
815814
beginStrip();
816-
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap());
815+
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(getFreeHeapSize());
817816

818817
USER_PRINTLN(F("\nUsermods setup ..."));
819818
userSetup();
820819
usermods.setup();
821-
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap());
820+
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(getFreeHeapSize());
822821

823822
if (strcmp(clientSSID, DEFAULT_CLIENT_SSID) == 0)
824823
showWelcomePage = true;
@@ -882,7 +881,7 @@ void WLED::setup()
882881
// HTTP server page init
883882
DEBUG_PRINTLN(F("initServer"));
884883
initServer();
885-
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(ESP.getFreeHeap());
884+
DEBUG_PRINT(F("heap ")); DEBUG_PRINTLN(getFreeHeapSize());
886885
#ifdef ARDUINO_ARCH_ESP32
887886
DEBUG_PRINT(pcTaskGetTaskName(NULL)); DEBUG_PRINT(F(" free stack ")); DEBUG_PRINTLN(uxTaskGetStackHighWaterMark(NULL));
888887
#endif
@@ -967,7 +966,7 @@ void WLED::setup()
967966
USER_PRINTLN(F("\n"));
968967
#endif
969968

970-
USER_PRINT(F("Free heap ")); USER_PRINTLN(ESP.getFreeHeap());USER_PRINTLN();
969+
USER_PRINT(F("Free heap ")); USER_PRINTLN(getFreeHeapSize());USER_PRINTLN();
971970

972971
// WLEDMM force initial calculation of gamma correction LUT
973972
if ((gammaCorrectVal < 0.999f) || (gammaCorrectVal > 3.0f)) calcGammaTable(1.0f); // no gamma => create linear LUT
@@ -1346,13 +1345,22 @@ void WLED::handleConnection()
13461345
}
13471346

13481347
static unsigned retryCount = 0; // WLEDMM
1349-
#ifdef ARDUINO_ARCH_ESP32
1348+
#ifdef ARDUINO_ARCH_ESP32
13501349
// reconnect WiFi to clear stale allocations if heap gets too low
1351-
if ((!strip.isUpdating()) && (now - heapTime > 5000)) { // WLEDMM: updated with better logic for small heap available by block, not total. // WLEDMM trying to use a moment when the strip is idle
1352-
#if defined(ARDUINO_ARCH_ESP32S2) || defined(WLED_ENABLE_HUB75MATRIX)
1353-
uint32_t heap = ESP.getFreeHeap(); // WLEDMM works better on -S2
1350+
if ((now - heapTime > 5000) && !strip.isUpdating()) { // WLEDMM: updated with better logic for small heap available by block, not total. // WLEDMM trying to use a moment when the strip is idle
1351+
#ifdef WLEDMM_FILEWAIT // only when we don't use the RMTHI driver
1352+
// calling getContiguousFreeHeap() during led update causes glitches on C3
1353+
// this can (probably) be removed once RMT driver for C3 is fixed
1354+
unsigned t0 = millis();
1355+
while (strip.isUpdating() && (millis() - t0 < 15)) delay(1); // be nice, but not too nice. Waits up to 15ms
1356+
#endif
1357+
1358+
#if defined(ARDUINO_ARCH_ESP32S2) /*|| defined(WLED_ENABLE_HUB75MATRIX)*/
1359+
//uint32_t heap = ESP.getFreeHeap(); // WLEDMM works better on -S2
1360+
uint32_t heap = getFreeHeapSize(); // WLEDMM works better on -S2
13541361
#else
1355-
uint32_t heap = heap_caps_get_largest_free_block(0x1800); // WLEDMM: This is a better metric for free heap.
1362+
//uint32_t heap = heap_caps_get_largest_free_block(0x1800); // WLEDMM: This is a better metric for free heap.
1363+
uint32_t heap = getContiguousFreeHeap(); // WLEDMM: This is a better metric for free heap.
13561364
#endif
13571365
if (heap < MIN_HEAP_SIZE && lastHeap < MIN_HEAP_SIZE) {
13581366
if (retryCount < 5) { // WLEDMM avoid repeated disconnects

0 commit comments

Comments
 (0)