- LED memory calculation (not UI)
- potential fix for #4040
- compiler warning in FX
This commit is contained in:
Blaz Kristan 2024-07-07 14:18:51 +02:00
parent 5874b78349
commit 887254f5da
8 changed files with 87 additions and 67 deletions

View File

@ -2367,7 +2367,7 @@ uint16_t mode_meteor() {
} }
// draw meteor // draw meteor
for (int j = 0; j < meteorSize; j++) { for (unsigned j = 0; j < meteorSize; j++) {
int index = (in + j) % SEGLEN; int index = (in + j) % SEGLEN;
int idx = 255; int idx = 255;
int i = trail[index] = max; int i = trail[index] = max;

View File

@ -670,9 +670,9 @@ void BusNetwork::cleanup() {
uint32_t BusManager::memUsage(BusConfig &bc) { uint32_t BusManager::memUsage(BusConfig &bc) {
if (bc.type == TYPE_ONOFF || IS_PWM(bc.type)) return 5; if (bc.type == TYPE_ONOFF || IS_PWM(bc.type)) return 5;
uint16_t len = bc.count + bc.skipAmount; unsigned len = bc.count + bc.skipAmount;
uint16_t channels = Bus::getNumberOfChannels(bc.type); unsigned channels = Bus::getNumberOfChannels(bc.type);
uint16_t multiplier = 1; unsigned multiplier = 1;
if (IS_DIGITAL(bc.type)) { // digital types if (IS_DIGITAL(bc.type)) { // digital types
if (IS_16BIT(bc.type)) len *= 2; // 16-bit LEDs if (IS_16BIT(bc.type)) len *= 2; // 16-bit LEDs
#ifdef ESP8266 #ifdef ESP8266
@ -686,6 +686,12 @@ uint32_t BusManager::memUsage(BusConfig &bc) {
return (len * multiplier + bc.doubleBuffer * (bc.count + bc.skipAmount)) * channels; return (len * multiplier + bc.doubleBuffer * (bc.count + bc.skipAmount)) * channels;
} }
uint32_t BusManager::memUsage(unsigned maxChannels, unsigned maxCount, unsigned minBuses) {
//ESP32 RMT uses double buffer, parallel I2S uses 8x buffer (3 times)
unsigned multiplier = PolyBus::isParallelI2S1Output() ? 3 : 2;
return (maxChannels * maxCount * minBuses * multiplier);
}
int BusManager::add(BusConfig &bc) { int BusManager::add(BusConfig &bc) {
if (getNumBusses() - getNumVirtualBusses() >= WLED_MAX_BUSSES) return -1; if (getNumBusses() - getNumVirtualBusses() >= WLED_MAX_BUSSES) return -1;
if (IS_VIRTUAL(bc.type)) { if (IS_VIRTUAL(bc.type)) {

View File

@ -356,6 +356,7 @@ class BusManager {
//utility to get the approx. memory usage of a given BusConfig //utility to get the approx. memory usage of a given BusConfig
static uint32_t memUsage(BusConfig &bc); static uint32_t memUsage(BusConfig &bc);
static uint32_t memUsage(unsigned channels, unsigned count, unsigned buses = 1);
static uint16_t currentMilliamps(void) { return _milliAmpsUsed; } static uint16_t currentMilliamps(void) { return _milliAmpsUsed; }
static uint16_t ablMilliampsMax(void) { return _milliAmpsMax; } static uint16_t ablMilliampsMax(void) { return _milliAmpsMax; }

View File

@ -178,7 +178,7 @@ void handleAnalog(uint8_t b)
#ifdef ESP8266 #ifdef ESP8266
rawReading = analogRead(A0) << 2; // convert 10bit read to 12bit rawReading = analogRead(A0) << 2; // convert 10bit read to 12bit
#else #else
if ((btnPin[b] < 0) || (digitalPinToAnalogChannel(btnPin[b]) < 0)) return; // pin must support analog ADC - newer esp32 frameworks throw lots of warnings otherwise if ((btnPin[b] < 0) /*|| (digitalPinToAnalogChannel(btnPin[b]) < 0)*/) return; // pin must support analog ADC - newer esp32 frameworks throw lots of warnings otherwise
rawReading = analogRead(btnPin[b]); // collect at full 12bit resolution rawReading = analogRead(btnPin[b]); // collect at full 12bit resolution
#endif #endif
yield(); // keep WiFi task running - analog read may take several millis on ESP8266 yield(); // keep WiFi task running - analog read may take several millis on ESP8266

View File

@ -160,44 +160,46 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
DEBUG_PRINTF_P(PSTR("Heap before buses: %d\n"), ESP.getFreeHeap()); DEBUG_PRINTF_P(PSTR("Heap before buses: %d\n"), ESP.getFreeHeap());
int s = 0; // bus iterator int s = 0; // bus iterator
if (fromFS) BusManager::removeAll(); // can't safely manipulate busses directly in network callback if (fromFS) BusManager::removeAll(); // can't safely manipulate busses directly in network callback
uint32_t mem = 0; unsigned mem = 0;
bool busesChanged = false;
// determine if it is sensible to use parallel I2S outputs on ESP32 (i.e. more than 5 outputs = 1 I2S + 4 RMT) // determine if it is sensible to use parallel I2S outputs on ESP32 (i.e. more than 5 outputs = 1 I2S + 4 RMT)
bool useParallel = false; bool useParallel = false;
#if defined(ARDUINO_ARCH_ESP32) && !defined(ARDUINO_ARCH_ESP32S2) && !defined(ARDUINO_ARCH_ESP32S3) && !defined(ARDUINO_ARCH_ESP32C3) #if defined(ARDUINO_ARCH_ESP32) && !defined(ARDUINO_ARCH_ESP32S2) && !defined(ARDUINO_ARCH_ESP32S3) && !defined(ARDUINO_ARCH_ESP32C3)
unsigned digitalCount = 0; unsigned digitalCount = 0;
unsigned maxLeds = 0; unsigned maxLedsOnBus = 0;
int oldType = 0; unsigned maxChannels = 0;
int j = 0;
for (JsonObject elm : ins) { for (JsonObject elm : ins) {
unsigned type = elm["type"] | TYPE_WS2812_RGB; unsigned type = elm["type"] | TYPE_WS2812_RGB;
unsigned len = elm["len"] | 30; unsigned len = elm["len"] | DEFAULT_LED_COUNT;
if (IS_DIGITAL(type) && !IS_2PIN(type)) digitalCount++; if (!IS_DIGITAL(type)) continue;
if (len > maxLeds) maxLeds = len; if (!IS_2PIN(type)) {
// we need to have all LEDs of the same type for parallel digitalCount++;
if (j++ < 8 && oldType > 0 && oldType != type) oldType = -1; unsigned channels = Bus::getNumberOfChannels(type);
else if (oldType == 0) oldType = type; if (len > maxLedsOnBus) maxLedsOnBus = len;
if (channels > maxChannels) maxChannels = channels;
}
} }
DEBUG_PRINTF_P(PSTR("Maximum LEDs on a bus: %u\nDigital buses: %u\nDifferent types: %d\n"), maxLeds, digitalCount, (int)(oldType == -1)); DEBUG_PRINTF_P(PSTR("Maximum LEDs on a bus: %u\nDigital buses: %u\n"), maxLedsOnBus, digitalCount);
// we may remove 300 LEDs per bus limit when NeoPixelBus is updated beyond 2.9.0 // we may remove 300 LEDs per bus limit when NeoPixelBus is updated beyond 2.9.0
if (/*oldType != -1 && */maxLeds <= 300 && digitalCount > 5) { if (maxLedsOnBus <= 300 && digitalCount > 5) {
DEBUG_PRINTLN(F("Switching to parallel I2S."));
useParallel = true; useParallel = true;
BusManager::useParallelOutput(); BusManager::useParallelOutput();
DEBUG_PRINTF_P(PSTR("Switching to parallel I2S with max. %d LEDs per ouptut.\n"), maxLeds); mem = BusManager::memUsage(maxChannels, maxLedsOnBus, 8); // use alternate memory calculation
} }
#endif #endif
for (JsonObject elm : ins) { for (JsonObject elm : ins) {
if (s >= WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES) break; if (s >= WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES) break;
uint8_t pins[5] = {255, 255, 255, 255, 255}; uint8_t pins[5] = {255, 255, 255, 255, 255};
JsonArray pinArr = elm["pin"]; JsonArray pinArr = elm["pin"];
if (pinArr.size() == 0) continue; if (pinArr.size() == 0) continue;
pins[0] = pinArr[0]; //pins[0] = pinArr[0];
unsigned i = 0; unsigned i = 0;
for (int p : pinArr) { for (int p : pinArr) {
pins[i++] = p; pins[i++] = p;
if (i>4) break; if (i>4) break;
} }
uint16_t length = elm["len"] | 1; uint16_t length = elm["len"] | 1;
uint8_t colorOrder = (int)elm[F("order")]; // contains white channel swap option in upper nibble uint8_t colorOrder = (int)elm[F("order")]; // contains white channel swap option in upper nibble
uint8_t skipFirst = elm[F("skip")]; uint8_t skipFirst = elm[F("skip")];
@ -208,7 +210,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
bool refresh = elm["ref"] | false; bool refresh = elm["ref"] | false;
uint16_t freqkHz = elm[F("freq")] | 0; // will be in kHz for DotStar and Hz for PWM uint16_t freqkHz = elm[F("freq")] | 0; // will be in kHz for DotStar and Hz for PWM
uint8_t AWmode = elm[F("rgbwm")] | RGBW_MODE_MANUAL_ONLY; uint8_t AWmode = elm[F("rgbwm")] | RGBW_MODE_MANUAL_ONLY;
uint8_t maPerLed = elm[F("ledma")] | 55; uint8_t maPerLed = elm[F("ledma")] | LED_MILLIAMPS_DEFAULT;
uint16_t maMax = elm[F("maxpwr")] | (ablMilliampsMax * length) / total; // rough (incorrect?) per strip ABL calculation when no config exists uint16_t maMax = elm[F("maxpwr")] | (ablMilliampsMax * length) / total; // rough (incorrect?) per strip ABL calculation when no config exists
// To disable brightness limiter we either set output max current to 0 or single LED current to 0 (we choose output max current) // To disable brightness limiter we either set output max current to 0 or single LED current to 0 (we choose output max current)
if (IS_PWM(ledType) || IS_ONOFF(ledType) || IS_VIRTUAL(ledType)) { // analog and virtual if (IS_PWM(ledType) || IS_ONOFF(ledType) || IS_VIRTUAL(ledType)) { // analog and virtual
@ -219,26 +221,21 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
if (fromFS) { if (fromFS) {
BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax); BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax);
if (useParallel && s < 8) { if (useParallel && s < 8) {
// we are using parallel I2S and memUsage() will include x8 allocation into account // if for some unexplained reason the above pre-calculation was wrong, update
if (s == 0) unsigned memT = BusManager::memUsage(bc); // includes x8 memory allocation for parallel I2S
mem = BusManager::memUsage(bc); // includes x8 memory allocation for parallel I2S if (memT > mem) mem = memT; // if we have unequal LED count use the largest
else
if (BusManager::memUsage(bc) > mem)
mem = BusManager::memUsage(bc); // if we have unequal LED count use the largest
} else } else
mem += BusManager::memUsage(bc); // includes global buffer mem += BusManager::memUsage(bc); // includes global buffer
if (mem <= MAX_LED_MEMORY) if (BusManager::add(bc) == -1) break; // finalization will be done in WLED::beginStrip() if (mem <= MAX_LED_MEMORY) if (BusManager::add(bc) == -1) break; // finalization will be done in WLED::beginStrip()
} else { } else {
if (busConfigs[s] != nullptr) delete busConfigs[s]; if (busConfigs[s] != nullptr) delete busConfigs[s];
busConfigs[s] = new BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax); busConfigs[s] = new BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax);
busesChanged = true; doInitBusses = true; // finalization done in beginStrip()
} }
s++; s++;
} }
DEBUG_PRINTF_P(PSTR("LED buffer size: %uB\n"), mem); DEBUG_PRINTF_P(PSTR("LED buffer size: %uB\n"), mem);
DEBUG_PRINTF_P(PSTR("Heap after buses: %d\n"), ESP.getFreeHeap()); DEBUG_PRINTF_P(PSTR("Heap after buses: %d\n"), ESP.getFreeHeap());
doInitBusses = busesChanged;
// finalization done in beginStrip()
} }
if (hw_led["rev"]) BusManager::getBus(0)->setReversed(true); //set 0.11 global reversed setting for first bus if (hw_led["rev"]) BusManager::getBus(0)->setReversed(true); //set 0.11 global reversed setting for first bus
@ -275,22 +272,34 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
btnPin[s] = pin; btnPin[s] = pin;
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
// ESP32 only: check that analog button pin is a valid ADC gpio // ESP32 only: check that analog button pin is a valid ADC gpio
if (((buttonType[s] == BTN_TYPE_ANALOG) || (buttonType[s] == BTN_TYPE_ANALOG_INVERTED)) && (digitalPinToAnalogChannel(btnPin[s]) < 0)) if ((buttonType[s] == BTN_TYPE_ANALOG) || (buttonType[s] == BTN_TYPE_ANALOG_INVERTED)) {
{ if (digitalPinToAnalogChannel(btnPin[s]) < 0) {
// not an ADC analog pin // not an ADC analog pin
DEBUG_PRINT(F("PIN ALLOC error: GPIO")); DEBUG_PRINT(btnPin[s]); DEBUG_PRINT(F("PIN ALLOC error: GPIO")); DEBUG_PRINT(btnPin[s]);
DEBUG_PRINT(F("for analog button #")); DEBUG_PRINT(s); DEBUG_PRINT(F("for analog button #")); DEBUG_PRINT(s);
DEBUG_PRINTLN(F(" is not an analog pin!")); DEBUG_PRINTLN(F(" is not an analog pin!"));
btnPin[s] = -1; btnPin[s] = -1;
pinManager.deallocatePin(pin,PinOwner::Button); pinManager.deallocatePin(pin,PinOwner::Button);
} else {
analogReadResolution(12); // see #4040
}
} }
//if touch pin, enable the touch interrupt on ESP32 S2 & S3 else if ((buttonType[s] == BTN_TYPE_TOUCH || buttonType[s] == BTN_TYPE_TOUCH_SWITCH))
#ifdef SOC_TOUCH_VERSION_2 // ESP32 S2 and S3 have a fucntion to check touch state but need to attach an interrupt to do so
if ((buttonType[s] == BTN_TYPE_TOUCH || buttonType[s] == BTN_TYPE_TOUCH_SWITCH))
{ {
touchAttachInterrupt(btnPin[s], touchButtonISR, 256 + (touchThreshold << 4)); // threshold on Touch V2 is much higher (1500 is a value given by Espressif example, I measured changes of over 5000) if (digitalPinToTouchChannel(btnPin[s]) < 0) {
// not a touch pin
DEBUG_PRINTF_P(PSTR("PIN ALLOC error: GPIO%d for touch button #%d is not an touch pin!\n"), btnPin[s], s);
btnPin[s] = -1;
pinManager.deallocatePin(pin,PinOwner::Button);
}
//if touch pin, enable the touch interrupt on ESP32 S2 & S3
#ifdef SOC_TOUCH_VERSION_2 // ESP32 S2 and S3 have a function to check touch state but need to attach an interrupt to do so
else
{
touchAttachInterrupt(btnPin[s], touchButtonISR, 256 + (touchThreshold << 4)); // threshold on Touch V2 is much higher (1500 is a value given by Espressif example, I measured changes of over 5000)
}
#endif
} }
#endif
else else
#endif #endif
{ {

View File

@ -266,12 +266,16 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
buttonType[i] = request->arg(be).toInt(); buttonType[i] = request->arg(be).toInt();
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
// ESP32 only: check that button pin is a valid gpio // ESP32 only: check that button pin is a valid gpio
if (((buttonType[i] == BTN_TYPE_ANALOG) || (buttonType[i] == BTN_TYPE_ANALOG_INVERTED)) && (digitalPinToAnalogChannel(btnPin[i]) < 0)) if ((buttonType[i] == BTN_TYPE_ANALOG) || (buttonType[i] == BTN_TYPE_ANALOG_INVERTED))
{ {
// not an ADC analog pin if (digitalPinToAnalogChannel(btnPin[i]) < 0) {
DEBUG_PRINTF_P(PSTR("PIN ALLOC error: GPIO%d for analog button #%d is not an analog pin!\n"), btnPin[i], i); // not an ADC analog pin
btnPin[i] = -1; DEBUG_PRINTF_P(PSTR("PIN ALLOC error: GPIO%d for analog button #%d is not an analog pin!\n"), btnPin[i], i);
pinManager.deallocatePin(hw_btn_pin,PinOwner::Button); btnPin[i] = -1;
pinManager.deallocatePin(hw_btn_pin,PinOwner::Button);
} else {
analogReadResolution(12); // see #4040
}
} }
else if ((buttonType[i] == BTN_TYPE_TOUCH || buttonType[i] == BTN_TYPE_TOUCH_SWITCH)) else if ((buttonType[i] == BTN_TYPE_TOUCH || buttonType[i] == BTN_TYPE_TOUCH_SWITCH))
{ {

View File

@ -175,39 +175,39 @@ void WLED::loop()
DEBUG_PRINTLN(F("Re-init busses.")); DEBUG_PRINTLN(F("Re-init busses."));
bool aligned = strip.checkSegmentAlignment(); //see if old segments match old bus(ses) bool aligned = strip.checkSegmentAlignment(); //see if old segments match old bus(ses)
BusManager::removeAll(); BusManager::removeAll();
unsigned mem = 0;
// determine if it is sensible to use parallel I2S outputs on ESP32 (i.e. more than 5 outputs = 1 I2S + 4 RMT) // determine if it is sensible to use parallel I2S outputs on ESP32 (i.e. more than 5 outputs = 1 I2S + 4 RMT)
bool useParallel = false; bool useParallel = false;
#if defined(ARDUINO_ARCH_ESP32) && !defined(ARDUINO_ARCH_ESP32S2) && !defined(ARDUINO_ARCH_ESP32S3) && !defined(ARDUINO_ARCH_ESP32C3) #if defined(ARDUINO_ARCH_ESP32) && !defined(ARDUINO_ARCH_ESP32S2) && !defined(ARDUINO_ARCH_ESP32S3) && !defined(ARDUINO_ARCH_ESP32C3)
unsigned digitalCount = 0; unsigned digitalCount = 0;
unsigned maxLeds = 0; unsigned maxLedsOnBus = 0;
int oldType = 0; unsigned maxChannels = 0;
for (unsigned i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) { for (unsigned i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
if (busConfigs[i] == nullptr) break; if (busConfigs[i] == nullptr) break;
if (IS_DIGITAL(busConfigs[i]->type) && !IS_2PIN(busConfigs[i]->type)) digitalCount++; if (!IS_DIGITAL(busConfigs[i]->type)) continue;
if (busConfigs[i]->count > maxLeds) maxLeds = busConfigs[i]->count; if (!IS_2PIN(busConfigs[i]->type)) {
// we need to have all LEDs of the same type for parallel digitalCount++;
if (i < 8 && oldType > 0 && oldType != busConfigs[i]->type) oldType = -1; unsigned channels = Bus::getNumberOfChannels(busConfigs[i]->type);
else if (oldType == 0) oldType = busConfigs[i]->type; if (busConfigs[i]->count > maxLedsOnBus) maxLedsOnBus = busConfigs[i]->count;
if (channels > maxChannels) maxChannels = channels;
}
} }
DEBUG_PRINTF_P(PSTR("Maximum LEDs on a bus: %u\nDigital buses: %u\nDifferent types: %d\n"), maxLeds, digitalCount, (int)(oldType == -1)); DEBUG_PRINTF_P(PSTR("Maximum LEDs on a bus: %u\nDigital buses: %u\n"), maxLedsOnBus, digitalCount);
// we may remove 300 LEDs per bus limit when NeoPixelBus is updated beyond 2.9.0 // we may remove 300 LEDs per bus limit when NeoPixelBus is updated beyond 2.9.0
if (/*oldType != -1 && */maxLeds <= 300 && digitalCount > 5) { if (maxLedsOnBus <= 300 && digitalCount > 5) {
DEBUG_PRINTF_P(PSTR("Switching to parallel I2S."));
useParallel = true; useParallel = true;
BusManager::useParallelOutput(); BusManager::useParallelOutput();
DEBUG_PRINTF_P(PSTR("Switching to parallel I2S with max. %d LEDs per ouptut.\n"), maxLeds); mem = BusManager::memUsage(maxChannels, maxLedsOnBus, 8); // use alternate memory calculation (hse to be used *after* useParallelOutput())
} }
#endif #endif
// create buses/outputs // create buses/outputs
unsigned mem = 0;
for (unsigned i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) { for (unsigned i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
if (busConfigs[i] == nullptr || (!useParallel && i > 10)) break; if (busConfigs[i] == nullptr || (!useParallel && i > 10)) break;
if (useParallel && i < 8) { if (useParallel && i < 8) {
// we are using parallel I2S and memUsage() will include x8 allocation into account // if for some unexplained reason the above pre-calculation was wrong, update
if (i == 0) unsigned memT = BusManager::memUsage(*busConfigs[i]); // includes x8 memory allocation for parallel I2S
mem = BusManager::memUsage(*busConfigs[i]); // includes x8 memory allocation for parallel I2S if (memT > mem) mem = memT; // if we have unequal LED count use the largest
else
if (BusManager::memUsage(*busConfigs[i]) > mem)
mem = BusManager::memUsage(*busConfigs[i]); // if we have unequal LED count use the largest
} else } else
mem += BusManager::memUsage(*busConfigs[i]); // includes global buffer mem += BusManager::memUsage(*busConfigs[i]); // includes global buffer
if (mem <= MAX_LED_MEMORY) BusManager::add(*busConfigs[i]); if (mem <= MAX_LED_MEMORY) BusManager::add(*busConfigs[i]);

View File

@ -8,7 +8,7 @@
*/ */
// version code in format yymmddb (b = daily build) // version code in format yymmddb (b = daily build)
#define VERSION 2406290 #define VERSION 2407070
//uncomment this if you have a "my_config.h" file you'd like to use //uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG //#define WLED_USE_MY_CONFIG