Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 3 additions & 3 deletions usermods/artifx/arti.h
Original file line number Diff line number Diff line change
Expand Up @@ -2565,7 +2565,7 @@ class ARTI {
uint16_t programFileSize;
#if ARTI_PLATFORM == ARTI_ARDUINO
programFileSize = programFile.size();
programText = (char *)malloc(programFileSize+1);
programText = (char *)d_malloc(programFileSize+1);
programFile.read((byte *)programText, programFileSize);
programText[programFileSize] = '\0';
#else
Comment on lines 2567 to 2571
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Handle d_malloc failure before reading the program file

d_malloc can return nullptr (especially with MIN_HEAP_SIZE constraints). The current code immediately reads into programText, which will crash on allocation failure. Please guard and bail out cleanly.

🛠️ Suggested fix
   programFileSize = programFile.size();
   programText = (char *)d_malloc(programFileSize+1);
+  if (programText == nullptr) {
+    ERROR_ARTI("ARTI-FX: Out of memory allocating program text (%u bytes)\n", programFileSize + 1);
+    programFile.close();
+    return false;
+  }
   programFile.read((byte *)programText, programFileSize);
   programText[programFileSize] = '\0';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
programFileSize = programFile.size();
programText = (char *)malloc(programFileSize+1);
programText = (char *)d_malloc(programFileSize+1);
programFile.read((byte *)programText, programFileSize);
programText[programFileSize] = '\0';
#else
programFileSize = programFile.size();
programText = (char *)d_malloc(programFileSize+1);
if (programText == nullptr) {
ERROR_ARTI("ARTI-FX: Out of memory allocating program text (%u bytes)\n", programFileSize + 1);
programFile.close();
return false;
}
programFile.read((byte *)programText, programFileSize);
programText[programFileSize] = '\0';
`#else`
🤖 Prompt for AI Agents
In `@usermods/artifx/arti.h` around lines 2567 - 2571, The allocation for
programText using d_malloc can return nullptr; before calling programFile.read
or dereferencing programText, check the result of d_malloc and handle failure
(e.g., log an error via the existing error/reporting mechanism and return or
exit cleanly). Specifically, after programText = (char
*)d_malloc(programFileSize+1) validate programText != nullptr, avoid calling
programFile.read((byte *)programText, programFileSize) if null, and ensure any
resources (like programFile) are closed or cleaned up before bailing; reference
programFileSize, programText, d_malloc, and programFile.read to locate and
implement this guard.

Expand Down Expand Up @@ -2607,7 +2607,7 @@ class ARTI {
#endif

if (stages < 1) {
if (nullptr != programText) free(programText); // softhack007 prevent memory leak
if (nullptr != programText) d_free(programText); // softhack007 prevent memory leak
close();
return true;
}
Expand Down Expand Up @@ -2666,7 +2666,7 @@ class ARTI {
#endif
}
#if ARTI_PLATFORM == ARTI_ARDUINO //not on windows as cause crash???
free(programText);
d_free(programText);
#endif

if (stages >= 3)
Expand Down
12 changes: 6 additions & 6 deletions usermods/audioreactive/audio_reactive.h
Original file line number Diff line number Diff line change
Expand Up @@ -454,13 +454,13 @@ static bool alocateFFTBuffers(void) {
USER_PRINT(F("\nFree heap ")); USER_PRINTLN(ESP.getFreeHeap());
#endif

if (vReal) free(vReal); // should not happen
if (vImag) free(vImag); // should not happen
if ((vReal = (float*) calloc(samplesFFT, sizeof(float))) == nullptr) return false; // calloc or die
if ((vImag = (float*) calloc(samplesFFT, sizeof(float))) == nullptr) return false;
if (vReal) d_free(vReal); // should not happen
if (vImag) d_free(vImag); // should not happen
if ((vReal = (float*) d_calloc(samplesFFT, sizeof(float))) == nullptr) return false; // calloc or die
if ((vImag = (float*) d_calloc(samplesFFT, sizeof(float))) == nullptr) return false;
#ifdef FFT_MAJORPEAK_HUMAN_EAR
if (pinkFactors) free(pinkFactors);
if ((pinkFactors = (float*) calloc(samplesFFT, sizeof(float))) == nullptr) return false;
if (pinkFactors) p_free(pinkFactors);
if ((pinkFactors = (float*) p_calloc(samplesFFT, sizeof(float))) == nullptr) return false;
#endif
Comment on lines +457 to 464
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Free partially allocated FFT buffers on failure.

If vImag or pinkFactors allocation fails, vReal stays allocated and you return false, keeping the heap under pressure and hindering recovery. Free any already-allocated buffers before returning.

🛠️ Proposed fix
-  if ((vImag = (float*) d_calloc(samplesFFT, sizeof(float))) == nullptr) return false;
+  if ((vImag = (float*) d_calloc(samplesFFT, sizeof(float))) == nullptr) {
+    d_free(vReal);
+    vReal = nullptr;
+    return false;
+  }
`#ifdef` FFT_MAJORPEAK_HUMAN_EAR
-  if ((pinkFactors = (float*) p_calloc(samplesFFT, sizeof(float))) == nullptr) return false;
+  if ((pinkFactors = (float*) p_calloc(samplesFFT, sizeof(float))) == nullptr) {
+    d_free(vReal);
+    d_free(vImag);
+    vReal = nullptr;
+    vImag = nullptr;
+    return false;
+  }
`#endif`
🤖 Prompt for AI Agents
In `@usermods/audioreactive/audio_reactive.h` around lines 457 - 464, The
allocation sequence allocates vReal then vImag then pinkFactors but returns
early on failure without freeing any earlier allocations; update the failure
paths in the allocation block so that if vImag allocation fails you call
d_free(vReal) before returning false, and if pinkFactors allocation fails you
call d_free(vReal) and d_free(vImag) (and p_free(pinkFactors) only if non-null)
before returning false; use the existing d_free and p_free helpers and the same
null-check style used elsewhere to avoid leaks (references: vReal, vImag,
pinkFactors, d_calloc, p_calloc, d_free, p_free).


#ifdef SR_DEBUG
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ void RotaryEncoderUIUsermod::sortModesAndPalettes() {
}

byte *RotaryEncoderUIUsermod::re_initIndexArray(int numModes) {
byte *indexes = (byte *)malloc(sizeof(byte) * numModes);
byte *indexes = (byte *)d_calloc(numModes, sizeof(byte));
for (byte i = 0; i < numModes; i++) {
indexes[i] = i;
}
Comment on lines 354 to 358
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Guard d_calloc failure before populating the index array.

If d_calloc returns nullptr, the loop dereferences it and crashes. Add an early return on allocation failure.

🛠️ Proposed fix
-  byte *indexes = (byte *)d_calloc(numModes, sizeof(byte));
-  for (byte i = 0; i < numModes; i++) {
+  byte *indexes = (byte *)d_calloc(numModes, sizeof(byte));
+  if (!indexes) return nullptr;
+  for (byte i = 0; i < numModes; i++) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
byte *RotaryEncoderUIUsermod::re_initIndexArray(int numModes) {
byte *indexes = (byte *)malloc(sizeof(byte) * numModes);
byte *indexes = (byte *)d_calloc(numModes, sizeof(byte));
for (byte i = 0; i < numModes; i++) {
indexes[i] = i;
}
byte *RotaryEncoderUIUsermod::re_initIndexArray(int numModes) {
byte *indexes = (byte *)d_calloc(numModes, sizeof(byte));
if (!indexes) return nullptr;
for (byte i = 0; i < numModes; i++) {
indexes[i] = i;
}
🤖 Prompt for AI Agents
In `@usermods/usermod_v2_rotary_encoder_ui_ALT/usermod_v2_rotary_encoder_ui_ALT.h`
around lines 354 - 358, The function RotaryEncoderUIUsermod::re_initIndexArray
calls d_calloc and immediately dereferences the returned pointer; add a null
check after the allocation (check if indexes == nullptr) and if allocation
failed return nullptr (or handle the error) before entering the for loop that
writes to indexes, so the loop doesn't dereference a null pointer.

Expand All @@ -364,7 +364,7 @@ byte *RotaryEncoderUIUsermod::re_initIndexArray(int numModes) {
* They don't end in '\0', they end in '"'.
*/
const char **RotaryEncoderUIUsermod::re_findModeStrings(const char json[], int numModes) {
const char **modeStrings = (const char **)malloc(sizeof(const char *) * numModes);
const char **modeStrings = (const char **)d_calloc(numModes, sizeof(const char *));
uint8_t modeIndex = 0;
bool insideQuotes = false;
// advance past the mark for markLineNum that may exist.
Expand All @@ -380,7 +380,7 @@ const char **RotaryEncoderUIUsermod::re_findModeStrings(const char json[], int n
insideQuotes = !insideQuotes;
if (insideQuotes) {
// We have a new mode or palette
modeStrings[modeIndex] = (char *)(json + i + 1);
if (modeIndex < numModes) modeStrings[modeIndex] = (char *)(json + i + 1); //WLEDMM prevent array bounds violation
}
break;
case '[':
Expand Down
6 changes: 3 additions & 3 deletions wled00/FX.h
Original file line number Diff line number Diff line change
Expand Up @@ -609,7 +609,7 @@ typedef struct Segment {
endImagePlayback(this);
#endif

if ((Segment::_globalLeds == nullptr) && !strip_uses_global_leds() && (ledsrgb != nullptr)) {free(ledsrgb); ledsrgb = nullptr;} // WLEDMM we need "!strip_uses_global_leds()" to avoid crashes (#104)
if ((Segment::_globalLeds == nullptr) && !strip_uses_global_leds() && (ledsrgb != nullptr)) {d_free(ledsrgb); ledsrgb = nullptr;} // WLEDMM we need "!strip_uses_global_leds()" to avoid crashes (#104)
if (name) { delete[] name; name = nullptr; }
if (_t) { transitional = false; delete _t; _t = nullptr; }
deallocateData();
Expand Down Expand Up @@ -1009,15 +1009,15 @@ class WS2812FX { // 96 bytes
#ifdef WLED_DEBUG
if (Serial) Serial.println(F("~WS2812FX destroying strip.")); // WLEDMM can't use DEBUG_PRINTLN here
#endif
if (customMappingTable) delete[] customMappingTable;
if (customMappingTable) d_free(customMappingTable); customMappingTable = nullptr;
_mode.clear();
_modeData.clear();
_segments.clear();
#ifndef WLED_DISABLE_2D
panel.clear();
#endif
customPalettes.clear();
if (useLedsArray && Segment::_globalLeds) free(Segment::_globalLeds);
if (useLedsArray && Segment::_globalLeds) d_free(Segment::_globalLeds);
}

static WS2812FX* getInstance(void) { return instance; }
Expand Down
11 changes: 6 additions & 5 deletions wled00/FX_2Dfcn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,12 @@ void WS2812FX::setUpMatrix() {

// don't use new / delete
if ((size > 0) && (customMappingTable != nullptr)) { // resize
customMappingTable = (uint16_t*) reallocf(customMappingTable, sizeof(uint16_t) * size); // reallocf will free memory if it cannot resize
//customMappingTable = (uint16_t*) reallocf(customMappingTable, sizeof(uint16_t) * size); // reallocf will free memory if it cannot resize
customMappingTable = (uint16_t*) d_realloc_malloc(customMappingTable, sizeof(uint16_t) * size); // will free memory if it cannot resize
}
if ((size > 0) && (customMappingTable == nullptr)) { // second try
DEBUG_PRINTLN("setUpMatrix: trying to get fresh memory block.");
customMappingTable = (uint16_t*) calloc(size, sizeof(uint16_t));
customMappingTable = (uint16_t*) d_calloc(size, sizeof(uint16_t));
if (customMappingTable == nullptr) {
USER_PRINTLN("setUpMatrix: alloc failed");
errorFlag = ERR_LOW_MEM; // WLEDMM raise errorflag
Expand Down Expand Up @@ -122,7 +123,7 @@ void WS2812FX::setUpMatrix() {
JsonArray map = doc.as<JsonArray>();
gapSize = map.size();
if (!map.isNull() && (gapSize > 0) && gapSize >= customMappingSize) { // not an empty map //softhack also check gapSize>0
gapTable = new(std::nothrow) int8_t[gapSize];
gapTable = static_cast<int8_t*>(p_malloc(gapSize));
if (gapTable) for (size_t i = 0; i < gapSize; i++) {
gapTable[i] = constrain(map[i], -1, 1);
}
Expand Down Expand Up @@ -152,7 +153,7 @@ void WS2812FX::setUpMatrix() {
}

// delete gap array as we no longer need it
if (gapTable) {delete[] gapTable; gapTable=nullptr;} // softhack prevent dangling pointer
if (gapTable) {p_free(gapTable); gapTable=nullptr;} // softhack prevent dangling pointer

#ifdef WLED_DEBUG_MAPS
DEBUG_PRINTF("Matrix ledmap: \n");
Expand Down Expand Up @@ -185,7 +186,7 @@ void WS2812FX::setUpMatrix() {
if (customMappingTable[i] != (uint16_t)i ) isIdentity = false;
}
if (isIdentity) {
free(customMappingTable); customMappingTable = nullptr;
d_free(customMappingTable); customMappingTable = nullptr;
USER_PRINTF("!setupmatrix: customMappingTable is not needed. Dropping %d bytes.\n", customMappingTableSize * sizeof(uint16_t));
customMappingTableSize = 0;
customMappingSize = 0;
Expand Down
25 changes: 13 additions & 12 deletions wled00/FX_fcn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ void Segment::allocLeds() {
DEBUG_PRINTF("allocLeds warning: size == %u !!\n", size);
if (ledsrgb && (ledsrgbSize == 0)) {
USER_PRINTLN("allocLeds warning: ledsrgbSize == 0 but ledsrgb!=NULL");
free(ledsrgb); ledsrgb=nullptr;
d_free(ledsrgb); ledsrgb=nullptr;
} // softhack007 clean up buffer
}
if ((size > 0) && (!ledsrgb || size > ledsrgbSize)) { //softhack dont allocate zero bytes
Expand All @@ -124,8 +124,8 @@ void Segment::allocLeds() {
ledsrgb = nullptr;
portEXIT_CRITICAL(&ledsrgb_mux);

if (oldLedsRgb) free(oldLedsRgb); // we need a bigger buffer, so free the old one first
CRGB* newLedsRgb = (CRGB*)calloc(size, 1); // WLEDMM This is an OS call, so we should not wrap it in portEnterCRITICAL
if (oldLedsRgb) d_free(oldLedsRgb); // we need a bigger buffer, so free the old one first
CRGB* newLedsRgb = (CRGB*)d_calloc(size, 1); // WLEDMM This is an OS call, so we should not wrap it in portEnterCRITICAL

portENTER_CRITICAL(&ledsrgb_mux);
ledsrgb = newLedsRgb;
Expand Down Expand Up @@ -175,7 +175,7 @@ Segment& Segment::operator= (const Segment &orig) {
if (_t) delete _t;
CRGB* oldLeds = ledsrgb;
size_t oldLedsSize = ledsrgbSize;
if (ledsrgb && !Segment::_globalLeds) free(ledsrgb);
if (ledsrgb && !Segment::_globalLeds) d_free(ledsrgb);
deallocateData();
// copy source
memcpy((void*)this, (void*)&orig, sizeof(Segment));
Expand Down Expand Up @@ -212,7 +212,7 @@ Segment& Segment::operator= (Segment &&orig) noexcept {
if (name) { delete[] name; name = nullptr; } // free old name
deallocateData(); // free old runtime data
if (_t) { delete _t; _t = nullptr; }
if (ledsrgb && !Segment::_globalLeds) free(ledsrgb); //WLEDMM: not needed anymore as we will use leds from copy. no need to nullify ledsrgb as it gets new value in memcpy
if (ledsrgb && !Segment::_globalLeds) d_free(ledsrgb); //WLEDMM: not needed anymore as we will use leds from copy. no need to nullify ledsrgb as it gets new value in memcpy

// WLEDMM temporarily prevent any fast draw calls to old and new segment
orig._isSimpleSegment = false;
Expand Down Expand Up @@ -265,7 +265,7 @@ bool Segment::allocateData(size_t len, bool allowOverdraft) { // WLEDMM allowOv
// data = (byte*) ps_malloc(len);
//else
//#endif
data = (byte*) malloc(len);
data = (byte*) d_malloc(len);
if (!data) {
_dataLen = 0; // WLEDMM reset dataLen
if ((errorFlag != ERR_LOW_MEM) && (errorFlag != ERR_LOW_SEG_MEM)) { // spam filter
Expand All @@ -292,7 +292,7 @@ void Segment::deallocateData() {
_dataLen = 0;
return;
} // WLEDMM reset dataLen
free(data);
d_free(data);
data = nullptr;
DEBUG_PRINTF("Segment::deallocateData: free'd %d bytes.\n", _dataLen);
Segment::addUsedSegmentData(-_dataLen);
Expand All @@ -308,7 +308,7 @@ void Segment::deallocateData() {
*/
void Segment::resetIfRequired() {
if (reset) {
if (ledsrgb && !Segment::_globalLeds) { free(ledsrgb); ledsrgb = nullptr; ledsrgbSize=0;} // WLEDMM segment has changed, so we need a fresh buffer.
if (ledsrgb && !Segment::_globalLeds) { d_free(ledsrgb); ledsrgb = nullptr; ledsrgbSize=0;} // WLEDMM segment has changed, so we need a fresh buffer.
if (transitional && _t) { transitional = false; delete _t; _t = nullptr; }
deallocateData();
next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0;
Expand Down Expand Up @@ -1866,7 +1866,7 @@ void WS2812FX::finalizeInit(void)
portENTER_CRITICAL(&ledsrgb_mux);
Segment::_globalLeds = nullptr;
portEXIT_CRITICAL(&ledsrgb_mux);
free(oldGLeds);
d_free(oldGLeds);
purgeSegments(true); // WLEDMM moved here, because it seems to improve stability.
}
if (useLedsArray && getLengthTotal()>0) { // WLEDMM avoid malloc(0)
Expand All @@ -1877,7 +1877,7 @@ void WS2812FX::finalizeInit(void)
// Segment::_globalLeds = (CRGB*) ps_malloc(arrSize);
//else
//#endif
if (arrSize > 0) Segment::_globalLeds = (CRGB*) malloc(arrSize); // WLEDMM avoid malloc(0)
if (arrSize > 0) Segment::_globalLeds = (CRGB*) d_malloc(arrSize); // WLEDMM avoid malloc(0)
if ((Segment::_globalLeds != nullptr) && (arrSize > 0)) memset(Segment::_globalLeds, 0, arrSize); // WLEDMM avoid dereferencing nullptr
if ((Segment::_globalLeds == nullptr) && (arrSize > 0)) errorFlag = ERR_NORAM_PX; // WLEDMM raise errorflag
}
Expand Down Expand Up @@ -2703,11 +2703,12 @@ bool WS2812FX::deserializeMap(uint8_t n) {

// don't use new / delete
if ((size > 0) && (customMappingTable != nullptr)) {
customMappingTable = (uint16_t*) reallocf(customMappingTable, sizeof(uint16_t) * size); // reallocf will free memory if it cannot resize
//customMappingTable = (uint16_t*) reallocf(customMappingTable, sizeof(uint16_t) * size); // reallocf will free memory if it cannot resize
customMappingTable = (uint16_t*) d_realloc_malloc(customMappingTable, sizeof(uint16_t) * size); // will free memory if it cannot resize
}
if ((size > 0) && (customMappingTable == nullptr)) { // second try
DEBUG_PRINTLN("deserializeMap: trying to get fresh memory block.");
customMappingTable = (uint16_t*) calloc(size, sizeof(uint16_t));
customMappingTable = (uint16_t*) d_calloc(size, sizeof(uint16_t));
if (customMappingTable == nullptr) {
DEBUG_PRINTLN("deserializeMap: alloc failed!");
errorFlag = ERR_LOW_MEM; // WLEDMM raise errorflag
Expand Down
32 changes: 24 additions & 8 deletions wled00/bus_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,22 @@ uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, byte
#define DEBUGOUT Serial
#endif

//util.cpp
// memory allocation wrappers
// forward declaration: memory functions (util.cpp)
extern "C" {
// prefer DRAM in d_xalloc functions, PSRAM as fallback
void *d_malloc(size_t);
void *d_calloc(size_t, size_t);
void *d_realloc_malloc(void *ptr, size_t size);
void d_free(void *ptr);
// prefer PSRAM in p_xalloc functions, DRAM as fallback
void *p_malloc(size_t);
void *p_calloc(size_t, size_t);
void *p_realloc_malloc(void *ptr, size_t size);
void p_free(void *ptr);
}

#ifdef WLED_DEBUG
#ifndef ESP8266
#include <rom/rtc.h>
Expand Down Expand Up @@ -668,7 +684,7 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh
MatrixPanel_I2S_DMA* display = nullptr;
VirtualMatrixPanel* fourScanPanel = nullptr;
HUB75_I2S_CFG mxconfig;
size_t lastHeap = ESP.getFreeHeap();
size_t lastHeap = getFreeHeapSize();

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

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

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

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

delay(24); // experimental
DEBUG_PRINT(F("heap usage: ")); DEBUG_PRINTLN(int(lastHeap - ESP.getFreeHeap()));
DEBUG_PRINT(F("heap usage: ")); DEBUG_PRINTLN(int(lastHeap - getFreeHeapSize()));
// Allocate memory and start DMA display
if (newDisplay && (display->begin() == false)) {
USER_PRINTLN("****** MatrixPanel_I2S_DMA !KABOOM! I2S memory allocation failed ***********");
USER_PRINT(F("heap usage: ")); USER_PRINTLN(int(lastHeap - ESP.getFreeHeap()));
USER_PRINT(F("heap usage: ")); USER_PRINTLN(int(lastHeap - getFreeHeapSize()));
_valid = false;
return;
}
else {
if (newDisplay) { USER_PRINTLN("MatrixPanel_I2S_DMA begin, started ok"); }
else { USER_PRINTLN("MatrixPanel_I2S_DMA begin, using existing display."); }

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

Expand Down Expand Up @@ -1114,7 +1130,7 @@ BusHub75Matrix::BusHub75Matrix(BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWh
#endif

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

void __attribute__((hot)) IRAM_ATTR BusHub75Matrix::setPixelColor(uint16_t pix, uint32_t c) {
Expand Down
29 changes: 23 additions & 6 deletions wled00/const.h
Original file line number Diff line number Diff line change
Expand Up @@ -528,7 +528,29 @@
#endif
#endif

// Web server limits
// minimum heap size required to process web requests: try to keep free heap above this value
#if !defined(MIN_HEAP_SIZE)
#ifdef ESP8266
#define MIN_HEAP_SIZE (9*1024)
#else
#define MIN_HEAP_SIZE (15*1024) // WLED allocation functions (util.cpp) try to keep this much contiguous heap free for other tasks
#endif
#endif
// threshold for PSRAM use: if heap is running low, requests to allocate_buffer(prefer DRAM) above PSRAM_THRESHOLD may be put in PSRAM
// if heap is depleted, PSRAM will be used regardless of threshold
#if defined(CONFIG_IDF_TARGET_ESP32S3)
#define PSRAM_THRESHOLD (12*1024) // S3 has plenty of DRAM
#elif defined(CONFIG_IDF_TARGET_ESP32)
#define PSRAM_THRESHOLD (5*1024)
#else
#define PSRAM_THRESHOLD (2*1024) // S2 does not have a lot of RAM. C3 and ESP8266 do not support PSRAM: the value is not used
#endif

// Web server limits (8k for AsyncWebServer)
//#if !defined(MIN_HEAP_SIZE)
//#define MIN_HEAP_SIZE 8192
//#endif

#ifdef ESP8266
// Minimum heap to consider handling a request
#define WLED_REQUEST_MIN_HEAP (8*1024)
Expand All @@ -545,11 +567,6 @@
// Websockets do not count against this limit.
#define WLED_REQUEST_MAX_QUEUE 6

//#define MIN_HEAP_SIZE (8k for AsyncWebServer)
#if !defined(MIN_HEAP_SIZE)
#define MIN_HEAP_SIZE 8192
#endif

// Maximum size of node map (list of other WLED instances)
#ifdef ESP8266
#define WLED_MAX_NODES 24
Expand Down
Loading