From ee7ec20f29e1dd7fc3e525314b66101634b28974 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Kristan?= Date: Tue, 21 Jan 2025 17:50:36 +0100 Subject: [PATCH 01/10] Convert BusManager class to namespace - use unique_ptr/make_unique for busses --- wled00/bus_manager.cpp | 67 +++++++++-------------- wled00/bus_manager.h | 120 ++++++++++++++++++++++------------------- wled00/cfg.cpp | 3 +- wled00/set.cpp | 3 +- wled00/wled.h | 11 ++-- wled00/xml.cpp | 2 +- 6 files changed, 99 insertions(+), 107 deletions(-) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 3abf61412b..669111bfad 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -63,6 +63,8 @@ uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, const #define W(c) (byte((c) >> 24)) +static ColorOrderMap _colorOrderMap = {}; + bool ColorOrderMap::add(uint16_t start, uint16_t len, uint8_t colorOrder) { if (count() >= WLED_MAX_COLOR_ORDER_MAPPINGS || len == 0 || (colorOrder & 0x0F) > COL_ORDER_MAX) return false; // upper nibble contains W swap information _mappings.push_back({start,len,colorOrder}); @@ -72,10 +74,8 @@ bool ColorOrderMap::add(uint16_t start, uint16_t len, uint8_t colorOrder) { uint8_t IRAM_ATTR ColorOrderMap::getPixelColorOrder(uint16_t pix, uint8_t defaultColorOrder) const { // upper nibble contains W swap information // when ColorOrderMap's upper nibble contains value >0 then swap information is used from it, otherwise global swap is used - for (unsigned i = 0; i < count(); i++) { - if (pix >= _mappings[i].start && pix < (_mappings[i].start + _mappings[i].len)) { - return _mappings[i].colorOrder | ((_mappings[i].colorOrder >> 4) ? 0 : (defaultColorOrder & 0xF0)); - } + for (const auto& map : _mappings) { + if (pix >= map.start && pix < (map.start + map.len)) return map.colorOrder | ((map.colorOrder >> 4) ? 0 : (defaultColorOrder & 0xF0)); } return defaultColorOrder; } @@ -124,13 +124,12 @@ uint8_t *Bus::allocateData(size_t size) { } -BusDigital::BusDigital(const BusConfig &bc, uint8_t nr, const ColorOrderMap &com) +BusDigital::BusDigital(const BusConfig &bc, uint8_t nr) : Bus(bc.type, bc.start, bc.autoWhite, bc.count, bc.reversed, (bc.refreshReq || bc.type == TYPE_TM1814)) , _skip(bc.skipAmount) //sacrificial pixels , _colorOrder(bc.colorOrder) , _milliAmpsPerLed(bc.milliAmpsPerLed) , _milliAmpsMax(bc.milliAmpsMax) -, _colorOrderMap(com) { if (!isDigital(bc.type) || !bc.count) return; if (!PinManager::allocatePin(bc.pins[0], true, PinOwner::BusDigital)) return; @@ -819,13 +818,17 @@ uint32_t BusManager::memUsage(unsigned maxChannels, unsigned maxCount, unsigned int BusManager::add(const BusConfig &bc) { if (getNumBusses() - getNumVirtualBusses() >= WLED_MAX_BUSSES) return -1; if (Bus::isVirtual(bc.type)) { - busses[numBusses] = new BusNetwork(bc); + busses.push_back(make_unique(bc)); + //busses.push_back(new BusNetwork(bc)); } else if (Bus::isDigital(bc.type)) { - busses[numBusses] = new BusDigital(bc, numBusses, colorOrderMap); + busses.push_back(make_unique(bc, numDigital)); + //busses.push_back(new BusDigital(bc, numDigital)); } else if (Bus::isOnOff(bc.type)) { - busses[numBusses] = new BusOnOff(bc); + busses.push_back(make_unique(bc)); + //busses.push_back(new BusOnOff(bc)); } else { - busses[numBusses] = new BusPwm(bc); + busses.push_back(make_unique(bc)); + //busses.push_back(new BusPwm(bc)); } return numBusses++; } @@ -865,9 +868,8 @@ void BusManager::removeAll() { DEBUG_PRINTLN(F("Removing all.")); //prevents crashes due to deleting busses while in use. while (!canAllShow()) yield(); - for (unsigned i = 0; i < numBusses; i++) delete busses[i]; - numBusses = 0; - _parallelOutputs = 1; + //for (auto &bus : busses) delete bus; // needed when not using std::unique_ptr C++ >11 + busses.clear(); PolyBus::setParallelI2S1Output(false); } @@ -914,8 +916,8 @@ void BusManager::on() { uint8_t pins[2] = {255,255}; if (busses[i]->isDigital() && busses[i]->getPins(pins)) { if (pins[0] == LED_BUILTIN || pins[1] == LED_BUILTIN) { - BusDigital *bus = static_cast(busses[i]); - bus->begin(); + BusDigital &b = static_cast(*bus); + b.begin(); break; } } @@ -943,16 +945,10 @@ void BusManager::off() { } void BusManager::show() { - _milliAmpsUsed = 0; - for (unsigned i = 0; i < numBusses; i++) { - busses[i]->show(); - _milliAmpsUsed += busses[i]->getUsedCurrent(); - } -} - -void BusManager::setStatusPixel(uint32_t c) { - for (unsigned i = 0; i < numBusses; i++) { - busses[i]->setStatusPixel(c); + _gMilliAmpsUsed = 0; + for (auto &bus : busses) { + bus->show(); + _gMilliAmpsUsed += bus->getUsedCurrent(); } } @@ -995,17 +991,8 @@ bool BusManager::canAllShow() { return true; } -Bus* BusManager::getBus(uint8_t busNr) { - if (busNr >= numBusses) return nullptr; - return busses[busNr]; -} +ColorOrderMap& BusManager::getColorOrderMap() { return _colorOrderMap; } -//semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit()) -uint16_t BusManager::getTotalLength() { - unsigned len = 0; - for (unsigned i=0; igetLength(); - return len; -} bool PolyBus::useParallelI2S = false; @@ -1016,9 +1003,7 @@ uint8_t Bus::_gAWM = 255; uint16_t BusDigital::_milliAmpsTotal = 0; -uint8_t BusManager::numBusses = 0; -Bus* BusManager::busses[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES]; -ColorOrderMap BusManager::colorOrderMap = {}; -uint16_t BusManager::_milliAmpsUsed = 0; -uint16_t BusManager::_milliAmpsMax = ABL_MILLIAMPS_DEFAULT; -uint8_t BusManager::_parallelOutputs = 1; +std::vector> BusManager::busses; +//std::vector BusManager::busses; +uint16_t BusManager::_gMilliAmpsUsed = 0; +uint16_t BusManager::_gMilliAmpsMax = ABL_MILLIAMPS_DEFAULT; diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index 9aed013089..16a708d47c 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -8,6 +8,20 @@ #include "const.h" #include "pin_manager.h" #include +#include +#include + +#if __cplusplus >= 201402L +using std::make_unique; +#else +// Really simple C++11 shim for non-array case; implementation from cppreference.com +template +std::unique_ptr +make_unique(Args&&... args) +{ + return std::unique_ptr(new T(std::forward(args)...)); +} +#endif //colors.cpp uint16_t approximateKelvinFromRGB(uint32_t rgb); @@ -198,7 +212,7 @@ class Bus { class BusDigital : public Bus { public: - BusDigital(const BusConfig &bc, uint8_t nr, const ColorOrderMap &com); + BusDigital(const BusConfig &bc, uint8_t nr); ~BusDigital() { cleanup(); } void show() override; @@ -229,7 +243,6 @@ class BusDigital : public Bus { uint8_t _milliAmpsPerLed; uint16_t _milliAmpsMax; void * _busPtr; - const ColorOrderMap &_colorOrderMap; static uint16_t _milliAmpsTotal; // is overwitten/recalculated on each show() @@ -374,61 +387,58 @@ struct BusConfig { #endif #endif -class BusManager { - public: - BusManager() {}; - - //utility to get the approx. memory usage of a given BusConfig - static uint32_t memUsage(const BusConfig &bc); - static uint32_t memUsage(unsigned channels, unsigned count, unsigned buses = 1); - static uint16_t currentMilliamps() { return _milliAmpsUsed + MA_FOR_ESP; } - static uint16_t ablMilliampsMax() { return _milliAmpsMax; } - - static int add(const BusConfig &bc); - static void useParallelOutput(); // workaround for inaccessible PolyBus - - //do not call this method from system context (network callback) - static void removeAll(); - - static void on(); - static void off(); - - static void show(); - static bool canAllShow(); - static void setStatusPixel(uint32_t c); - [[gnu::hot]] static void setPixelColor(unsigned pix, uint32_t c); - static void setBrightness(uint8_t b); - // for setSegmentCCT(), cct can only be in [-1,255] range; allowWBCorrection will convert it to K - // WARNING: setSegmentCCT() is a misleading name!!! much better would be setGlobalCCT() or just setCCT() - static void setSegmentCCT(int16_t cct, bool allowWBCorrection = false); - static inline void setMilliampsMax(uint16_t max) { _milliAmpsMax = max;} - [[gnu::hot]] static uint32_t getPixelColor(unsigned pix); - static inline int16_t getSegmentCCT() { return Bus::getCCT(); } +namespace BusManager { - static Bus* getBus(uint8_t busNr); + extern std::vector> busses; + //extern std::vector busses; + extern uint16_t _gMilliAmpsUsed; + extern uint16_t _gMilliAmpsMax; - //semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit()) - static uint16_t getTotalLength(); - static inline uint8_t getNumBusses() { return numBusses; } - static String getLEDTypesJSONString(); - - static inline ColorOrderMap& getColorOrderMap() { return colorOrderMap; } + #ifdef ESP32_DATA_IDLE_HIGH + void esp32RMTInvertIdle() ; + #endif + inline uint8_t getNumVirtualBusses() { + int j = 0; + for (const auto &bus : busses) j += bus->isVirtual(); + return j; + } - private: - static uint8_t numBusses; - static Bus* busses[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES]; - static ColorOrderMap colorOrderMap; - static uint16_t _milliAmpsUsed; - static uint16_t _milliAmpsMax; - static uint8_t _parallelOutputs; - - #ifdef ESP32_DATA_IDLE_HIGH - static void esp32RMTInvertIdle() ; - #endif - static uint8_t getNumVirtualBusses() { - int j = 0; - for (int i=0; iisVirtual()) j++; - return j; - } + unsigned memUsage(); + inline uint16_t currentMilliamps() { return _gMilliAmpsUsed + MA_FOR_ESP; } + //inline uint16_t ablMilliampsMax() { unsigned sum = 0; for (auto &bus : busses) sum += bus->getMaxCurrent(); return sum; } + inline uint16_t ablMilliampsMax() { return _gMilliAmpsMax; } // used for compatibility reasons (and enabling virtual global ABL) + inline void setMilliampsMax(uint16_t max) { _gMilliAmpsMax = max;} + + void useParallelOutput(); // workaround for inaccessible PolyBus + bool hasParallelOutput(); // workaround for inaccessible PolyBus + + //do not call this method from system context (network callback) + void removeAll(); + int add(const BusConfig &bc); + + void on(); + void off(); + + [[gnu::hot]] void setPixelColor(unsigned pix, uint32_t c); + [[gnu::hot]] uint32_t getPixelColor(unsigned pix); + void show(); + bool canAllShow(); + inline void setStatusPixel(uint32_t c) { for (auto &bus : busses) bus->setStatusPixel(c);} + inline void setBrightness(uint8_t b) { for (auto &bus : busses) bus->setBrightness(b); } + // for setSegmentCCT(), cct can only be in [-1,255] range; allowWBCorrection will convert it to K + // WARNING: setSegmentCCT() is a misleading name!!! much better would be setGlobalCCT() or just setCCT() + void setSegmentCCT(int16_t cct, bool allowWBCorrection = false); + inline int16_t getSegmentCCT() { return Bus::getCCT(); } + inline Bus& getBus(uint8_t busNr) { return *busses[std::min((size_t)busNr, busses.size()-1)]; } + inline uint8_t getNumBusses() { return busses.size(); } + + //semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit()) + inline uint16_t getTotalLength(bool onlyPhysical = false) { + unsigned len = 0; + for (const auto &bus : busses) if (!(bus->isVirtual() && onlyPhysical)) len += bus->getLength(); + return len; + } + String getLEDTypesJSONString(); + ColorOrderMap& getColorOrderMap(); }; #endif diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 00cfc60d7e..352b6c7732 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -876,8 +876,7 @@ void serializeConfig() { const ColorOrderMap& com = BusManager::getColorOrderMap(); for (size_t s = 0; s < com.count(); s++) { const ColorOrderMapEntry *entry = com.get(s); - if (!entry) break; - + if (!entry || !entry->len) break; JsonObject co = hw_com.createNestedObject(); co["start"] = entry->start; co["len"] = entry->len; diff --git a/wled00/set.cpp b/wled00/set.cpp index c0977f262c..f7be16e0fd 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -208,8 +208,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) type |= request->hasArg(rf) << 7; // off refresh override // actual finalization is done in WLED::loop() (removing old busses and adding new) // this may happen even before this loop is finished so we do "doInitBusses" after the loop - if (busConfigs[s] != nullptr) delete busConfigs[s]; - busConfigs[s] = new(std::nothrow) BusConfig(type, pins, start, length, colorOrder | (channelSwap<<4), request->hasArg(cv), skip, awmode, freq, useGlobalLedBuffer, maPerLed, maMax); + busConfigs.emplace_back(type, pins, start, length, colorOrder | (channelSwap<<4), request->hasArg(cv), skip, awmode, freq, useGlobalLedBuffer, maPerLed, maMax); busesChanged = true; } //doInitBusses = busesChanged; // we will do that below to ensure all input data is processed diff --git a/wled00/wled.h b/wled00/wled.h index a18199446c..80d3198562 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -891,12 +891,11 @@ WLED_GLOBAL ESPAsyncE131 ddp _INIT_N(((handleE131Packet))); WLED_GLOBAL bool e131NewData _INIT(false); // led fx library object -WLED_GLOBAL BusManager busses _INIT(BusManager()); -WLED_GLOBAL WS2812FX strip _INIT(WS2812FX()); -WLED_GLOBAL BusConfig* busConfigs[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES] _INIT({nullptr}); //temporary, to remember values from network callback until after -WLED_GLOBAL bool doInitBusses _INIT(false); -WLED_GLOBAL int8_t loadLedmap _INIT(-1); -WLED_GLOBAL uint8_t currentLedmap _INIT(0); +WLED_GLOBAL WS2812FX strip _INIT(WS2812FX()); +WLED_GLOBAL std::vector busConfigs; //temporary, to remember values from network callback until after +WLED_GLOBAL bool doInitBusses _INIT(false); +WLED_GLOBAL int8_t loadLedmap _INIT(-1); +WLED_GLOBAL uint8_t currentLedmap _INIT(0); #ifndef ESP8266 WLED_GLOBAL char *ledmapNames[WLED_MAX_LEDMAPS-1] _INIT_N(({nullptr})); #endif diff --git a/wled00/xml.cpp b/wled00/xml.cpp index 78d2d7d567..b53958c4fd 100644 --- a/wled00/xml.cpp +++ b/wled00/xml.cpp @@ -357,7 +357,7 @@ void getSettingsJS(byte subPage, Print& settingsScript) const ColorOrderMap& com = BusManager::getColorOrderMap(); for (int s = 0; s < com.count(); s++) { const ColorOrderMapEntry* entry = com.get(s); - if (entry == nullptr) break; + if (!entry || !entry->len) break; settingsScript.printf_P(PSTR("addCOM(%d,%d,%d);"), entry->start, entry->len, entry->colorOrder); } From bf69d37cbe6bc4430093d275a6200e3780cb941e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Kristan?= Date: Tue, 21 Jan 2025 20:14:20 +0100 Subject: [PATCH 02/10] Revert getBus() changes --- wled00/FX_fcn.cpp | 31 +++++++++++++++---------------- wled00/bus_manager.cpp | 15 ++++++++++++--- wled00/bus_manager.h | 2 +- wled00/cfg.cpp | 35 +++++++++++++++++++++++------------ wled00/xml.cpp | 6 +++--- 5 files changed, 54 insertions(+), 35 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index cf37a46c2f..0313814203 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1117,12 +1117,9 @@ void Segment::refreshLightCapabilities() { } for (unsigned b = 0; b < BusManager::getNumBusses(); b++) { - Bus *bus = BusManager::getBus(b); - if (bus == nullptr || bus->getLength()==0) break; - if (!bus->isOk()) continue; - if (bus->getStart() >= segStopIdx) continue; - if (bus->getStart() + bus->getLength() <= segStartIdx) continue; - + const Bus *bus = BusManager::getBus(b); + if (!bus || !bus->isOk()) break; + if (bus->getStart() >= segStopIdx || bus->getStart() + bus->getLength() <= segStartIdx) continue; if (bus->hasRGB() || (strip.cctFromRgb && bus->hasCCT())) capabilities |= SEG_CAPABILITY_RGB; if (!strip.cctFromRgb && bus->hasCCT()) capabilities |= SEG_CAPABILITY_CCT; if (strip.correctWB && (bus->hasRGB() || bus->hasCCT())) capabilities |= SEG_CAPABILITY_CCT; //white balance correction (CCT slider) @@ -1397,8 +1394,7 @@ void WS2812FX::finalizeInit() { _length = 0; for (int i=0; igetStart() + bus->getLength() > MAX_LEDS) break; + if (!bus || !bus->isOk() || bus->getStart() + bus->getLength() > MAX_LEDS) break; //RGBW mode is enabled if at least one of the strips is RGBW _hasWhiteChannel |= bus->hasWhite(); //refresh is required to remain off if at least one of the strips requires the refresh. @@ -1408,6 +1404,7 @@ void WS2812FX::finalizeInit() { // This must be done after all buses have been created, as some kinds (parallel I2S) interact bus->begin(); + bus->setBrightness(bri); } Segment::maxWidth = _length; @@ -1691,8 +1688,8 @@ uint16_t WS2812FX::getLengthPhysical() const { //not influenced by auto-white mode, also true if white slider does not affect output white channel bool WS2812FX::hasRGBWBus() const { for (size_t b = 0; b < BusManager::getNumBusses(); b++) { - Bus *bus = BusManager::getBus(b); - if (bus == nullptr || bus->getLength()==0) break; + const Bus *bus = BusManager::getBus(b); + if (!bus || !bus->isOk()) break; if (bus->hasRGB() && bus->hasWhite()) return true; } return false; @@ -1701,8 +1698,8 @@ bool WS2812FX::hasRGBWBus() const { bool WS2812FX::hasCCTBus() const { if (cctFromRgb && !correctWB) return false; for (size_t b = 0; b < BusManager::getNumBusses(); b++) { - Bus *bus = BusManager::getBus(b); - if (bus == nullptr || bus->getLength()==0) break; + const Bus *bus = BusManager::getBus(b); + if (!bus || !bus->isOk()) break; if (bus->hasCCT()) return true; } return false; @@ -1755,10 +1752,11 @@ void WS2812FX::makeAutoSegments(bool forceReset) { #endif for (size_t i = s; i < BusManager::getNumBusses(); i++) { - Bus* b = BusManager::getBus(i); + const Bus *bus = BusManager::getBus(i); + if (!bus || !bus->isOk()) break; - segStarts[s] = b->getStart(); - segStops[s] = segStarts[s] + b->getLength(); + segStarts[s] = bus->getStart(); + segStops[s] = segStarts[s] + bus->getLength(); #ifndef WLED_DISABLE_2D if (isMatrix && segStops[s] <= Segment::maxWidth*Segment::maxHeight) continue; // ignore buses comprising matrix @@ -1848,7 +1846,8 @@ bool WS2812FX::checkSegmentAlignment() const { bool aligned = false; for (const segment &seg : _segments) { for (unsigned b = 0; bisOk()) break; if (seg.start == bus->getStart() && seg.stop == bus->getStart() + bus->getLength()) aligned = true; } if (seg.start == 0 && seg.stop == _length) aligned = true; diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 669111bfad..6807f43813 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -153,8 +153,17 @@ BusDigital::BusDigital(const BusConfig &bc, uint8_t nr) uint16_t lenToCreate = bc.count; if (bc.type == TYPE_WS2812_1CH_X3) lenToCreate = NUM_ICS_WS2812_1CH_3X(bc.count); // only needs a third of "RGB" LEDs for NeoPixelBus _busPtr = PolyBus::create(_iType, _pins, lenToCreate + _skip, nr); - _valid = (_busPtr != nullptr); - DEBUG_PRINTF_P(PSTR("%successfully inited strip %u (len %u) with type %u and pins %u,%u (itype %u). mA=%d/%d\n"), _valid?"S":"Uns", nr, bc.count, bc.type, _pins[0], is2Pin(bc.type)?_pins[1]:255, _iType, _milliAmpsPerLed, _milliAmpsMax); + _valid = (_busPtr != nullptr) && bc.count > 0; + DEBUG_PRINTF_P(PSTR("Bus: %successfully inited #%u (len:%u, type:%u (RGB:%d, W:%d, CCT:%d), pins:%u,%u [itype:%u] mA=%d/%d)\n"), + _valid?"S":"Uns", + (int)nr, + (int)bc.count, + (int)bc.type, + (int)_hasRgb, (int)_hasWhite, (int)_hasCCT, + (unsigned)_pins[0], is2Pin(bc.type)?(unsigned)_pins[1]:255U, + (unsigned)_iType, + (int)_milliAmpsPerLed, (int)_milliAmpsMax + ); } //DISCLAIMER @@ -734,7 +743,7 @@ BusNetwork::BusNetwork(const BusConfig &bc) _hasCCT = false; _UDPchannels = _hasWhite + 3; _client = IPAddress(bc.pins[0],bc.pins[1],bc.pins[2],bc.pins[3]); - _valid = (allocateData(_len * _UDPchannels) != nullptr); + _valid = (allocateData(_len * _UDPchannels) != nullptr) && bc.count > 0; DEBUG_PRINTF_P(PSTR("%successfully inited virtual strip with type %u and IP %u.%u.%u.%u\n"), _valid?"S":"Uns", bc.type, bc.pins[0], bc.pins[1], bc.pins[2], bc.pins[3]); } diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index 16a708d47c..8f4906eae8 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -429,7 +429,7 @@ namespace BusManager { // WARNING: setSegmentCCT() is a misleading name!!! much better would be setGlobalCCT() or just setCCT() void setSegmentCCT(int16_t cct, bool allowWBCorrection = false); inline int16_t getSegmentCCT() { return Bus::getCCT(); } - inline Bus& getBus(uint8_t busNr) { return *busses[std::min((size_t)busNr, busses.size()-1)]; } + inline Bus* getBus(size_t busNr) { return busNr < busses.size() ? busses[busNr].get() : nullptr; } inline uint8_t getNumBusses() { return busses.size(); } //semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit()) diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 352b6c7732..0d3165e0ea 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -239,7 +239,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { DEBUG_PRINTF_P(PSTR("LED buffer size: %uB\n"), mem); DEBUG_PRINTF_P(PSTR("Heap after buses: %d\n"), ESP.getFreeHeap()); } - if (hw_led["rev"]) BusManager::getBus(0)->setReversed(true); //set 0.11 global reversed setting for first bus + if (hw_led["rev"] && BusManager::getNumBusses()) BusManager::getBus(0)->setReversed(true); //set 0.11 global reversed setting for first bus // read color order map configuration JsonArray hw_com = hw[F("com")]; @@ -852,24 +852,35 @@ void serializeConfig() { JsonArray hw_led_ins = hw_led.createNestedArray("ins"); for (size_t s = 0; s < BusManager::getNumBusses(); s++) { - Bus *bus = BusManager::getBus(s); - if (!bus || bus->getLength()==0) break; + DEBUG_PRINTF_P(PSTR("Cfg: Saving bus #%u\n"), s); + const Bus *bus = BusManager::getBus(s); + if (!bus || !bus->isOk()) break; + DEBUG_PRINTF_P(PSTR(" (%d-%d, type:%d, CO:%d, rev:%d, skip:%d, AW:%d kHz:%d, mA:%d/%d)\n"), + (int)bus->getStart(), (int)(bus->getStart()+bus->getLength()), + (int)(bus->getType() & 0x7F), + (int)bus->getColorOrder(), + (int)bus->isReversed(), + (int)bus->skippedLeds(), + (int)bus->getAutoWhiteMode(), + (int)bus->getFrequency(), + (int)bus->getLEDCurrent(), (int)bus->getMaxCurrent() + ); JsonObject ins = hw_led_ins.createNestedObject(); ins["start"] = bus->getStart(); - ins["len"] = bus->getLength(); + ins["len"] = bus->getLength(); JsonArray ins_pin = ins.createNestedArray("pin"); uint8_t pins[5]; uint8_t nPins = bus->getPins(pins); for (int i = 0; i < nPins; i++) ins_pin.add(pins[i]); - ins[F("order")] = bus->getColorOrder(); - ins["rev"] = bus->isReversed(); - ins[F("skip")] = bus->skippedLeds(); - ins["type"] = bus->getType() & 0x7F; - ins["ref"] = bus->isOffRefreshRequired(); - ins[F("rgbwm")] = bus->getAutoWhiteMode(); - ins[F("freq")] = bus->getFrequency(); + ins[F("order")] = bus->getColorOrder(); + ins["rev"] = bus->isReversed(); + ins[F("skip")] = bus->skippedLeds(); + ins["type"] = bus->getType() & 0x7F; + ins["ref"] = bus->isOffRefreshRequired(); + ins[F("rgbwm")] = bus->getAutoWhiteMode(); + ins[F("freq")] = bus->getFrequency(); ins[F("maxpwr")] = bus->getMaxCurrent(); - ins[F("ledma")] = bus->getLEDCurrent(); + ins[F("ledma")] = bus->getLEDCurrent(); } JsonArray hw_com = hw.createNestedArray(F("com")); diff --git a/wled00/xml.cpp b/wled00/xml.cpp index b53958c4fd..a31b130724 100644 --- a/wled00/xml.cpp +++ b/wled00/xml.cpp @@ -292,8 +292,8 @@ void getSettingsJS(byte subPage, Print& settingsScript) unsigned sumMa = 0; for (int s = 0; s < BusManager::getNumBusses(); s++) { - Bus* bus = BusManager::getBus(s); - if (bus == nullptr) continue; + const Bus *bus = BusManager::getBus(s); + if (!bus || !bus->isOk()) break; // should not happen but for safety int offset = s < 10 ? 48 : 55; char lp[4] = "L0"; lp[2] = offset+s; lp[3] = 0; //ascii 0-9 //strip data pin char lc[4] = "LC"; lc[2] = offset+s; lc[3] = 0; //strip length @@ -312,7 +312,7 @@ void getSettingsJS(byte subPage, Print& settingsScript) uint8_t pins[5]; int nPins = bus->getPins(pins); for (int i = 0; i < nPins; i++) { - lp[1] = offset+i; + lp[1] = '0'+i; if (PinManager::isPinOk(pins[i]) || bus->isVirtual()) printSetFormValue(settingsScript,lp,pins[i]); } printSetFormValue(settingsScript,lc,bus->getLength()); From 70042db2dea8ff7a1b4b00430b250a60c30f3ef0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Kristan?= Date: Wed, 22 Jan 2025 20:33:56 +0100 Subject: [PATCH 03/10] Allow "unlimited" virtual buses - added config upload options - number of buses it limited to 36 (0-9+A-Z identifiers) - WRNING web server may not support that many variables --- wled00/cfg.cpp | 22 +++------- wled00/const.h | 24 ++++++----- wled00/data/settings_leds.htm | 76 ++++++++++++++++++++++++----------- wled00/set.cpp | 4 +- wled00/xml.cpp | 2 +- 5 files changed, 75 insertions(+), 53 deletions(-) diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 0d3165e0ea..3e8782a995 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -192,7 +192,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { #endif for (JsonObject elm : ins) { - if (s >= WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES) break; + if (s >= WLED_MAX_BUSSES) break; uint8_t pins[5] = {255, 255, 255, 255, 255}; JsonArray pinArr = elm["pin"]; if (pinArr.size() == 0) continue; @@ -220,21 +220,11 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { maMax = 0; } ledType |= refresh << 7; // hack bit 7 to indicate strip requires off refresh - if (fromFS) { - BusConfig bc = BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax); - if (useParallel && s < 8) { - // if for some unexplained reason the above pre-calculation was wrong, update - unsigned memT = 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 - 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() - } else { - if (busConfigs[s] != nullptr) delete busConfigs[s]; - busConfigs[s] = new BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax); - doInitBusses = true; // finalization done in beginStrip() - } - s++; + + //busConfigs.push_back(std::move(BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax))); + busConfigs.emplace_back(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax); + doInitBusses = true; // finalization done in beginStrip() + if (!Bus::isVirtual(ledType)) s++; // have as many virtual buses as you want } DEBUG_PRINTF_P(PSTR("LED buffer size: %uB\n"), mem); DEBUG_PRINTF_P(PSTR("Heap after buses: %d\n"), ESP.getFreeHeap()); diff --git a/wled00/const.h b/wled00/const.h index 1ebcb9397d..0f0fc4f99c 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -49,31 +49,31 @@ #define WLED_MAX_DIGITAL_CHANNELS 3 #define WLED_MAX_ANALOG_CHANNELS 5 #define WLED_MAX_BUSSES 4 // will allow 3 digital & 1 analog RGB - #define WLED_MIN_VIRTUAL_BUSSES 2 + #define WLED_MIN_VIRTUAL_BUSSES 3 // no longer used for bus creation but used to distinguish S2/S3 in UI #else #define WLED_MAX_ANALOG_CHANNELS (LEDC_CHANNEL_MAX*LEDC_SPEED_MODE_MAX) #if defined(CONFIG_IDF_TARGET_ESP32C3) // 2 RMT, 6 LEDC, only has 1 I2S but NPB does not support it ATM #define WLED_MAX_BUSSES 6 // will allow 2 digital & 2 analog RGB or 6 PWM white #define WLED_MAX_DIGITAL_CHANNELS 2 //#define WLED_MAX_ANALOG_CHANNELS 6 - #define WLED_MIN_VIRTUAL_BUSSES 3 + #define WLED_MIN_VIRTUAL_BUSSES 4 // no longer used for bus creation but used to distinguish S2/S3 in UI #elif defined(CONFIG_IDF_TARGET_ESP32S2) // 4 RMT, 8 LEDC, only has 1 I2S bus, supported in NPB // the 5th bus (I2S) will prevent Audioreactive usermod from functioning (it is last used though) #define WLED_MAX_BUSSES 7 // will allow 5 digital & 2 analog RGB #define WLED_MAX_DIGITAL_CHANNELS 5 //#define WLED_MAX_ANALOG_CHANNELS 8 - #define WLED_MIN_VIRTUAL_BUSSES 3 - #elif defined(CONFIG_IDF_TARGET_ESP32S3) // 4 RMT, 8 LEDC, has 2 I2S but NPB does not support them ATM - #define WLED_MAX_BUSSES 6 // will allow 4 digital & 2 analog RGB - #define WLED_MAX_DIGITAL_CHANNELS 4 + #define WLED_MIN_VIRTUAL_BUSSES 4 // no longer used for bus creation but used to distinguish S2/S3 in UI + #elif defined(CONFIG_IDF_TARGET_ESP32S3) // 4 RMT, 8 LEDC, has 2 I2S but NPB supports parallel x8 LCD on I2S1 + #define WLED_MAX_BUSSES 14 // will allow 12 digital & 2 analog RGB + #define WLED_MAX_DIGITAL_CHANNELS 12 // x4 RMT + x8 I2S-LCD //#define WLED_MAX_ANALOG_CHANNELS 8 - #define WLED_MIN_VIRTUAL_BUSSES 4 + #define WLED_MIN_VIRTUAL_BUSSES 6 // no longer used for bus creation but used to distinguish S2/S3 in UI #else // the last digital bus (I2S0) will prevent Audioreactive usermod from functioning #define WLED_MAX_BUSSES 20 // will allow 17 digital & 3 analog RGB #define WLED_MAX_DIGITAL_CHANNELS 17 //#define WLED_MAX_ANALOG_CHANNELS 16 - #define WLED_MIN_VIRTUAL_BUSSES 4 + #define WLED_MIN_VIRTUAL_BUSSES 6 // no longer used for bus creation but used to distinguish S2/S3 in UI #endif #endif #else @@ -87,7 +87,7 @@ #ifndef WLED_MAX_DIGITAL_CHANNELS #error You must also define WLED_MAX_DIGITAL_CHANNELS. #endif - #define WLED_MIN_VIRTUAL_BUSSES (5-WLED_MAX_BUSSES) + #define WLED_MIN_VIRTUAL_BUSSES 3 #else #if WLED_MAX_BUSSES > 20 #error Maximum number of buses is 20. @@ -98,7 +98,11 @@ #ifndef WLED_MAX_DIGITAL_CHANNELS #error You must also define WLED_MAX_DIGITAL_CHANNELS. #endif - #define WLED_MIN_VIRTUAL_BUSSES (20-WLED_MAX_BUSSES) + #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) + #define WLED_MIN_VIRTUAL_BUSSES 4 + #else + #define WLED_MIN_VIRTUAL_BUSSES 6 + #endif #endif #endif diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index 02ebb6ed0b..87e76e9b63 100644 --- a/wled00/data/settings_leds.htm +++ b/wled00/data/settings_leds.htm @@ -42,10 +42,10 @@ if (loc) d.Sf.action = getURL('/settings/leds'); } function bLimits(b,v,p,m,l,o=5,d=2,a=6) { - oMaxB = maxB = b; // maxB - max buses (can be changed if using ESP32 parallel I2S) - maxD = d; // maxD - max digital channels (can be changed if using ESP32 parallel I2S) - maxA = a; // maxA - max analog channels - maxV = v; // maxV - min virtual buses + oMaxB = maxB = b; // maxB - max buses (can be changed if using ESP32 parallel I2S): 20 - ESP32, 14 - S3/S2, 6 - C3, 4 - 8266 + maxD = d; // maxD - max digital channels (can be changed if using ESP32 parallel I2S): 17 - ESP32, 12 - S3/S2, 2 - C3, 3 - 8266 + maxA = a; // maxA - max analog channels: 16 - ESP32, 8 - S3/S2, 6 - C3, 5 - 8266 + maxV = v; // maxV - min virtual buses: 6 - ESP32/S3, 4 - S2/C3, 3 - ESP8266 (only used to distinguish S2/S3) maxPB = p; // maxPB - max LEDs per bus maxM = m; // maxM - max LED memory maxL = l; // maxL - max LEDs (will serve to determine ESP >1664 == ESP32) @@ -350,6 +350,18 @@ else LC.style.color = d.ro_gpio.some((e)=>e==parseInt(LC.value)) ? "orange" : "#fff"; } }); + const S2 = (oMaxB == 14) && (maxV == 4); + const S3 = (oMaxB == 14) && (maxV == 6); + if (oMaxB == 19 || S2 || S3) { // TODO: crude ESP32 & S2/S3 detection + if (maxLC > 300 || dC <= 2) { + d.Sf["PR"].checked = false; + gId("prl").classList.add("hide"); + } else + gId("prl").classList.remove("hide"); + // S2 supports mono I2S as well as parallel so we need to take that into account; S3 only supports parallel + maxD = (S2 || S3 ? 4 : 8) + (d.Sf["PR"].checked ? 8 : S2); // TODO: use bLimits() : 4/8RMT + (x1/x8 parallel) I2S1 + maxB = oMaxB - (d.Sf["PR"].checked ? 0 : 7 + S3); // S2 (maxV==4) does support mono I2S + } // distribute ABL current if not using PPL enPPL(sDI); @@ -409,8 +421,8 @@ if (isVir(t)) virtB++; }); - if ((n==1 && i>=maxB+maxV) || (n==-1 && i==0)) return; - var s = String.fromCharCode((i<10?48:55)+i); + if ((n==1 && i>=36) || (n==-1 && i==0)) return; // used to be i>=maxB+maxV when virtual buses were limited (now :"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") + var s = chrID(i); if (n==1) { // npm run build has trouble minimizing spaces inside string @@ -484,7 +496,7 @@ o[i].querySelector("[name^=LT]").disabled = false; } - gId("+").style.display = (i0) ? "inline":"none"; if (!init) { @@ -603,22 +615,32 @@ function receivedText(e) { let lines = e.target.result; - var c = JSON.parse(lines); + let c = JSON.parse(lines); if (c.hw) { if (c.hw.led) { - for (var i=0; i<10; i++) addLEDs(-1); - var l = c.hw.led; + // remove all existing outputs + for (const i=0; i<36; i++) addLEDs(-1); // was i{ addLEDs(1); for (var j=0; j>4) & 0x0F; + d.getElementsByName("SP"+i)[0].value = v.freq; + d.getElementsByName("LA"+i)[0].value = v.ledma; + d.getElementsByName("MA"+i)[0].value = v.maxpwr; }); + d.getElementsByName("PR")[0].checked = l.prl | 0; + d.getElementsByName("LD")[0].checked = l.ld; + d.getElementsByName("MA")[0].value = l.maxpwr; + d.getElementsByName("ABL")[0].checked = l.maxpwr > 0; } if(c.hw.com) { resetCOM(); @@ -626,22 +648,28 @@ addCOM(e.start, e.len, e.order); }); } - if (c.hw.btn) { - var b = c.hw.btn; + let b = c.hw.btn; + if (b) { if (Array.isArray(b.ins)) gId("btns").innerHTML = ""; b.ins.forEach((v,i,a)=>{ addBtn(i,v.pin[0],v.type); }); d.getElementsByName("TT")[0].value = b.tt; } - if (c.hw.ir) { - d.getElementsByName("IR")[0].value = c.hw.ir.pin; - d.getElementsByName("IT")[0].value = c.hw.ir.type; + let ir = c.hw.ir; + if (ir) { + d.getElementsByName("IR")[0].value = ir.pin; + d.getElementsByName("IT")[0].value = ir.type; + } + let rl = c.hw.relay; + if (rl) { + d.getElementsByName("RL")[0].value = rl.pin; + d.getElementsByName("RM")[0].checked = rl.rev; + d.getElementsByName("RO")[0].checked = rl.odrain; } - if (c.hw.relay) { - d.getElementsByName("RL")[0].value = c.hw.relay.pin; - d.getElementsByName("RM")[0].checked = c.hw.relay.rev; - d.getElementsByName("RO")[0].checked = c.hw.relay.odrain; + let li = c.light; + if (li) { + d.getElementsByName("MS")[0].checked = li.aseg; } UI(); } diff --git a/wled00/set.cpp b/wled00/set.cpp index f7be16e0fd..193574c74d 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -141,7 +141,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) useGlobalLedBuffer = request->hasArg(F("LD")); bool busesChanged = false; - for (int s = 0; s < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; s++) { + for (int s = 0; s < 36; s++) { // theoretical limit is 36 : "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" int offset = s < 10 ? 48 : 55; char lp[4] = "L0"; lp[2] = offset+s; lp[3] = 0; //ascii 0-9 //strip data pin char lc[4] = "LC"; lc[2] = offset+s; lc[3] = 0; //strip length @@ -157,7 +157,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) char la[4] = "LA"; la[2] = offset+s; la[3] = 0; //LED mA char ma[4] = "MA"; ma[2] = offset+s; ma[3] = 0; //max mA if (!request->hasArg(lp)) { - DEBUG_PRINTF_P(PSTR("No data for %d\n"), s); + DEBUG_PRINTF_P(PSTR("# of buses: %d\n"), s+1); break; } for (int i = 0; i < 5; i++) { diff --git a/wled00/xml.cpp b/wled00/xml.cpp index a31b130724..2fbc32a603 100644 --- a/wled00/xml.cpp +++ b/wled00/xml.cpp @@ -272,7 +272,7 @@ void getSettingsJS(byte subPage, Print& settingsScript) // set limits settingsScript.printf_P(PSTR("bLimits(%d,%d,%d,%d,%d,%d,%d,%d);"), WLED_MAX_BUSSES, - WLED_MIN_VIRTUAL_BUSSES, + WLED_MIN_VIRTUAL_BUSSES, // irrelevant, but kept to distinguish S2/S3 in UI MAX_LEDS_PER_BUS, MAX_LED_MEMORY, MAX_LEDS, From 5b7bab675218b9e725e589003b2a93a063cd662a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Kristan?= Date: Thu, 30 Jan 2025 20:46:26 +0100 Subject: [PATCH 04/10] Compile fixes --- wled00/bus_manager.cpp | 133 +++++++++++++++++++++++++---------------- wled00/bus_manager.h | 46 ++++++++------ 2 files changed, 107 insertions(+), 72 deletions(-) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 6807f43813..f7661c2264 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -119,10 +119,15 @@ uint32_t Bus::autoWhiteCalc(uint32_t c) const { } uint8_t *Bus::allocateData(size_t size) { - if (_data) free(_data); // should not happen, but for safety + freeData(); // should not happen, but for safety return _data = (uint8_t *)(size>0 ? calloc(size, sizeof(uint8_t)) : nullptr); } +void Bus::freeData() { + if (_data) free(_data); + _data = nullptr; +} + BusDigital::BusDigital(const BusConfig &bc, uint8_t nr) : Bus(bc.type, bc.start, bc.autoWhite, bc.count, bc.reversed, (bc.refreshReq || bc.type == TYPE_TM1814)) @@ -174,7 +179,7 @@ BusDigital::BusDigital(const BusConfig &bc, uint8_t nr) //I am NOT to be held liable for burned down garages or houses! // To disable brightness limiter we either set output max current to 0 or single LED current to 0 -uint8_t BusDigital::estimateCurrentAndLimitBri() { +uint8_t BusDigital::estimateCurrentAndLimitBri() const { bool useWackyWS2815PowerModel = false; byte actualMilliampsPerLed = _milliAmpsPerLed; @@ -379,12 +384,16 @@ uint32_t IRAM_ATTR BusDigital::getPixelColor(unsigned pix) const { } } -uint8_t BusDigital::getPins(uint8_t* pinArray) const { +unsigned BusDigital::getPins(uint8_t* pinArray) const { unsigned numPins = is2Pin(_type) + 1; if (pinArray) for (unsigned i = 0; i < numPins; i++) pinArray[i] = _pins[i]; return numPins; } +unsigned BusDigital::getBusSize() const { + return sizeof(BusDigital) + (isOk() ? PolyBus::getDataSize(_busPtr, _iType) + (_data ? _len * getNumberOfChannels() : 0) : 0); +} + void BusDigital::setColorOrder(uint8_t colorOrder) { // upper nibble contains W swap information if ((colorOrder & 0x0F) > 5) return; @@ -631,7 +640,7 @@ void BusPwm::show() { } } -uint8_t BusPwm::getPins(uint8_t* pinArray) const { +unsigned BusPwm::getPins(uint8_t* pinArray) const { if (!_valid) return 0; unsigned numPins = numPWMPins(_type); if (pinArray) for (unsigned i = 0; i < numPins; i++) pinArray[i] = _pins[i]; @@ -707,7 +716,7 @@ void BusOnOff::show() { digitalWrite(_pin, _reversed ? !(bool)_data[0] : (bool)_data[0]); } -uint8_t BusOnOff::getPins(uint8_t* pinArray) const { +unsigned BusOnOff::getPins(uint8_t* pinArray) const { if (!_valid) return 0; if (pinArray) pinArray[0] = _pin; return 1; @@ -771,7 +780,7 @@ void BusNetwork::show() { _broadcastLock = false; } -uint8_t BusNetwork::getPins(uint8_t* pinArray) const { +unsigned BusNetwork::getPins(uint8_t* pinArray) const { if (pinArray) for (unsigned i = 0; i < 4; i++) pinArray[i] = _client[i]; return 4; } @@ -799,33 +808,52 @@ void BusNetwork::cleanup() { //utility to get the approx. memory usage of a given BusConfig -uint32_t BusManager::memUsage(const BusConfig &bc) { - if (Bus::isOnOff(bc.type) || Bus::isPWM(bc.type)) return OUTPUT_MAX_PINS; - - unsigned len = bc.count + bc.skipAmount; - unsigned channels = Bus::getNumberOfChannels(bc.type); - unsigned multiplier = 1; - if (Bus::isDigital(bc.type)) { // digital types - if (Bus::is16bit(bc.type)) len *= 2; // 16-bit LEDs - #ifdef ESP8266 - if (bc.pins[0] == 3) { //8266 DMA uses 5x the mem - multiplier = 5; - } - #else //ESP32 RMT uses double buffer, parallel I2S uses 8x buffer (3 times) - multiplier = PolyBus::isParallelI2S1Output() ? 24 : 2; - #endif +unsigned BusConfig::memUsage(unsigned nr) const { + if (Bus::isVirtual(type)) { + return sizeof(BusNetwork) + (count * Bus::getNumberOfChannels(type)); + } else if (Bus::isDigital(type)) { + return sizeof(BusDigital) + PolyBus::memUsage(count + skipAmount, PolyBus::getI(type, pins, nr)) + doubleBuffer * (count + skipAmount) * Bus::getNumberOfChannels(type); + } else if (Bus::isOnOff(type)) { + return sizeof(BusOnOff); + } else { + return sizeof(BusPwm); } - 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); + +unsigned BusManager::memUsage() { + // when ESP32, S2 & S3 use parallel I2S only the largest bus determines the total memory requirements for back buffers + // front buffers are always allocated per bus + unsigned size = 0; + unsigned maxI2S = 0; + #if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(ESP8266) + unsigned digitalCount = 0; + #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) + #define MAX_RMT 4 + #else + #define MAX_RMT 8 + #endif + #endif + for (const auto &bus : busses) { + unsigned busSize = bus->getBusSize(); + #if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(ESP8266) + if (bus->isDigital() && !bus->is2Pin()) digitalCount++; + if (PolyBus::isParallelI2S1Output() && digitalCount > MAX_RMT) { + unsigned i2sCommonSize = 3 * bus->getLength() * bus->getNumberOfChannels() * (bus->is16bit()+1); + if (i2sCommonSize > maxI2S) maxI2S = i2sCommonSize; + busSize -= i2sCommonSize; + } + #endif + size += busSize; + } + return size + maxI2S; } int BusManager::add(const BusConfig &bc) { + DEBUG_PRINTF_P(PSTR("Bus: Adding bus (%d - %d >= %d)\n"), getNumBusses(), getNumVirtualBusses(), WLED_MAX_BUSSES); if (getNumBusses() - getNumVirtualBusses() >= WLED_MAX_BUSSES) return -1; + unsigned numDigital = 0; + for (const auto &bus : busses) if (bus->isDigital() && !bus->is2Pin()) numDigital++; if (Bus::isVirtual(bc.type)) { busses.push_back(make_unique(bc)); //busses.push_back(new BusNetwork(bc)); @@ -839,7 +867,7 @@ int BusManager::add(const BusConfig &bc) { busses.push_back(make_unique(bc)); //busses.push_back(new BusPwm(bc)); } - return numBusses++; + return busses.size(); } // credit @willmmiles @@ -868,10 +896,14 @@ String BusManager::getLEDTypesJSONString() { } void BusManager::useParallelOutput() { - _parallelOutputs = 8; // hardcoded since we use NPB I2S x8 methods + DEBUG_PRINTLN(F("Bus: Enabling parallel I2S.")); PolyBus::setParallelI2S1Output(); } +bool BusManager::hasParallelOutput() { + return PolyBus::isParallelI2S1Output(); +} + //do not call this method from system context (network callback) void BusManager::removeAll() { DEBUG_PRINTLN(F("Removing all.")); @@ -889,7 +921,9 @@ void BusManager::removeAll() { void BusManager::esp32RMTInvertIdle() { bool idle_out; unsigned rmt = 0; - for (unsigned u = 0; u < numBusses(); u++) { + unsigned u = 0; + for (auto &bus : busses) { + if (bus->getLength()==0 || !bus->isDigital() || bus->is2Pin()) continue; #if defined(CONFIG_IDF_TARGET_ESP32C3) // 2 RMT, only has 1 I2S but NPB does not support it ATM if (u > 1) return; rmt = u; @@ -900,11 +934,11 @@ void BusManager::esp32RMTInvertIdle() { if (u > 3) return; rmt = u; #else - if (u < _parallelOutputs) continue; - if (u >= _parallelOutputs + 8) return; // only 8 RMT channels - rmt = u - _parallelOutputs; + unsigned numI2S = !PolyBus::isParallelI2S1Output(); // if using parallel I2S, RMT is used 1st + if (numI2S > u) continue; + if (u > 7 + numI2S) return; + rmt = u - numI2S; #endif - if (busses[u]->getLength()==0 || !busses[u]->isDigital() || busses[u]->is2Pin()) continue; //assumes that bus number to rmt channel mapping stays 1:1 rmt_channel_t ch = static_cast(rmt); rmt_idle_level_t lvl; @@ -913,6 +947,7 @@ void BusManager::esp32RMTInvertIdle() { else if (lvl == RMT_IDLE_LEVEL_LOW) lvl = RMT_IDLE_LEVEL_HIGH; else continue; rmt_set_idle_level(ch, idle_out, lvl); + u++ } } #endif @@ -921,9 +956,9 @@ void BusManager::on() { #ifdef ESP8266 //Fix for turning off onboard LED breaking bus if (PinManager::getPinOwner(LED_BUILTIN) == PinOwner::BusDigital) { - for (unsigned i = 0; i < numBusses; i++) { + for (auto &bus : busses) { uint8_t pins[2] = {255,255}; - if (busses[i]->isDigital() && busses[i]->getPins(pins)) { + if (bus->isDigital() && bus->getPins(pins)) { if (pins[0] == LED_BUILTIN || pins[1] == LED_BUILTIN) { BusDigital &b = static_cast(*bus); b.begin(); @@ -943,7 +978,7 @@ void BusManager::off() { // turn off built-in LED if strip is turned off // this will break digital bus so will need to be re-initialised on On if (PinManager::getPinOwner(LED_BUILTIN) == PinOwner::BusDigital) { - for (unsigned i = 0; i < numBusses; i++) if (busses[i]->isOffRefreshRequired()) return; + for (const auto &bus : busses) if (bus->isOffRefreshRequired()) return; pinMode(LED_BUILTIN, OUTPUT); digitalWrite(LED_BUILTIN, HIGH); } @@ -962,16 +997,10 @@ void BusManager::show() { } void IRAM_ATTR BusManager::setPixelColor(unsigned pix, uint32_t c) { - for (unsigned i = 0; i < numBusses; i++) { - unsigned bstart = busses[i]->getStart(); - if (pix < bstart || pix >= bstart + busses[i]->getLength()) continue; - busses[i]->setPixelColor(pix - bstart, c); - } -} - -void BusManager::setBrightness(uint8_t b) { - for (unsigned i = 0; i < numBusses; i++) { - busses[i]->setBrightness(b); + for (auto &bus : busses) { + unsigned bstart = bus->getStart(); + if (pix < bstart || pix >= bstart + bus->getLength()) continue; + bus->setPixelColor(pix - bstart, c); } } @@ -985,18 +1014,16 @@ void BusManager::setSegmentCCT(int16_t cct, bool allowWBCorrection) { } uint32_t BusManager::getPixelColor(unsigned pix) { - for (unsigned i = 0; i < numBusses; i++) { - unsigned bstart = busses[i]->getStart(); - if (!busses[i]->containsPixel(pix)) continue; - return busses[i]->getPixelColor(pix - bstart); + for (auto &bus : busses) { + unsigned bstart = bus->getStart(); + if (!bus->containsPixel(pix)) continue; + return bus->getPixelColor(pix - bstart); } return 0; } bool BusManager::canAllShow() { - for (unsigned i = 0; i < numBusses; i++) { - if (!busses[i]->canShow()) return false; - } + for (const auto &bus : busses) if (!bus->canShow()) return false; return true; } diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index 8f4906eae8..74dfd4cfff 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -1,3 +1,4 @@ +#pragma once #ifndef BusManager_h #define BusManager_h @@ -92,7 +93,7 @@ class Bus { _autoWhiteMode = Bus::hasWhite(type) ? aw : RGBW_MODE_MANUAL_ONLY; }; - virtual ~Bus() {} //throw the bus under the bus + virtual ~Bus() {} //throw the bus under the bus (derived class needs to freeData()) virtual void begin() {}; virtual void show() = 0; @@ -102,14 +103,15 @@ class Bus { virtual void setBrightness(uint8_t b) { _bri = b; }; virtual void setColorOrder(uint8_t co) {} virtual uint32_t getPixelColor(unsigned pix) const { return 0; } - virtual uint8_t getPins(uint8_t* pinArray = nullptr) const { return 0; } + virtual unsigned getPins(uint8_t* pinArray = nullptr) const { return 0; } virtual uint16_t getLength() const { return isOk() ? _len : 0; } virtual uint8_t getColorOrder() const { return COL_ORDER_RGB; } - virtual uint8_t skippedLeds() const { return 0; } + virtual unsigned skippedLeds() const { return 0; } virtual uint16_t getFrequency() const { return 0U; } virtual uint16_t getLEDCurrent() const { return 0; } virtual uint16_t getUsedCurrent() const { return 0; } virtual uint16_t getMaxCurrent() const { return 0; } + virtual unsigned getBusSize() const { return sizeof(Bus); } inline bool hasRGB() const { return _hasRgb; } inline bool hasWhite() const { return _hasWhite; } @@ -125,7 +127,7 @@ class Bus { inline void setStart(uint16_t start) { _start = start; } inline void setAutoWhiteMode(uint8_t m) { if (m < 5) _autoWhiteMode = m; } inline uint8_t getAutoWhiteMode() const { return _autoWhiteMode; } - inline uint32_t getNumberOfChannels() const { return hasWhite() + 3*hasRGB() + hasCCT(); } + inline unsigned getNumberOfChannels() const { return hasWhite() + 3*hasRGB() + hasCCT(); } inline uint16_t getStart() const { return _start; } inline uint8_t getType() const { return _type; } inline bool isOk() const { return _valid; } @@ -133,9 +135,9 @@ class Bus { inline bool isOffRefreshRequired() const { return _needsRefresh; } inline bool containsPixel(uint16_t pix) const { return pix >= _start && pix < _start + _len; } - static inline std::vector getLEDTypes() { return {{TYPE_NONE, "", PSTR("None")}}; } // not used. just for reference for derived classes - static constexpr uint32_t getNumberOfPins(uint8_t type) { return isVirtual(type) ? 4 : isPWM(type) ? numPWMPins(type) : is2Pin(type) + 1; } // credit @PaoloTK - static constexpr uint32_t getNumberOfChannels(uint8_t type) { return hasWhite(type) + 3*hasRGB(type) + hasCCT(type); } + static inline std::vector getLEDTypes() { return {{TYPE_NONE, "", PSTR("None")}}; } // not used. just for reference for derived classes + static constexpr unsigned getNumberOfPins(uint8_t type) { return isVirtual(type) ? 4 : isPWM(type) ? numPWMPins(type) : is2Pin(type) + 1; } // credit @PaoloTK + static constexpr unsigned getNumberOfChannels(uint8_t type) { return hasWhite(type) + 3*hasRGB(type) + hasCCT(type); } static constexpr bool hasRGB(uint8_t type) { return !((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_ANALOG_1CH || type == TYPE_ANALOG_2CH || type == TYPE_ONOFF); } @@ -167,7 +169,7 @@ class Bus { static inline uint8_t getGlobalAWMode() { return _gAWM; } static inline void setCCT(int16_t cct) { _cct = cct; } static inline uint8_t getCCTBlend() { return _cctBlend; } - static inline void setCCTBlend(uint8_t b) { + static inline void setCCTBlend(uint8_t b) { _cctBlend = (std::min((int)b,100) * 127) / 100; //compile-time limiter for hardware that can't power both white channels at max #ifdef WLED_MAX_CCT_BLEND @@ -206,7 +208,7 @@ class Bus { uint32_t autoWhiteCalc(uint32_t c) const; uint8_t *allocateData(size_t size = 1); - void freeData() { if (_data != nullptr) free(_data); _data = nullptr; } + void freeData(); }; @@ -223,12 +225,13 @@ class BusDigital : public Bus { void setColorOrder(uint8_t colorOrder) override; [[gnu::hot]] uint32_t getPixelColor(unsigned pix) const override; uint8_t getColorOrder() const override { return _colorOrder; } - uint8_t getPins(uint8_t* pinArray = nullptr) const override; - uint8_t skippedLeds() const override { return _skip; } + unsigned getPins(uint8_t* pinArray = nullptr) const override; + unsigned skippedLeds() const override { return _skip; } uint16_t getFrequency() const override { return _frequencykHz; } uint16_t getLEDCurrent() const override { return _milliAmpsPerLed; } uint16_t getUsedCurrent() const override { return _milliAmpsTotal; } uint16_t getMaxCurrent() const override { return _milliAmpsMax; } + unsigned getBusSize() const override; void begin() override; void cleanup(); @@ -257,7 +260,7 @@ class BusDigital : public Bus { return c; } - uint8_t estimateCurrentAndLimitBri(); + uint8_t estimateCurrentAndLimitBri() const; }; @@ -268,10 +271,11 @@ class BusPwm : public Bus { void setPixelColor(unsigned pix, uint32_t c) override; uint32_t getPixelColor(unsigned pix) const override; //does no index check - uint8_t getPins(uint8_t* pinArray = nullptr) const override; + unsigned getPins(uint8_t* pinArray = nullptr) const override; uint16_t getFrequency() const override { return _frequency; } + unsigned getBusSize() const override { return sizeof(BusPwm); } void show() override; - void cleanup() { deallocatePins(); } + inline void cleanup() { deallocatePins(); _data = nullptr; } static std::vector getLEDTypes(); @@ -295,9 +299,10 @@ class BusOnOff : public Bus { void setPixelColor(unsigned pix, uint32_t c) override; uint32_t getPixelColor(unsigned pix) const override; - uint8_t getPins(uint8_t* pinArray) const override; + unsigned getPins(uint8_t* pinArray) const override; + unsigned getBusSize() const override { return sizeof(BusOnOff); } void show() override; - void cleanup() { PinManager::deallocatePin(_pin, PinOwner::BusOnOff); } + inline void cleanup() { PinManager::deallocatePin(_pin, PinOwner::BusOnOff); _data = nullptr; } static std::vector getLEDTypes(); @@ -313,9 +318,10 @@ class BusNetwork : public Bus { ~BusNetwork() { cleanup(); } bool canShow() const override { return !_broadcastLock; } // this should be a return value from UDP routine if it is still sending data out - void setPixelColor(unsigned pix, uint32_t c) override; - uint32_t getPixelColor(unsigned pix) const override; - uint8_t getPins(uint8_t* pinArray = nullptr) const override; + [[gnu::hot]] void setPixelColor(unsigned pix, uint32_t c) override; + [[gnu::hot]] uint32_t getPixelColor(unsigned pix) const override; + unsigned getPins(uint8_t* pinArray = nullptr) const override; + unsigned getBusSize() const override { return sizeof(BusNetwork) + (isOk() ? _len * _UDPchannels : 0); } void show() override; void cleanup(); @@ -374,6 +380,8 @@ struct BusConfig { if (start + count > total) total = start + count; return true; } + + unsigned memUsage(unsigned nr = 0) const; }; From 1db3359b84ce25129729bbcf35ed5425e9bb12c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Kristan?= Date: Sat, 1 Feb 2025 12:04:24 +0100 Subject: [PATCH 05/10] Replace unsigned with size_t --- wled00/bus_manager.cpp | 18 +++++++++--------- wled00/bus_manager.h | 41 ++++++++++++++++++++--------------------- 2 files changed, 29 insertions(+), 30 deletions(-) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index f7661c2264..69cc63bed8 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -384,13 +384,13 @@ uint32_t IRAM_ATTR BusDigital::getPixelColor(unsigned pix) const { } } -unsigned BusDigital::getPins(uint8_t* pinArray) const { +size_t BusDigital::getPins(uint8_t* pinArray) const { unsigned numPins = is2Pin(_type) + 1; if (pinArray) for (unsigned i = 0; i < numPins; i++) pinArray[i] = _pins[i]; return numPins; } -unsigned BusDigital::getBusSize() const { +size_t BusDigital::getBusSize() const { return sizeof(BusDigital) + (isOk() ? PolyBus::getDataSize(_busPtr, _iType) + (_data ? _len * getNumberOfChannels() : 0) : 0); } @@ -573,7 +573,7 @@ uint32_t BusPwm::getPixelColor(unsigned pix) const { void BusPwm::show() { if (!_valid) return; - const unsigned numPins = getPins(); + const size_t numPins = getPins(); #ifdef ESP8266 const unsigned analogPeriod = F_CPU / _frequency; const unsigned maxBri = analogPeriod; // compute to clock cycle accuracy @@ -640,7 +640,7 @@ void BusPwm::show() { } } -unsigned BusPwm::getPins(uint8_t* pinArray) const { +size_t BusPwm::getPins(uint8_t* pinArray) const { if (!_valid) return 0; unsigned numPins = numPWMPins(_type); if (pinArray) for (unsigned i = 0; i < numPins; i++) pinArray[i] = _pins[i]; @@ -660,7 +660,7 @@ std::vector BusPwm::getLEDTypes() { } void BusPwm::deallocatePins() { - unsigned numPins = getPins(); + size_t numPins = getPins(); for (unsigned i = 0; i < numPins; i++) { PinManager::deallocatePin(_pins[i], PinOwner::BusPwm); if (!PinManager::isPinOk(_pins[i])) continue; @@ -716,7 +716,7 @@ void BusOnOff::show() { digitalWrite(_pin, _reversed ? !(bool)_data[0] : (bool)_data[0]); } -unsigned BusOnOff::getPins(uint8_t* pinArray) const { +size_t BusOnOff::getPins(uint8_t* pinArray) const { if (!_valid) return 0; if (pinArray) pinArray[0] = _pin; return 1; @@ -780,7 +780,7 @@ void BusNetwork::show() { _broadcastLock = false; } -unsigned BusNetwork::getPins(uint8_t* pinArray) const { +size_t BusNetwork::getPins(uint8_t* pinArray) const { if (pinArray) for (unsigned i = 0; i < 4; i++) pinArray[i] = _client[i]; return 4; } @@ -808,7 +808,7 @@ void BusNetwork::cleanup() { //utility to get the approx. memory usage of a given BusConfig -unsigned BusConfig::memUsage(unsigned nr) const { +size_t BusConfig::memUsage(unsigned nr) const { if (Bus::isVirtual(type)) { return sizeof(BusNetwork) + (count * Bus::getNumberOfChannels(type)); } else if (Bus::isDigital(type)) { @@ -821,7 +821,7 @@ unsigned BusConfig::memUsage(unsigned nr) const { } -unsigned BusManager::memUsage() { +size_t BusManager::memUsage() { // when ESP32, S2 & S3 use parallel I2S only the largest bus determines the total memory requirements for back buffers // front buffers are always allocated per bus unsigned size = 0; diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index 74dfd4cfff..65724003b9 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -10,7 +10,6 @@ #include "pin_manager.h" #include #include -#include #if __cplusplus >= 201402L using std::make_unique; @@ -103,7 +102,7 @@ class Bus { virtual void setBrightness(uint8_t b) { _bri = b; }; virtual void setColorOrder(uint8_t co) {} virtual uint32_t getPixelColor(unsigned pix) const { return 0; } - virtual unsigned getPins(uint8_t* pinArray = nullptr) const { return 0; } + virtual size_t getPins(uint8_t* pinArray = nullptr) const { return 0; } virtual uint16_t getLength() const { return isOk() ? _len : 0; } virtual uint8_t getColorOrder() const { return COL_ORDER_RGB; } virtual unsigned skippedLeds() const { return 0; } @@ -111,7 +110,7 @@ class Bus { virtual uint16_t getLEDCurrent() const { return 0; } virtual uint16_t getUsedCurrent() const { return 0; } virtual uint16_t getMaxCurrent() const { return 0; } - virtual unsigned getBusSize() const { return sizeof(Bus); } + virtual size_t getBusSize() const { return sizeof(Bus); } inline bool hasRGB() const { return _hasRgb; } inline bool hasWhite() const { return _hasWhite; } @@ -127,7 +126,7 @@ class Bus { inline void setStart(uint16_t start) { _start = start; } inline void setAutoWhiteMode(uint8_t m) { if (m < 5) _autoWhiteMode = m; } inline uint8_t getAutoWhiteMode() const { return _autoWhiteMode; } - inline unsigned getNumberOfChannels() const { return hasWhite() + 3*hasRGB() + hasCCT(); } + inline size_t getNumberOfChannels() const { return hasWhite() + 3*hasRGB() + hasCCT(); } inline uint16_t getStart() const { return _start; } inline uint8_t getType() const { return _type; } inline bool isOk() const { return _valid; } @@ -136,8 +135,8 @@ class Bus { inline bool containsPixel(uint16_t pix) const { return pix >= _start && pix < _start + _len; } static inline std::vector getLEDTypes() { return {{TYPE_NONE, "", PSTR("None")}}; } // not used. just for reference for derived classes - static constexpr unsigned getNumberOfPins(uint8_t type) { return isVirtual(type) ? 4 : isPWM(type) ? numPWMPins(type) : is2Pin(type) + 1; } // credit @PaoloTK - static constexpr unsigned getNumberOfChannels(uint8_t type) { return hasWhite(type) + 3*hasRGB(type) + hasCCT(type); } + static constexpr size_t getNumberOfPins(uint8_t type) { return isVirtual(type) ? 4 : isPWM(type) ? numPWMPins(type) : is2Pin(type) + 1; } // credit @PaoloTK + static constexpr size_t getNumberOfChannels(uint8_t type) { return hasWhite(type) + 3*hasRGB(type) + hasCCT(type); } static constexpr bool hasRGB(uint8_t type) { return !((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_ANALOG_1CH || type == TYPE_ANALOG_2CH || type == TYPE_ONOFF); } @@ -225,13 +224,13 @@ class BusDigital : public Bus { void setColorOrder(uint8_t colorOrder) override; [[gnu::hot]] uint32_t getPixelColor(unsigned pix) const override; uint8_t getColorOrder() const override { return _colorOrder; } - unsigned getPins(uint8_t* pinArray = nullptr) const override; + size_t getPins(uint8_t* pinArray = nullptr) const override; unsigned skippedLeds() const override { return _skip; } uint16_t getFrequency() const override { return _frequencykHz; } uint16_t getLEDCurrent() const override { return _milliAmpsPerLed; } uint16_t getUsedCurrent() const override { return _milliAmpsTotal; } uint16_t getMaxCurrent() const override { return _milliAmpsMax; } - unsigned getBusSize() const override; + size_t getBusSize() const override; void begin() override; void cleanup(); @@ -271,9 +270,9 @@ class BusPwm : public Bus { void setPixelColor(unsigned pix, uint32_t c) override; uint32_t getPixelColor(unsigned pix) const override; //does no index check - unsigned getPins(uint8_t* pinArray = nullptr) const override; + size_t getPins(uint8_t* pinArray = nullptr) const override; uint16_t getFrequency() const override { return _frequency; } - unsigned getBusSize() const override { return sizeof(BusPwm); } + size_t getBusSize() const override { return sizeof(BusPwm); } void show() override; inline void cleanup() { deallocatePins(); _data = nullptr; } @@ -299,8 +298,8 @@ class BusOnOff : public Bus { void setPixelColor(unsigned pix, uint32_t c) override; uint32_t getPixelColor(unsigned pix) const override; - unsigned getPins(uint8_t* pinArray) const override; - unsigned getBusSize() const override { return sizeof(BusOnOff); } + size_t getPins(uint8_t* pinArray) const override; + size_t getBusSize() const override { return sizeof(BusOnOff); } void show() override; inline void cleanup() { PinManager::deallocatePin(_pin, PinOwner::BusOnOff); _data = nullptr; } @@ -320,10 +319,10 @@ class BusNetwork : public Bus { bool canShow() const override { return !_broadcastLock; } // this should be a return value from UDP routine if it is still sending data out [[gnu::hot]] void setPixelColor(unsigned pix, uint32_t c) override; [[gnu::hot]] uint32_t getPixelColor(unsigned pix) const override; - unsigned getPins(uint8_t* pinArray = nullptr) const override; - unsigned getBusSize() const override { return sizeof(BusNetwork) + (isOk() ? _len * _UDPchannels : 0); } - void show() override; - void cleanup(); + size_t getPins(uint8_t* pinArray = nullptr) const override; + size_t getBusSize() const override { return sizeof(BusNetwork) + (isOk() ? _len * _UDPchannels : 0); } + void show() override; + void cleanup(); static std::vector getLEDTypes(); @@ -381,7 +380,7 @@ struct BusConfig { return true; } - unsigned memUsage(unsigned nr = 0) const; + size_t memUsage(unsigned nr = 0) const; }; @@ -405,13 +404,13 @@ namespace BusManager { #ifdef ESP32_DATA_IDLE_HIGH void esp32RMTInvertIdle() ; #endif - inline uint8_t getNumVirtualBusses() { - int j = 0; + inline size_t getNumVirtualBusses() { + size_t j = 0; for (const auto &bus : busses) j += bus->isVirtual(); return j; } - unsigned memUsage(); + size_t memUsage(); inline uint16_t currentMilliamps() { return _gMilliAmpsUsed + MA_FOR_ESP; } //inline uint16_t ablMilliampsMax() { unsigned sum = 0; for (auto &bus : busses) sum += bus->getMaxCurrent(); return sum; } inline uint16_t ablMilliampsMax() { return _gMilliAmpsMax; } // used for compatibility reasons (and enabling virtual global ABL) @@ -438,7 +437,7 @@ namespace BusManager { void setSegmentCCT(int16_t cct, bool allowWBCorrection = false); inline int16_t getSegmentCCT() { return Bus::getCCT(); } inline Bus* getBus(size_t busNr) { return busNr < busses.size() ? busses[busNr].get() : nullptr; } - inline uint8_t getNumBusses() { return busses.size(); } + inline size_t getNumBusses() { return busses.size(); } //semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit()) inline uint16_t getTotalLength(bool onlyPhysical = false) { From c43d09c8b18f02a5a9142f000b6449948c9335bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Kristan?= Date: Fri, 7 Feb 2025 15:02:27 +0100 Subject: [PATCH 06/10] Move _data and allocation to derived class - as suggested by @TripleWhy - minimum length guard Conflicts: wled00/bus_manager.cpp wled00/bus_manager.h --- wled00/bus_manager.cpp | 46 ++++++++++++++++++------------------------ wled00/bus_manager.h | 34 +++++++++++++++---------------- 2 files changed, 36 insertions(+), 44 deletions(-) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 69cc63bed8..68a58d5a4c 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -91,7 +91,7 @@ void Bus::calculateCCT(uint32_t c, uint8_t &ww, uint8_t &cw) { } else { cct = (approximateKelvinFromRGB(c) - 1900) >> 5; // convert K (from RGB value) to relative format } - + //0 - linear (CCT 127 = 50% warm, 50% cold), 127 - additive CCT blending (CCT 127 = 100% warm, 100% cold) if (cct < _cctBlend) ww = 255; else ww = ((255-cct) * 255) / (255 - _cctBlend); @@ -118,16 +118,6 @@ uint32_t Bus::autoWhiteCalc(uint32_t c) const { return RGBW32(r, g, b, w); } -uint8_t *Bus::allocateData(size_t size) { - freeData(); // should not happen, but for safety - return _data = (uint8_t *)(size>0 ? calloc(size, sizeof(uint8_t)) : nullptr); -} - -void Bus::freeData() { - if (_data) free(_data); - _data = nullptr; -} - BusDigital::BusDigital(const BusConfig &bc, uint8_t nr) : Bus(bc.type, bc.start, bc.autoWhite, bc.count, bc.reversed, (bc.refreshReq || bc.type == TYPE_TM1814)) @@ -153,8 +143,10 @@ BusDigital::BusDigital(const BusConfig &bc, uint8_t nr) _hasRgb = hasRGB(bc.type); _hasWhite = hasWhite(bc.type); _hasCCT = hasCCT(bc.type); - if (bc.doubleBuffer && !allocateData(bc.count * Bus::getNumberOfChannels(bc.type))) return; - //_buffering = bc.doubleBuffer; + if (bc.doubleBuffer) { + _data = (uint8_t*)d_calloc(_len, Bus::getNumberOfChannels(_type)); + if (!_data) DEBUG_PRINTLN(F("Bus: Buffer allocation failed!")); + } uint16_t lenToCreate = bc.count; if (bc.type == TYPE_WS2812_1CH_X3) lenToCreate = NUM_ICS_WS2812_1CH_3X(bc.count); // only needs a third of "RGB" LEDs for NeoPixelBus _busPtr = PolyBus::create(_iType, _pins, lenToCreate + _skip, nr); @@ -285,7 +277,7 @@ void BusDigital::show() { } } } - PolyBus::show(_busPtr, _iType, !_data); // faster if buffer consistency is not important (use !_buffering this causes 20% FPS drop) + PolyBus::show(_busPtr, _iType, !_data); // faster if buffer consistency is not important // restore bus brightness to its original value // this is done right after show, so this is only OK if LED updates are completed before show() returns // or async show has a separate buffer (ESP32 RMT and I2S are ok) @@ -434,10 +426,11 @@ void BusDigital::begin() { void BusDigital::cleanup() { DEBUG_PRINTLN(F("Digital Cleanup.")); PolyBus::cleanup(_busPtr, _iType); + free(_data); + _data = nullptr; _iType = I_NONE; _valid = false; _busPtr = nullptr; - if (_data != nullptr) freeData(); PinManager::deallocatePin(_pins[1], PinOwner::BusDigital); PinManager::deallocatePin(_pins[0], PinOwner::BusDigital); } @@ -461,7 +454,7 @@ void BusDigital::cleanup() { #else #ifdef SOC_LEDC_TIMER_BIT_WIDE_NUM // C6/H2/P4: 20 bit, S2/S3/C2/C3: 14 bit - #define MAX_BIT_WIDTH SOC_LEDC_TIMER_BIT_WIDE_NUM + #define MAX_BIT_WIDTH SOC_LEDC_TIMER_BIT_WIDE_NUM #else // ESP32: 20 bit (but in reality we would never go beyond 16 bit as the frequency would be to low) #define MAX_BIT_WIDTH 14 @@ -509,7 +502,6 @@ BusPwm::BusPwm(const BusConfig &bc) _hasRgb = hasRGB(bc.type); _hasWhite = hasWhite(bc.type); _hasCCT = hasCCT(bc.type); - _data = _pwmdata; // avoid malloc() and use stack _valid = true; DEBUG_PRINTF_P(PSTR("%successfully inited PWM strip with type %u, frequency %u, bit depth %u and pins %u,%u,%u,%u,%u\n"), _valid?"S":"Uns", bc.type, _frequency, _depth, _pins[0], _pins[1], _pins[2], _pins[3], _pins[4]); } @@ -583,7 +575,7 @@ void BusPwm::show() { // if _needsRefresh is true (UI hack) we are using dithering (credit @dedehai & @zalatnaicsongor) // https://github.com/Aircoookie/WLED/pull/4115 and https://github.com/zalatnaicsongor/WLED/pull/1) const bool dithering = _needsRefresh; // avoid working with bitfield - const unsigned maxBri = (1<<_depth); // possible values: 16384 (14), 8192 (13), 4096 (12), 2048 (11), 1024 (10), 512 (9) and 256 (8) + const unsigned maxBri = (1<<_depth); // possible values: 16384 (14), 8192 (13), 4096 (12), 2048 (11), 1024 (10), 512 (9) and 256 (8) const unsigned bitShift = dithering * 4; // if dithering, _depth is 12 bit but LEDC channel is set to 8 bit (using 4 fractional bits) #endif // use CIE brightness formula (linear + cubic) to approximate human eye perceived brightness @@ -599,7 +591,7 @@ void BusPwm::show() { [[maybe_unused]] unsigned hPoint = 0; // phase shift (0 - maxBri) // we will be phase shifting every channel by previous pulse length (plus dead time if required) - // phase shifting is only mandatory when using H-bridge to drive reverse-polarity PWM CCT (2 wire) LED type + // phase shifting is only mandatory when using H-bridge to drive reverse-polarity PWM CCT (2 wire) LED type // CCT additive blending must be 0 (WW & CW will not overlap) otherwise signals *will* overlap // for all other cases it will just try to "spread" the load on PSU // Phase shifting requires that LEDC timers are synchronised (see setup()). For PWM CCT (and H-bridge) it is @@ -678,7 +670,7 @@ void BusPwm::deallocatePins() { BusOnOff::BusOnOff(const BusConfig &bc) : Bus(bc.type, bc.start, bc.autoWhite, 1, bc.reversed) -, _onoffdata(0) +, _data(0) { if (!Bus::isOnOff(bc.type)) return; @@ -691,7 +683,6 @@ BusOnOff::BusOnOff(const BusConfig &bc) _hasRgb = false; _hasWhite = false; _hasCCT = false; - _data = &_onoffdata; // avoid malloc() and use stack _valid = true; DEBUG_PRINTF_P(PSTR("%successfully inited On/Off strip with pin %u\n"), _valid?"S":"Uns", _pin); } @@ -703,17 +694,17 @@ void BusOnOff::setPixelColor(unsigned pix, uint32_t c) { uint8_t g = G(c); uint8_t b = B(c); uint8_t w = W(c); - _data[0] = bool(r|g|b|w) && bool(_bri) ? 0xFF : 0; + _data = bool(r|g|b|w) && bool(_bri) ? 0xFF : 0; } uint32_t BusOnOff::getPixelColor(unsigned pix) const { if (!_valid) return 0; - return RGBW32(_data[0], _data[0], _data[0], _data[0]); + return RGBW32(_data, _data, _data, _data); } void BusOnOff::show() { if (!_valid) return; - digitalWrite(_pin, _reversed ? !(bool)_data[0] : (bool)_data[0]); + digitalWrite(_pin, _reversed ? !(bool)_data : (bool)_data); } size_t BusOnOff::getPins(uint8_t* pinArray) const { @@ -752,7 +743,8 @@ BusNetwork::BusNetwork(const BusConfig &bc) _hasCCT = false; _UDPchannels = _hasWhite + 3; _client = IPAddress(bc.pins[0],bc.pins[1],bc.pins[2],bc.pins[3]); - _valid = (allocateData(_len * _UDPchannels) != nullptr) && bc.count > 0; + _data = (uint8_t*)d_calloc(_len, _UDPchannels); + _valid = (_data != nullptr); DEBUG_PRINTF_P(PSTR("%successfully inited virtual strip with type %u and IP %u.%u.%u.%u\n"), _valid?"S":"Uns", bc.type, bc.pins[0], bc.pins[1], bc.pins[2], bc.pins[3]); } @@ -801,9 +793,11 @@ std::vector BusNetwork::getLEDTypes() { } void BusNetwork::cleanup() { + DEBUG_PRINTLN(F("Virtual Cleanup.")); + free(_data); + _data = nullptr; _type = I_NONE; _valid = false; - freeData(); } diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index 65724003b9..60b96048d8 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -83,20 +83,19 @@ class Bus { : _type(type) , _bri(255) , _start(start) - , _len(len) + , _len(std::max(len,(uint16_t)1)) , _reversed(reversed) , _valid(false) , _needsRefresh(refresh) - , _data(nullptr) // keep data access consistent across all types of buses { _autoWhiteMode = Bus::hasWhite(type) ? aw : RGBW_MODE_MANUAL_ONLY; }; - virtual ~Bus() {} //throw the bus under the bus (derived class needs to freeData()) + virtual ~Bus() {} //throw the bus under the bus virtual void begin() {}; virtual void show() = 0; - virtual bool canShow() const { return true; } + virtual bool canShow() const { return true; } virtual void setStatusPixel(uint32_t c) {} virtual void setPixelColor(unsigned pix, uint32_t c) = 0; virtual void setBrightness(uint8_t b) { _bri = b; }; @@ -191,7 +190,6 @@ class Bus { bool _hasCCT;// : 1; //} __attribute__ ((packed)); uint8_t _autoWhiteMode; - uint8_t *_data; // global Auto White Calculation override static uint8_t _gAWM; // _cct has the following menaings (see calculateCCT() & BusManager::setSegmentCCT()): @@ -206,8 +204,6 @@ class Bus { static uint8_t _cctBlend; uint32_t autoWhiteCalc(uint32_t c) const; - uint8_t *allocateData(size_t size = 1); - void freeData(); }; @@ -237,14 +233,15 @@ class BusDigital : public Bus { static std::vector getLEDTypes(); private: - uint8_t _skip; - uint8_t _colorOrder; - uint8_t _pins[2]; - uint8_t _iType; + uint8_t _skip; + uint8_t _colorOrder; + uint8_t _pins[2]; + uint8_t _iType; uint16_t _frequencykHz; - uint8_t _milliAmpsPerLed; + uint8_t _milliAmpsPerLed; uint16_t _milliAmpsMax; - void * _busPtr; + uint8_t *_data; + void *_busPtr; static uint16_t _milliAmpsTotal; // is overwitten/recalculated on each show() @@ -274,13 +271,13 @@ class BusPwm : public Bus { uint16_t getFrequency() const override { return _frequency; } size_t getBusSize() const override { return sizeof(BusPwm); } void show() override; - inline void cleanup() { deallocatePins(); _data = nullptr; } + inline void cleanup() { deallocatePins(); } static std::vector getLEDTypes(); private: uint8_t _pins[OUTPUT_MAX_PINS]; - uint8_t _pwmdata[OUTPUT_MAX_PINS]; + uint8_t _data[OUTPUT_MAX_PINS]; #ifdef ARDUINO_ARCH_ESP32 uint8_t _ledcStart; #endif @@ -301,13 +298,13 @@ class BusOnOff : public Bus { size_t getPins(uint8_t* pinArray) const override; size_t getBusSize() const override { return sizeof(BusOnOff); } void show() override; - inline void cleanup() { PinManager::deallocatePin(_pin, PinOwner::BusOnOff); _data = nullptr; } + inline void cleanup() { PinManager::deallocatePin(_pin, PinOwner::BusOnOff); } static std::vector getLEDTypes(); private: uint8_t _pin; - uint8_t _onoffdata; + uint8_t _data; }; @@ -331,6 +328,7 @@ class BusNetwork : public Bus { uint8_t _UDPtype; uint8_t _UDPchannels; bool _broadcastLock; + uint8_t *_data; }; @@ -351,7 +349,7 @@ struct BusConfig { uint16_t milliAmpsMax; BusConfig(uint8_t busType, uint8_t* ppins, uint16_t pstart, uint16_t len = 1, uint8_t pcolorOrder = COL_ORDER_GRB, bool rev = false, uint8_t skip = 0, byte aw=RGBW_MODE_MANUAL_ONLY, uint16_t clock_kHz=0U, bool dblBfr=false, uint8_t maPerLed=LED_MILLIAMPS_DEFAULT, uint16_t maMax=ABL_MILLIAMPS_DEFAULT) - : count(len) + : count(std::max(len,(uint16_t)1)) , start(pstart) , colorOrder(pcolorOrder) , reversed(rev) From ed91c54654e7bd4ecf40b121a3900180836796b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Kristan?= Date: Sun, 9 Feb 2025 18:13:55 +0100 Subject: [PATCH 07/10] Uninitialised _data bugfix --- wled00/bus_manager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 68a58d5a4c..2a7dce4f64 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -125,6 +125,7 @@ BusDigital::BusDigital(const BusConfig &bc, uint8_t nr) , _colorOrder(bc.colorOrder) , _milliAmpsPerLed(bc.milliAmpsPerLed) , _milliAmpsMax(bc.milliAmpsMax) +, _data(nullptr) { if (!isDigital(bc.type) || !bc.count) return; if (!PinManager::allocatePin(bc.pins[0], true, PinOwner::BusDigital)) return; From f3de45c6ad7f8a64788ea320212ee4ca933120db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Kristan?= Date: Sun, 9 Feb 2025 21:43:35 +0100 Subject: [PATCH 08/10] Remove reference to custom allocators --- wled00/bus_manager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 2a7dce4f64..cdb00b0b38 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -145,7 +145,7 @@ BusDigital::BusDigital(const BusConfig &bc, uint8_t nr) _hasWhite = hasWhite(bc.type); _hasCCT = hasCCT(bc.type); if (bc.doubleBuffer) { - _data = (uint8_t*)d_calloc(_len, Bus::getNumberOfChannels(_type)); + _data = (uint8_t*)calloc(_len, Bus::getNumberOfChannels(_type)); if (!_data) DEBUG_PRINTLN(F("Bus: Buffer allocation failed!")); } uint16_t lenToCreate = bc.count; @@ -744,7 +744,7 @@ BusNetwork::BusNetwork(const BusConfig &bc) _hasCCT = false; _UDPchannels = _hasWhite + 3; _client = IPAddress(bc.pins[0],bc.pins[1],bc.pins[2],bc.pins[3]); - _data = (uint8_t*)d_calloc(_len, _UDPchannels); + _data = (uint8_t*)calloc(_len, _UDPchannels); _valid = (_data != nullptr); DEBUG_PRINTF_P(PSTR("%successfully inited virtual strip with type %u and IP %u.%u.%u.%u\n"), _valid?"S":"Uns", bc.type, bc.pins[0], bc.pins[1], bc.pins[2], bc.pins[3]); } From a5277ff4a0da51786a2c4abd3c4cbd370b24e54c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Kristan?= Date: Tue, 25 Feb 2025 16:01:30 +0100 Subject: [PATCH 09/10] Merge fix --- wled00/bus_manager.h | 1 + 1 file changed, 1 insertion(+) diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index d83a1d03b7..0570cc2d6c 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -21,6 +21,7 @@ make_unique(Args&&... args) { return std::unique_ptr(new T(std::forward(args)...)); } +#endif // enable additional debug output #if defined(WLED_DEBUG_HOST) From 4271588ecd12e7f73a01522a7617aa0504f01c35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Kristan?= Date: Tue, 25 Feb 2025 16:06:13 +0100 Subject: [PATCH 10/10] unique_ptr fix --- wled00/bus_manager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index 57e1936af4..cee34c2ea2 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -942,8 +942,8 @@ void BusManager::on() { uint8_t pins[2] = {255,255}; if (bus->isDigital() && bus->getPins(pins)) { if (pins[0] == LED_BUILTIN || pins[1] == LED_BUILTIN) { - BusDigital *b = static_cast(bus); - b->begin(); + BusDigital &b = static_cast(*bus); + b.begin(); break; } }