Skip to content

Commit 76cb2e9

Browse files
authored
Improvements to heap-memory and PSRAM handling (#4791)
* Improved heap and PSRAM handling - Segment `allocateData()` uses more elaborate DRAM checking to reduce fragmentation and allow for larger setups to run on low heap - Segment data allocation fails if minimum contiguous block size runs low to keep the UI working - Increased `MAX_SEGMENT_DATA` to account for better segment data handling - Memory allocation functions try to keep enough DRAM for segment data - Added constant `PSRAM_THRESHOLD` to improve PSARM usage - Increase MIN_HEAP_SIZE to reduce risk of breaking UI due to low memory for JSON response - ESP32 makes use of IRAM (no 8bit access) for pixeluffers, freeing up to 50kB of RAM - Fix to properly get available heap on all platforms: added function `getFreeHeapSize()` - Bugfix for effects that divide by SEGLEN: don't run FX in service() if segment is not active -Syntax fix in AR: calloc() uses (numelements, size) as arguments * Added new functions for allocation and heap checking - added `allocate_buffer()` function that can be used to allocate large buffers: takes parameters to set preferred ram location, including 32bit accessible RAM on ESP32. Returns null if heap runs low or switches to PSRAM - getFreeHeapSize() and getContiguousFreeHeap() helper functions for all platforms to correctly report free useable heap - updated some constants - updated segment data allocation to free the data if it is large - replaced "psramsafe" variable with it's #ifdef: BOARD_HAS_PSRAM and made accomodating changes - added some compile-time checks to handle invalid env. definitions - updated all allocation functions and some of the logic behind them - added use of fast RTC-Memory where available - increased MIN_HEAP_SIZE for all systems (improved stability in tests) - updated memory calculation in web-UI to account for required segment buffer - added UI alerts if buffer allocation fails - made getUsedSegmentData() non-private (used in buffer alloc function) - changed MAX_SEGMENT_DATA - added more detailed memory log to DEBUG output - added debug output to buffer alloc function
1 parent 9d70601 commit 76cb2e9

File tree

15 files changed

+357
-212
lines changed

15 files changed

+357
-212
lines changed

usermods/audioreactive/audio_reactive.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,8 @@ void FFTcode(void * parameter)
224224
DEBUGSR_PRINT("FFT started on core: "); DEBUGSR_PRINTLN(xPortGetCoreID());
225225

226226
// allocate FFT buffers on first call
227-
if (vReal == nullptr) vReal = (float*) calloc(sizeof(float), samplesFFT);
228-
if (vImag == nullptr) vImag = (float*) calloc(sizeof(float), samplesFFT);
227+
if (vReal == nullptr) vReal = (float*) calloc(samplesFFT, sizeof(float));
228+
if (vImag == nullptr) vImag = (float*) calloc(samplesFFT, sizeof(float));
229229
if ((vReal == nullptr) || (vImag == nullptr)) {
230230
// something went wrong
231231
if (vReal) free(vReal); vReal = nullptr;

wled00/FX.h

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -88,23 +88,26 @@ extern byte realtimeMode; // used in getMappedPixelIndex()
8888
#endif
8989
#define FPS_CALC_SHIFT 7 // bit shift for fixed point math
9090

91-
/* each segment uses 82 bytes of SRAM memory, so if you're application fails because of
92-
insufficient memory, decreasing MAX_NUM_SEGMENTS may help */
91+
// heap memory limit for effects data, pixel buffers try to reserve it if PSRAM is available
9392
#ifdef ESP8266
9493
#define MAX_NUM_SEGMENTS 16
9594
/* How much data bytes all segments combined may allocate */
96-
#define MAX_SEGMENT_DATA 5120
95+
#define MAX_SEGMENT_DATA (6*1024) // 6k by default
9796
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
98-
#define MAX_NUM_SEGMENTS 20
99-
#define MAX_SEGMENT_DATA (MAX_NUM_SEGMENTS*512) // 10k by default (S2 is short on free RAM)
97+
#define MAX_NUM_SEGMENTS 32
98+
#define MAX_SEGMENT_DATA (20*1024) // 20k by default (S2 is short on free RAM), limit does not apply if PSRAM is available
10099
#else
101-
#define MAX_NUM_SEGMENTS 32 // warning: going beyond 32 may consume too much RAM for stable operation
102-
#define MAX_SEGMENT_DATA (MAX_NUM_SEGMENTS*1280) // 40k by default
100+
#ifdef BOARD_HAS_PSRAM
101+
#define MAX_NUM_SEGMENTS 64
102+
#else
103+
#define MAX_NUM_SEGMENTS 32
104+
#endif
105+
#define MAX_SEGMENT_DATA (64*1024) // 64k by default, limit does not apply if PSRAM is available
103106
#endif
104107

105108
/* How much data bytes each segment should max allocate to leave enough space for other segments,
106109
assuming each segment uses the same amount of data. 256 for ESP8266, 640 for ESP32. */
107-
#define FAIR_DATA_PER_SEG (MAX_SEGMENT_DATA / WS2812FX::getMaxSegments())
110+
#define FAIR_DATA_PER_SEG (MAX_SEGMENT_DATA / MAX_NUM_SEGMENTS)
108111

109112
#define MIN_SHOW_DELAY (_frametime < 16 ? 8 : 15)
110113

@@ -533,7 +536,6 @@ class Segment {
533536

534537
protected:
535538

536-
inline static unsigned getUsedSegmentData() { return Segment::_usedSegmentData; }
537539
inline static void addUsedSegmentData(int len) { Segment::_usedSegmentData += len; }
538540

539541
inline uint32_t *getPixels() const { return pixels; }
@@ -600,8 +602,8 @@ class Segment {
600602
, _t(nullptr)
601603
{
602604
DEBUGFX_PRINTF_P(PSTR("-- Creating segment: %p [%d,%d:%d,%d]\n"), this, (int)start, (int)stop, (int)startY, (int)stopY);
603-
// allocate render buffer (always entire segment)
604-
pixels = static_cast<uint32_t*>(d_calloc(sizeof(uint32_t), length())); // error handling is also done in isActive()
605+
// allocate render buffer (always entire segment), prefer PSRAM if DRAM is running low. Note: impact on FPS with PSRAM buffer is low (<2% with QSPI PSRAM)
606+
pixels = static_cast<uint32_t*>(allocate_buffer(length() * sizeof(uint32_t), BFRALLOC_PREFER_PSRAM | BFRALLOC_NOBYTEACCESS | BFRALLOC_CLEAR));
605607
if (!pixels) {
606608
DEBUGFX_PRINTLN(F("!!! Not enough RAM for pixel buffer !!!"));
607609
extern byte errorFlag;
@@ -623,7 +625,7 @@ class Segment {
623625
#endif
624626
clearName();
625627
deallocateData();
626-
d_free(pixels);
628+
p_free(pixels);
627629
}
628630

629631
Segment& operator= (const Segment &orig); // copy assignment
@@ -646,7 +648,7 @@ class Segment {
646648
inline uint16_t groupLength() const { return grouping + spacing; }
647649
inline uint8_t getLightCapabilities() const { return _capabilities; }
648650
inline void deactivate() { setGeometry(0,0); }
649-
inline Segment &clearName() { d_free(name); name = nullptr; return *this; }
651+
inline Segment &clearName() { p_free(name); name = nullptr; return *this; }
650652
inline Segment &setName(const String &name) { return setName(name.c_str()); }
651653

652654
inline static unsigned vLength() { return Segment::_vLength; }
@@ -672,6 +674,7 @@ class Segment {
672674
inline uint16_t dataSize() const { return _dataLen; }
673675
bool allocateData(size_t len); // allocates effect data buffer in heap and clears it
674676
void deallocateData(); // deallocates (frees) effect data buffer from heap
677+
inline static unsigned getUsedSegmentData() { return Segment::_usedSegmentData; }
675678
/**
676679
* Flags that before the next effect is calculated,
677680
* the internal segment state should be reset.
@@ -868,8 +871,8 @@ class WS2812FX {
868871
}
869872

870873
~WS2812FX() {
871-
d_free(_pixels);
872-
d_free(_pixelCCT); // just in case
874+
p_free(_pixels);
875+
p_free(_pixelCCT); // just in case
873876
d_free(customMappingTable);
874877
_mode.clear();
875878
_modeData.clear();

wled00/FX_fcn.cpp

100644100755
Lines changed: 50 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,10 @@ Segment::Segment(const Segment &orig) {
6868
if (!stop) return; // nothing to do if segment is inactive/invalid
6969
if (orig.pixels) {
7070
// allocate pixel buffer: prefer IRAM/PSRAM
71-
pixels = static_cast<uint32_t*>(d_malloc(sizeof(uint32_t) * orig.length()));
71+
pixels = static_cast<uint32_t*>(allocate_buffer(orig.length() * sizeof(uint32_t), BFRALLOC_PREFER_PSRAM | BFRALLOC_NOBYTEACCESS));
7272
if (pixels) {
7373
memcpy(pixels, orig.pixels, sizeof(uint32_t) * orig.length());
74-
if (orig.name) { name = static_cast<char*>(d_malloc(strlen(orig.name)+1)); if (name) strcpy(name, orig.name); }
74+
if (orig.name) { name = static_cast<char*>(allocate_buffer(strlen(orig.name)+1, BFRALLOC_PREFER_PSRAM)); if (name) strcpy(name, orig.name); }
7575
if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); }
7676
} else {
7777
DEBUGFX_PRINTLN(F("!!! Not enough RAM for pixel buffer !!!"));
@@ -97,10 +97,10 @@ Segment& Segment::operator= (const Segment &orig) {
9797
//DEBUG_PRINTF_P(PSTR("-- Copying segment: %p -> %p\n"), &orig, this);
9898
if (this != &orig) {
9999
// clean destination
100-
if (name) { d_free(name); name = nullptr; }
100+
if (name) { p_free(name); name = nullptr; }
101101
if (_t) stopTransition(); // also erases _t
102102
deallocateData();
103-
d_free(pixels);
103+
p_free(pixels);
104104
// copy source
105105
memcpy((void*)this, (void*)&orig, sizeof(Segment));
106106
// erase pointers to allocated data
@@ -111,10 +111,10 @@ Segment& Segment::operator= (const Segment &orig) {
111111
// copy source data
112112
if (orig.pixels) {
113113
// allocate pixel buffer: prefer IRAM/PSRAM
114-
pixels = static_cast<uint32_t*>(d_malloc(sizeof(uint32_t) * orig.length()));
114+
pixels = static_cast<uint32_t*>(allocate_buffer(orig.length() * sizeof(uint32_t), BFRALLOC_PREFER_PSRAM | BFRALLOC_NOBYTEACCESS));
115115
if (pixels) {
116116
memcpy(pixels, orig.pixels, sizeof(uint32_t) * orig.length());
117-
if (orig.name) { name = static_cast<char*>(d_malloc(strlen(orig.name)+1)); if (name) strcpy(name, orig.name); }
117+
if (orig.name) { name = static_cast<char*>(allocate_buffer(strlen(orig.name)+1, BFRALLOC_PREFER_PSRAM)); if (name) strcpy(name, orig.name); }
118118
if (orig.data) { if (allocateData(orig._dataLen)) memcpy(data, orig.data, orig._dataLen); }
119119
} else {
120120
DEBUG_PRINTLN(F("!!! Not enough RAM for pixel buffer !!!"));
@@ -130,10 +130,10 @@ Segment& Segment::operator= (const Segment &orig) {
130130
Segment& Segment::operator= (Segment &&orig) noexcept {
131131
//DEBUG_PRINTF_P(PSTR("-- Moving segment: %p -> %p\n"), &orig, this);
132132
if (this != &orig) {
133-
if (name) { d_free(name); name = nullptr; } // free old name
133+
if (name) { p_free(name); name = nullptr; } // free old name
134134
if (_t) stopTransition(); // also erases _t
135135
deallocateData(); // free old runtime data
136-
d_free(pixels); // free old pixel buffer
136+
p_free(pixels); // free old pixel buffer
137137
// move source data
138138
memcpy((void*)this, (void*)&orig, sizeof(Segment));
139139
orig.name = nullptr;
@@ -147,35 +147,38 @@ Segment& Segment::operator= (Segment &&orig) noexcept {
147147

148148
// allocates effect data buffer on heap and initialises (erases) it
149149
bool Segment::allocateData(size_t len) {
150-
if (len == 0) return false; // nothing to do
151-
if (data && _dataLen >= len) { // already allocated enough (reduce fragmentation)
150+
if (len == 0) return false; // nothing to do
151+
if (data && _dataLen >= len) { // already allocated enough (reduce fragmentation)
152152
if (call == 0) {
153-
//DEBUG_PRINTF_P(PSTR("-- Clearing data (%d): %p\n"), len, this);
154-
memset(data, 0, len); // erase buffer if called during effect initialisation
153+
if (_dataLen < FAIR_DATA_PER_SEG) { // segment data is small
154+
//DEBUG_PRINTF_P(PSTR("-- Clearing data (%d): %p\n"), len, this);
155+
memset(data, 0, len); // erase buffer if called during effect initialisation
156+
return true; // no need to reallocate
157+
}
155158
}
156-
return true;
159+
else
160+
return true;
157161
}
158162
//DEBUG_PRINTF_P(PSTR("-- Allocating data (%d): %p\n"), len, this);
163+
// limit to MAX_SEGMENT_DATA if there is no PSRAM, otherwise prefer functionality over speed
164+
#ifndef BOARD_HAS_PSRAM
159165
if (Segment::getUsedSegmentData() + len - _dataLen > MAX_SEGMENT_DATA) {
160166
// not enough memory
161-
DEBUG_PRINTF_P(PSTR("!!! Not enough RAM: %d/%d !!!\n"), len, Segment::getUsedSegmentData());
167+
DEBUG_PRINTF_P(PSTR("SegmentData limit reached: %d/%d\n"), len, Segment::getUsedSegmentData());
162168
errorFlag = ERR_NORAM;
163169
return false;
164170
}
165-
// prefer DRAM over SPI RAM on ESP32 since it is slow
171+
#endif
172+
166173
if (data) {
167-
data = (byte*)d_realloc_malloc(data, len); // realloc with malloc fallback
168-
if (!data) {
169-
data = nullptr;
170-
Segment::addUsedSegmentData(-_dataLen); // subtract original buffer size
171-
_dataLen = 0; // reset data length
172-
}
174+
d_free(data); // free data and try to allocate again (segment buffer may be blocking contiguous heap)
175+
Segment::addUsedSegmentData(-_dataLen); // subtract buffer size
173176
}
174-
else data = (byte*)d_malloc(len);
177+
178+
data = static_cast<byte*>(allocate_buffer(len, BFRALLOC_PREFER_DRAM | BFRALLOC_CLEAR)); // prefer DRAM over PSRAM for speed
175179

176180
if (data) {
177-
memset(data, 0, len); // erase buffer
178-
Segment::addUsedSegmentData(len - _dataLen);
181+
Segment::addUsedSegmentData(len);
179182
_dataLen = len;
180183
//DEBUG_PRINTF_P(PSTR("--- Allocated data (%p): %d/%d -> %p\n"), this, len, Segment::getUsedSegmentData(), data);
181184
return true;
@@ -209,7 +212,11 @@ void Segment::deallocateData() {
209212
void Segment::resetIfRequired() {
210213
if (!reset || !isActive()) return;
211214
//DEBUG_PRINTF_P(PSTR("-- Segment reset: %p\n"), this);
212-
if (data && _dataLen > 0) memset(data, 0, _dataLen); // prevent heap fragmentation (just erase buffer instead of deallocateData())
215+
if (data && _dataLen > 0) {
216+
if (_dataLen > FAIR_DATA_PER_SEG) deallocateData(); // do not keep large allocations
217+
else memset(data, 0, _dataLen); // can prevent heap fragmentation
218+
DEBUG_PRINTF_P(PSTR("-- Segment %p reset, data cleared\n"), this);
219+
}
213220
if (pixels) for (size_t i = 0; i < length(); i++) pixels[i] = BLACK; // clear pixel buffer
214221
next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0;
215222
reset = false;
@@ -466,7 +473,7 @@ void Segment::setGeometry(uint16_t i1, uint16_t i2, uint8_t grp, uint8_t spc, ui
466473
if (length() != oldLength) {
467474
// allocate render buffer (always entire segment), prefer IRAM/PSRAM. Note: impact on FPS with PSRAM buffer is low (<2% with QSPI PSRAM) on S2/S3
468475
p_free(pixels);
469-
pixels = static_cast<uint32_t*>(d_malloc(sizeof(uint32_t) * length()));
476+
pixels = static_cast<uint32_t*>(allocate_buffer(length() * sizeof(uint32_t), BFRALLOC_PREFER_PSRAM | BFRALLOC_NOBYTEACCESS));
470477
if (!pixels) {
471478
DEBUGFX_PRINTLN(F("!!! Not enough RAM for pixel buffer !!!"));
472479
deallocateData();
@@ -581,8 +588,8 @@ Segment &Segment::setName(const char *newName) {
581588
if (newName) {
582589
const int newLen = min(strlen(newName), (size_t)WLED_MAX_SEGNAME_LEN);
583590
if (newLen) {
584-
if (name) d_free(name); // free old name
585-
name = static_cast<char*>(d_malloc(newLen+1));
591+
if (name) p_free(name); // free old name
592+
name = static_cast<char*>(allocate_buffer(newLen+1, BFRALLOC_PREFER_PSRAM));
586593
if (mode == FX_MODE_2DSCROLLTEXT) startTransition(strip.getTransition(), true); // if the name changes in scrolling text mode, we need to copy the segment for blending
587594
if (name) strlcpy(name, newName, newLen+1);
588595
return *this;
@@ -1177,7 +1184,10 @@ void WS2812FX::finalizeInit() {
11771184
mem += bus.memUsage(Bus::isDigital(bus.type) && !Bus::is2Pin(bus.type) ? digitalCount++ : 0); // includes global buffer
11781185
if (mem <= MAX_LED_MEMORY) {
11791186
if (BusManager::add(bus) == -1) break;
1180-
} else DEBUG_PRINTF_P(PSTR("Out of LED memory! Bus %d (%d) #%u not created."), (int)bus.type, (int)bus.count, digitalCount);
1187+
} else {
1188+
errorFlag = ERR_NORAM_PX; // alert UI
1189+
DEBUG_PRINTF_P(PSTR("Out of LED memory! Bus %d (%d) #%u not created."), (int)bus.type, (int)bus.count, digitalCount);
1190+
}
11811191
}
11821192
busConfigs.clear();
11831193
busConfigs.shrink_to_fit();
@@ -1209,10 +1219,11 @@ void WS2812FX::finalizeInit() {
12091219
deserializeMap(); // (re)load default ledmap (will also setUpMatrix() if ledmap does not exist)
12101220

12111221
// allocate frame buffer after matrix has been set up (gaps!)
1212-
d_free(_pixels); // using realloc on large buffers can cause additional fragmentation instead of reducing it
1213-
_pixels = static_cast<uint32_t*>(d_malloc(getLengthTotal() * sizeof(uint32_t)));
1222+
p_free(_pixels); // using realloc on large buffers can cause additional fragmentation instead of reducing it
1223+
// use PSRAM if available: there is no measurable perfomance impact between PSRAM and DRAM on S2/S3 with QSPI PSRAM for this buffer
1224+
_pixels = static_cast<uint32_t*>(allocate_buffer(getLengthTotal() * sizeof(uint32_t), BFRALLOC_ENFORCE_PSRAM | BFRALLOC_NOBYTEACCESS | BFRALLOC_CLEAR));
12141225
DEBUG_PRINTF_P(PSTR("strip buffer size: %uB\n"), getLengthTotal() * sizeof(uint32_t));
1215-
DEBUG_PRINTF_P(PSTR("Heap after strip init: %uB\n"), ESP.getFreeHeap());
1226+
DEBUG_PRINTF_P(PSTR("Heap after strip init: %uB\n"), getFreeHeapSize());
12161227
}
12171228

12181229
void WS2812FX::service() {
@@ -1552,7 +1563,11 @@ void WS2812FX::blendSegment(const Segment &topSegment) const {
15521563
}
15531564

15541565
void WS2812FX::show() {
1555-
if (!_pixels) return; // no pixels allocated, nothing to show
1566+
if (!_pixels) {
1567+
DEBUGFX_PRINTLN(F("Error: no _pixels!"));
1568+
errorFlag = ERR_NORAM;
1569+
return; // no pixels allocated, nothing to show
1570+
}
15561571

15571572
unsigned long showNow = millis();
15581573
size_t diff = showNow - _lastShow;
@@ -1562,7 +1577,7 @@ void WS2812FX::show() {
15621577
// we need to keep track of each pixel's CCT when blending segments (if CCT is present)
15631578
// and then set appropriate CCT from that pixel during paint (see below).
15641579
if ((hasCCTBus() || correctWB) && !cctFromRgb)
1565-
_pixelCCT = static_cast<uint8_t*>(d_malloc(totalLen * sizeof(uint8_t))); // allocate CCT buffer if necessary
1580+
_pixelCCT = static_cast<uint8_t*>(allocate_buffer(totalLen * sizeof(uint8_t), BFRALLOC_PREFER_PSRAM)); // allocate CCT buffer if necessary, prefer PSRAM
15661581
if (_pixelCCT) memset(_pixelCCT, 127, totalLen); // set neutral (50:50) CCT
15671582

15681583
if (realtimeMode == REALTIME_MODE_INACTIVE || useMainSegmentOnly || realtimeOverride > REALTIME_OVERRIDE_NONE) {
@@ -1596,7 +1611,7 @@ void WS2812FX::show() {
15961611
}
15971612
Bus::setCCT(oldCCT); // restore old CCT for ABL adjustments
15981613

1599-
d_free(_pixelCCT);
1614+
p_free(_pixelCCT);
16001615
_pixelCCT = nullptr;
16011616

16021617
// some buses send asynchronously and this method will return before

wled00/bus_manager.cpp

Lines changed: 21 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -39,35 +39,29 @@ uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
3939
uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, const byte *buffer, uint8_t bri=255, bool isRGBW=false);
4040

4141
//util.cpp
42-
// PSRAM allocation wrappers
43-
#if !defined(ESP8266) && !defined(CONFIG_IDF_TARGET_ESP32C3)
42+
// memory allocation wrappers
4443
extern "C" {
45-
void *p_malloc(size_t); // prefer PSRAM over DRAM
46-
void *p_calloc(size_t, size_t); // prefer PSRAM over DRAM
47-
void *p_realloc(void *, size_t); // prefer PSRAM over DRAM
48-
void *p_realloc_malloc(void *ptr, size_t size); // realloc with malloc fallback, prefer PSRAM over DRAM
49-
inline void p_free(void *ptr) { heap_caps_free(ptr); }
50-
void *d_malloc(size_t); // prefer DRAM over PSRAM
51-
void *d_calloc(size_t, size_t); // prefer DRAM over PSRAM
52-
void *d_realloc(void *, size_t); // prefer DRAM over PSRAM
53-
void *d_realloc_malloc(void *ptr, size_t size); // realloc with malloc fallback, prefer DRAM over PSRAM
44+
// prefer DRAM over PSRAM (if available) in d_ alloc functions
45+
void *d_malloc(size_t);
46+
void *d_calloc(size_t, size_t);
47+
void *d_realloc_malloc(void *ptr, size_t size);
48+
#ifndef ESP8266
5449
inline void d_free(void *ptr) { heap_caps_free(ptr); }
50+
#else
51+
inline void d_free(void *ptr) { free(ptr); }
52+
#endif
53+
#if defined(BOARD_HAS_PSRAM)
54+
// prefer PSRAM over DRAM in p_ alloc functions
55+
void *p_malloc(size_t);
56+
void *p_calloc(size_t, size_t);
57+
void *p_realloc_malloc(void *ptr, size_t size);
58+
inline void p_free(void *ptr) { heap_caps_free(ptr); }
59+
#else
60+
#define p_malloc d_malloc
61+
#define p_calloc d_calloc
62+
#define p_free d_free
63+
#endif
5564
}
56-
#else
57-
extern "C" {
58-
void *realloc_malloc(void *ptr, size_t size);
59-
}
60-
#define p_malloc malloc
61-
#define p_calloc calloc
62-
#define p_realloc realloc
63-
#define p_realloc_malloc realloc_malloc
64-
#define p_free free
65-
#define d_malloc malloc
66-
#define d_calloc calloc
67-
#define d_realloc realloc
68-
#define d_realloc_malloc realloc_malloc
69-
#define d_free free
70-
#endif
7165

7266
//color mangling macros
7367
#define RGBW32(r,g,b,w) (uint32_t((byte(w) << 24) | (byte(r) << 16) | (byte(g) << 8) | (byte(b))))
@@ -902,7 +896,7 @@ void BusManager::esp32RMTInvertIdle() {
902896
else if (lvl == RMT_IDLE_LEVEL_LOW) lvl = RMT_IDLE_LEVEL_HIGH;
903897
else continue;
904898
rmt_set_idle_level(ch, idle_out, lvl);
905-
u++
899+
u++;
906900
}
907901
}
908902
#endif

wled00/cfg.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
201201
}
202202
#endif
203203

204-
DEBUG_PRINTF_P(PSTR("Heap before buses: %d\n"), ESP.getFreeHeap());
204+
DEBUG_PRINTF_P(PSTR("Heap before buses: %d\n"), getFreeHeapSize());
205205
JsonArray ins = hw_led["ins"];
206206
if (!ins.isNull()) {
207207
int s = 0; // bus iterator

wled00/const.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -546,8 +546,21 @@ static_assert(WLED_MAX_BUSSES <= 32, "WLED_MAX_BUSSES exceeds hard limit");
546546
#endif
547547
#endif
548548

549-
// minimum heap size required to process web requests
550-
#define MIN_HEAP_SIZE 8192
549+
// minimum heap size required to process web requests: try to keep free heap above this value
550+
#ifdef ESP8266
551+
#define MIN_HEAP_SIZE (9*1024)
552+
#else
553+
#define MIN_HEAP_SIZE (15*1024) // WLED allocation functions (util.cpp) try to keep this much contiguous heap free for other tasks
554+
#endif
555+
// threshold for PSRAM use: if heap is running low, requests to allocate_buffer(prefer DRAM) above PSRAM_THRESHOLD may be put in PSRAM
556+
// if heap is depleted, PSRAM will be used regardless of threshold
557+
#if defined(CONFIG_IDF_TARGET_ESP32S3)
558+
#define PSRAM_THRESHOLD (12*1024) // S3 has plenty of DRAM
559+
#elif defined(CONFIG_IDF_TARGET_ESP32)
560+
#define PSRAM_THRESHOLD (5*1024)
561+
#else
562+
#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
563+
#endif
551564

552565
// Web server limits
553566
#ifdef ESP8266

0 commit comments

Comments
 (0)