diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 7bd875ad0..65cb2640e 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1138,12 +1138,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) @@ -1449,8 +1446,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. @@ -1460,6 +1456,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); } DEBUG_PRINTF_P(PSTR("Heap after buses: %d\n"), ESP.getFreeHeap()); @@ -1747,8 +1744,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; @@ -1757,8 +1754,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; @@ -1811,10 +1808,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 @@ -1904,7 +1902,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 6423a436b..cee34c2ea 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -43,6 +43,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}); @@ -53,10 +55,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; } @@ -72,7 +72,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); @@ -99,23 +99,14 @@ 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, 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) +, _data(nullptr) { DEBUGBUS_PRINTLN(F("Bus: Creating digital bus.")); if (!isDigital(bc.type) || !bc.count) { DEBUGBUS_PRINTLN(F("Not digial or empty bus!")); return; } @@ -136,12 +127,14 @@ BusDigital::BusDigital(const BusConfig &bc, uint8_t nr, const ColorOrderMap &com _hasRgb = hasRGB(bc.type); _hasWhite = hasWhite(bc.type); _hasCCT = hasCCT(bc.type); - if (bc.doubleBuffer && !allocateData(bc.count * Bus::getNumberOfChannels(bc.type))) { DEBUGBUS_PRINTLN(F("Buffer allocation failed!")); return; } - //_buffering = bc.doubleBuffer; + if (bc.doubleBuffer) { + _data = (uint8_t*)calloc(_len, Bus::getNumberOfChannels(_type)); + if (!_data) DEBUGBUS_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); - _valid = (_busPtr != nullptr); + _valid = (_busPtr != nullptr) && bc.count > 0; DEBUGBUS_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, @@ -268,7 +261,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) @@ -421,11 +414,11 @@ void BusDigital::begin() { void BusDigital::cleanup() { DEBUGBUS_PRINTLN(F("Digital Cleanup.")); PolyBus::cleanup(_busPtr, _iType); + free(_data); + _data = nullptr; _iType = I_NONE; _valid = false; _busPtr = nullptr; - freeData(); - //PinManager::deallocateMultiplePins(_pins, 2, PinOwner::BusDigital); PinManager::deallocatePin(_pins[1], PinOwner::BusDigital); PinManager::deallocatePin(_pins[0], PinOwner::BusDigital); } @@ -449,7 +442,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 @@ -497,7 +490,6 @@ BusPwm::BusPwm(const BusConfig &bc) _hasRgb = hasRGB(bc.type); _hasWhite = hasWhite(bc.type); _hasCCT = hasCCT(bc.type); - _data = _pwmdata; // avoid malloc() and use already allocated memory _valid = true; DEBUGBUS_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]); } @@ -561,7 +553,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 @@ -571,7 +563,7 @@ void BusPwm::show() { // if _needsRefresh is true (UI hack) we are using dithering (credit @dedehai & @zalatnaicsongor) // https://github.com/wled-dev/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 @@ -587,7 +579,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 @@ -648,7 +640,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; @@ -666,7 +658,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; @@ -679,7 +671,6 @@ BusOnOff::BusOnOff(const BusConfig &bc) _hasRgb = false; _hasWhite = false; _hasCCT = false; - _data = &_onoffdata; // avoid malloc() and use stack _valid = true; DEBUGBUS_PRINTF_P(PSTR("%successfully inited On/Off strip with pin %u\n"), _valid?"S":"Uns", _pin); } @@ -691,17 +682,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); } unsigned BusOnOff::getPins(uint8_t* pinArray) const { @@ -740,7 +731,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); + _data = (uint8_t*)calloc(_len, _UDPchannels); + _valid = (_data != nullptr); DEBUGBUS_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]); } @@ -790,9 +782,10 @@ std::vector BusNetwork::getLEDTypes() { void BusNetwork::cleanup() { DEBUGBUS_PRINTLN(F("Virtual Cleanup.")); + free(_data); + _data = nullptr; _type = I_NONE; _valid = false; - freeData(); } @@ -844,17 +837,17 @@ int BusManager::add(const BusConfig &bc) { unsigned numDigital = 0; for (const auto &bus : busses) if (bus->isDigital() && !bus->is2Pin()) numDigital++; if (Bus::isVirtual(bc.type)) { - //busses.push_back(std::make_unique(bc)); // when C++ >11 - busses.push_back(new BusNetwork(bc)); + busses.push_back(make_unique(bc)); + //busses.push_back(new BusNetwork(bc)); } else if (Bus::isDigital(bc.type)) { - //busses.push_back(std::make_unique(bc, numDigital, colorOrderMap)); - busses.push_back(new BusDigital(bc, numDigital, colorOrderMap)); + busses.push_back(make_unique(bc, numDigital)); + //busses.push_back(new BusDigital(bc, numDigital)); } else if (Bus::isOnOff(bc.type)) { - //busses.push_back(std::make_unique(bc)); - busses.push_back(new BusOnOff(bc)); + busses.push_back(make_unique(bc)); + //busses.push_back(new BusOnOff(bc)); } else { - //busses.push_back(std::make_unique(bc)); - busses.push_back(new BusPwm(bc)); + busses.push_back(make_unique(bc)); + //busses.push_back(new BusPwm(bc)); } return busses.size(); } @@ -898,7 +891,7 @@ void BusManager::removeAll() { DEBUGBUS_PRINTLN(F("Removing all.")); //prevents crashes due to deleting busses while in use. while (!canAllShow()) yield(); - for (auto &bus : busses) delete bus; // needed when not using std::unique_ptr C++ >11 + //for (auto &bus : busses) delete bus; // needed when not using std::unique_ptr C++ >11 busses.clear(); PolyBus::setParallelI2S1Output(false); } @@ -949,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; } } @@ -978,17 +971,13 @@ void BusManager::off() { } void BusManager::show() { - _milliAmpsUsed = 0; + _gMilliAmpsUsed = 0; for (auto &bus : busses) { bus->show(); - _milliAmpsUsed += bus->getUsedCurrent(); + _gMilliAmpsUsed += bus->getUsedCurrent(); } } -void BusManager::setStatusPixel(uint32_t c) { - for (auto &bus : busses) bus->setStatusPixel(c); -} - void IRAM_ATTR BusManager::setPixelColor(unsigned pix, uint32_t c) { for (auto &bus : busses) { unsigned bstart = bus->getStart(); @@ -997,10 +986,6 @@ void IRAM_ATTR BusManager::setPixelColor(unsigned pix, uint32_t c) { } } -void BusManager::setBrightness(uint8_t b) { - for (auto &bus : busses) bus->setBrightness(b); -} - void BusManager::setSegmentCCT(int16_t cct, bool allowWBCorrection) { if (cct > 255) cct = 255; if (cct >= 0) { @@ -1024,17 +1009,8 @@ bool BusManager::canAllShow() { return true; } -Bus* BusManager::getBus(uint8_t busNr) { - if (busNr >= busses.size()) 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 (const auto &bus : busses) len += bus->getLength(); - return len; -} bool PolyBus::_useParallelI2S = false; @@ -1045,8 +1021,7 @@ uint8_t Bus::_gAWM = 255; uint16_t BusDigital::_milliAmpsTotal = 0; -//std::vector> BusManager::busses; -std::vector BusManager::busses; -ColorOrderMap BusManager::colorOrderMap = {}; -uint16_t BusManager::_milliAmpsUsed = 0; -uint16_t BusManager::_milliAmpsMax = ABL_MILLIAMPS_DEFAULT; +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 59e574cb0..0570cc2d6 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -11,6 +11,18 @@ #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 + // enable additional debug output #if defined(WLED_DEBUG_HOST) #include "net_debug.h" @@ -94,11 +106,10 @@ 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; }; @@ -202,7 +213,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()): @@ -217,14 +227,12 @@ class Bus { static uint8_t _cctBlend; uint32_t autoWhiteCalc(uint32_t c) const; - uint8_t *allocateData(size_t size = 1); - void freeData(); }; 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; @@ -248,15 +256,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; - const ColorOrderMap &_colorOrderMap; + uint8_t *_data; + void *_busPtr; static uint16_t _milliAmpsTotal; // is overwitten/recalculated on each show() @@ -286,13 +294,13 @@ class BusPwm : public Bus { uint16_t getFrequency() const override { return _frequency; } unsigned 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 @@ -313,13 +321,13 @@ class BusOnOff : public Bus { unsigned getPins(uint8_t* pinArray) const override; unsigned 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; }; @@ -343,6 +351,7 @@ class BusNetwork : public Bus { uint8_t _UDPtype; uint8_t _UDPchannels; bool _broadcastLock; + uint8_t *_data; }; @@ -363,7 +372,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) @@ -416,59 +425,58 @@ struct BusConfig { #endif #endif -class BusManager { - public: - BusManager() {}; +namespace BusManager { - static unsigned memUsage(); - static uint16_t currentMilliamps() { return _milliAmpsUsed + MA_FOR_ESP; } - static uint16_t ablMilliampsMax() { return _milliAmpsMax; } + extern std::vector> busses; + //extern std::vector busses; + extern uint16_t _gMilliAmpsUsed; + extern uint16_t _gMilliAmpsMax; - static int add(const BusConfig &bc); - static void useParallelOutput(); // workaround for inaccessible PolyBus - static bool hasParallelOutput(); // workaround for inaccessible PolyBus + #ifdef ESP32_DATA_IDLE_HIGH + void esp32RMTInvertIdle() ; + #endif + inline size_t getNumVirtualBusses() { + size_t j = 0; + for (const auto &bus : busses) j += bus->isVirtual(); + return j; + } - //do not call this method from system context (network callback) - static void removeAll(); + 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) + inline void setMilliampsMax(uint16_t max) { _gMilliAmpsMax = max;} - static void on(); - static void off(); + void useParallelOutput(); // workaround for inaccessible PolyBus + bool hasParallelOutput(); // workaround for inaccessible PolyBus - 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(); } + //do not call this method from system context (network callback) + void removeAll(); + int add(const BusConfig &bc); - static Bus* getBus(uint8_t busNr); + void on(); + void off(); - //semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit()) - static uint16_t getTotalLength(); - static inline uint8_t getNumBusses() { return busses.size(); } - static String getLEDTypesJSONString(); + [[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(size_t busNr) { return busNr < busses.size() ? busses[busNr].get() : nullptr; } + inline size_t getNumBusses() { return busses.size(); } - static inline ColorOrderMap& getColorOrderMap() { return colorOrderMap; } - - private: - //static std::vector> busses; // we'd need C++ >11 - static std::vector busses; - static ColorOrderMap colorOrderMap; - static uint16_t _milliAmpsUsed; - static uint16_t _milliAmpsMax; - - #ifdef ESP32_DATA_IDLE_HIGH - static void esp32RMTInvertIdle() ; - #endif - static uint8_t getNumVirtualBusses() { - int j = 0; - for (const auto &bus : busses) j += bus->isVirtual(); - return j; - } + //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 3687b4734..11862f83f 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -170,7 +170,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { if (fromFS) BusManager::removeAll(); // can't safely manipulate busses directly in network callback 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; @@ -199,12 +199,13 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { } ledType |= refresh << 7; // hack bit 7 to indicate strip requires off refresh - busConfigs.push_back(std::move(BusConfig(ledType, pins, start, length, colorOrder, reversed, skipFirst, AWmode, freqkHz, useGlobalLedBuffer, maPerLed, maMax))); + //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() - s++; + if (!Bus::isVirtual(ledType)) s++; // have as many virtual buses as you want } } - 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")]; @@ -824,8 +825,8 @@ void serializeConfig() { for (size_t s = 0; s < BusManager::getNumBusses(); s++) { DEBUG_PRINTF_P(PSTR("Cfg: Saving bus #%u\n"), s); - Bus *bus = BusManager::getBus(s); - if (!bus || bus->getLength()==0) break; + 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), @@ -838,28 +839,27 @@ void serializeConfig() { ); 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")); 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/const.h b/wled00/const.h index b830bed74..2b460f3f1 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 3 + #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 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_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 14 // will allow 12 digital & 2 analog RGB - #define WLED_MAX_DIGITAL_CHANNELS 12 // x4 RMT + x1/x8 I2S0 + #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 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 6 + #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 19 // will allow 16 digital & 3 analog RGB #define WLED_MAX_DIGITAL_CHANNELS 16 // x1/x8 I2S1 + x8 RMT //#define WLED_MAX_ANALOG_CHANNELS 16 - #define WLED_MIN_VIRTUAL_BUSSES 6 + #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 0900be399..79b41bcdf 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): 19 - ESP32, 14 - S3/S2, 6 - C3, 4 - 8266 - maxD = d; // maxD - max digital channels (can be changed if using ESP32 parallel I2S): 16 - ESP32, 12 - S3/S2, 2 - C3, 3 - 8266 + 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: 4 - ESP32/S3, 3 - S2/C3, 2 - ESP8266 + 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) @@ -359,8 +359,9 @@ 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==3) does support single I2S + 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); @@ -421,8 +422,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 @@ -495,7 +496,7 @@ mA/LED: 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(); @@ -637,22 +648,28 @@ Swap: