Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9ad0d97
WIP: make bus driver (RMT/I2S) user-selectable
Copilot Jan 3, 2026
8d9e775
define new constants instead of using local variable (WIP)
DedeHai Jan 10, 2026
766097c
Simplify bus allocation by storing iType in BusConfig
Copilot Jan 10, 2026
b518c19
Fix parallel I2S DMA buffer estimation (8x multiplier)
Copilot Jan 10, 2026
6934100
improvements to getI()
DedeHai Jan 11, 2026
cd1148d
Improve UI validation logic for RMT/I2S bus management
Copilot Jan 11, 2026
044506f
Refactor UI validation: extract helper function and fix duplicate res…
Copilot Jan 11, 2026
5ebf650
Apply I2S validation rules to finalizeInit() matching UI behavior
Copilot Jan 11, 2026
89f07fd
add globals to UI
DedeHai Jan 11, 2026
d389806
remove obsolete I2S user select option, bugfix in getI(), better UI v…
DedeHai Jan 11, 2026
02393b2
remove "600 LEDs max" restriction, bugfixes in mem calc, other fixes
DedeHai Jan 11, 2026
cb24aec
add proper restriction to RMT/I2S buses for legacy configs, still nee…
DedeHai Jan 15, 2026
ae2912d
cleanup, remove unused/duplicate code
DedeHai Jan 15, 2026
bf4a066
cleanup, simplified memory calculation code by removing clutter
DedeHai Jan 15, 2026
0954f6c
cleanup, add ifdefs for ESP8266 to save some ram (UI for ESP8266 is b…
DedeHai Jan 15, 2026
e4f89e5
bugfix in UI: mux max buses instead of RMT+I2S which are zero on ESP8…
DedeHai Jan 15, 2026
94974fc
fix in UI if all RMT buses are used (restrict type)
DedeHai Jan 15, 2026
5231b27
fixes in memory calculations, added buffers to memory use, updated ma…
DedeHai Jan 17, 2026
33bacc1
removed some debug outputs
DedeHai Jan 17, 2026
0ea65d0
bugfixes, added ptr=nullptr to p_free() and d_free() for robustness
DedeHai Jan 17, 2026
0a724bd
bugfixes, removed annoying debug output
DedeHai Jan 17, 2026
8c10915
improvements for S2
DedeHai Jan 18, 2026
ead95e8
update for C3, removed max LED restriction, increased MAX_LED_MEMORY
DedeHai Jan 18, 2026
7554783
use CONFIG_IDF_TARGET_ESP32 instead of ARDUINO_ARCH
DedeHai Jan 18, 2026
ff34848
revert nullpointer mishap
DedeHai Jan 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions usermods/rgb-rotary-encoder/rgb-rotary-encoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ class RgbRotaryEncoderUsermod : public Usermod
// …then set only the LED pin
_pins[0] = static_cast<byte>(ledIo);
BusConfig busCfg = BusConfig(TYPE_WS2812_RGB, _pins, 0, numLeds, COL_ORDER_GRB, false, 0);

ledBus = new BusDigital(busCfg, WLED_MAX_BUSSES - 1);
busCfg.iType = BusManager::getI(busCfg.type, busCfg.pins, busCfg.driverType); // assign internal bus type and output driver
ledBus = new BusDigital(busCfg);
if (!ledBus->isOk()) {
cleanup();
return;
Expand Down
3 changes: 3 additions & 0 deletions wled00/FX_2Dfcn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ void WS2812FX::setUpMatrix() {
for (unsigned i=0; i<customMappingSize; i++) {
if (!(i%Segment::maxWidth)) DEBUG_PRINTLN();
DEBUG_PRINTF_P(PSTR("%4d,"), customMappingTable[i]);
#if defined(CONFIG_IDF_TARGET_ESP32S2)
delay(1); // on S2 the CDC output can crash without a delay
#endif
}
DEBUG_PRINTLN();
#endif
Expand Down
77 changes: 41 additions & 36 deletions wled00/FX_fcn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,16 +96,17 @@ Segment& Segment::operator= (const Segment &orig) {
//DEBUG_PRINTF_P(PSTR("-- Copying segment: %p -> %p\n"), &orig, this);
if (this != &orig) {
// clean destination
if (name) { p_free(name); name = nullptr; }
if (name) { p_free(name); }
name = nullptr;
if (_t) stopTransition(); // also erases _t
deallocateData();
p_free(pixels);
pixels = nullptr;
// copy source
memcpy((void*)this, (void*)&orig, sizeof(Segment));
// erase pointers to allocated data
data = nullptr;
_dataLen = 0;
pixels = nullptr;
if (!stop) return *this; // nothing to do if segment is inactive/invalid
// copy source data
if (orig.pixels) {
Expand Down Expand Up @@ -1159,66 +1160,71 @@ void WS2812FX::finalizeInit() {

_hasWhiteChannel = _isOffRefreshRequired = false;
BusManager::removeAll();

// TODO: ideally we would free everything segment related here to reduce fragmentation (pixel buffers, ledamp, segments, etc) but that somehow leads to heap corruption if touchig any of the buffers.
unsigned digitalCount = 0;
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3)
// determine if it is sensible to use parallel I2S outputs on ESP32 (i.e. more than 5 outputs = 1 I2S + 4 RMT)
unsigned maxLedsOnBus = 0;
unsigned busType = 0;
// validate the bus config: count I2S buses and check if they meet requirements
unsigned i2sBusCount = 0;

for (const auto &bus : busConfigs) {
if (Bus::isDigital(bus.type) && !Bus::is2Pin(bus.type)) {
digitalCount++;
if (busType == 0) busType = bus.type; // remember first bus type
if (busType != bus.type) {
DEBUG_PRINTF_P(PSTR("Mixed digital bus types detected! Forcing single I2S output.\n"));
useParallelI2S = false; // mixed bus types, no parallel I2S
}
if (bus.count > maxLedsOnBus) maxLedsOnBus = bus.count;
if (bus.driverType == 1)
i2sBusCount++;
}
}
DEBUG_PRINTF_P(PSTR("Maximum LEDs on a bus: %u\nDigital buses: %u\n"), maxLedsOnBus, digitalCount);
// we may remove 600 LEDs per bus limit when NeoPixelBus is updated beyond 2.8.3
if (maxLedsOnBus <= 600 && useParallelI2S) BusManager::useParallelOutput(); // must call before creating buses
else useParallelI2S = false; // enforce single I2S
digitalCount = 0;
DEBUG_PRINTF_P(PSTR("Digital buses: %u, I2S buses: %u\n"), digitalCount, i2sBusCount);

// Determine parallel vs single I2S usage (used for memory calculation only)
bool useParallelI2S = false;
#if defined(CONFIG_IDF_TARGET_ESP32S3)
// ESP32-S3 always uses parallel LCD driver for I2S
if (i2sBusCount > 0) {
useParallelI2S = true;
}
#else
if (i2sBusCount > 1) {
useParallelI2S = true;
}
#endif
#endif

DEBUG_PRINTF_P(PSTR("Heap before buses: %d\n"), getFreeHeapSize());
// create buses/outputs
unsigned mem = 0;
unsigned maxI2S = 0;
for (const auto &bus : busConfigs) {
unsigned memB = bus.memUsage(Bus::isDigital(bus.type) && !Bus::is2Pin(bus.type) ? digitalCount++ : 0); // does not include DMA/RMT buffer
unsigned mem = 0; // memory estimation including DMA buffer for I2S and pixel buffers
unsigned I2SdmaMem = 0;
for (auto &bus : busConfigs) {
// assign bus types: call to getI() determines bus types/drivers, allocates and tracks polybus channels
// store the result in iType for later use during bus creation (getI() must only be called once per BusConfig)
// note: this needs to be determined for all buses prior to creating them as it also determines parallel I2S usage
bus.iType = BusManager::getI(bus.type, bus.pins, bus.driverType);
}
for (auto &bus : busConfigs) {
unsigned memB = bus.memUsage(); // does not include DMA/RMT buffer but includes pixel buffers (segment buffer + global buffer)
mem += memB;
// estimate maximum I2S memory usage (only relevant for digital non-2pin busses)
// estimate maximum I2S memory usage (only relevant for digital non-2pin busses when I2S is enabled)
#if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(ESP8266)
#if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32S3)
const bool usesI2S = ((useParallelI2S && digitalCount <= 8) || (!useParallelI2S && digitalCount == 1));
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
const bool usesI2S = (useParallelI2S && digitalCount <= 8);
#else
const bool usesI2S = false;
#endif
bool usesI2S = (bus.iType & 0x01) == 0; // I2S bus types are even numbered, can't use bus.driverType == 1 as getI() may have defaulted to RMT
if (Bus::isDigital(bus.type) && !Bus::is2Pin(bus.type) && usesI2S) {
#ifdef NPB_CONF_4STEP_CADENCE
constexpr unsigned stepFactor = 4; // 4 step cadence (4 bits per pixel bit)
#else
constexpr unsigned stepFactor = 3; // 3 step cadence (3 bits per pixel bit)
#endif
unsigned i2sCommonSize = stepFactor * bus.count * (3*Bus::hasRGB(bus.type)+Bus::hasWhite(bus.type)+Bus::hasCCT(bus.type)) * (Bus::is16bit(bus.type)+1);
if (i2sCommonSize > maxI2S) maxI2S = i2sCommonSize;
unsigned i2sCommonMem = (stepFactor * bus.count * (3*Bus::hasRGB(bus.type)+Bus::hasWhite(bus.type)+Bus::hasCCT(bus.type)) * (Bus::is16bit(bus.type)+1));
if (useParallelI2S) i2sCommonMem *= 8; // parallel I2S uses 8 channels, requiring 8x the DMA buffer size (common buffer shared between all parallel busses)
if (i2sCommonMem > I2SdmaMem) I2SdmaMem = i2sCommonMem;
}
#endif
if (mem + maxI2S <= MAX_LED_MEMORY) {
if (mem + I2SdmaMem <= MAX_LED_MEMORY + 1024) { // +1k to allow some margin to not drop buses that are allowed in UI (calculation here includes bus overhead)
BusManager::add(bus);
DEBUG_PRINTF_P(PSTR("Bus memory: %uB\n"), memB);
} else {
errorFlag = ERR_NORAM_PX; // alert UI
DEBUG_PRINTF_P(PSTR("Out of LED memory! Bus %d (%d) #%u not created."), (int)bus.type, (int)bus.count, digitalCount);
DEBUG_PRINTF_P(PSTR("Out of LED memory! Bus %d (%d) not created."), (int)bus.type, (int)bus.count);
break;
}
}
DEBUG_PRINTF_P(PSTR("LED buffer size: %uB/%uB\n"), mem + maxI2S, BusManager::memUsage());
DEBUG_PRINTF_P(PSTR("Estimated buses + pixel-buffers size: %uB\n"), mem + I2SdmaMem);
busConfigs.clear();
busConfigs.shrink_to_fit();

Expand Down Expand Up @@ -1249,7 +1255,6 @@ void WS2812FX::finalizeInit() {
deserializeMap(); // (re)load default ledmap (will also setUpMatrix() if ledmap does not exist)

// allocate frame buffer after matrix has been set up (gaps!)
p_free(_pixels); // using realloc on large buffers can cause additional fragmentation instead of reducing it
// use PSRAM if available: there is no measurable perfomance impact between PSRAM and DRAM on S2/S3 with QSPI PSRAM for this buffer
_pixels = static_cast<uint32_t*>(allocate_buffer(getLengthTotal() * sizeof(uint32_t), BFRALLOC_ENFORCE_PSRAM | BFRALLOC_NOBYTEACCESS | BFRALLOC_CLEAR));
DEBUG_PRINTF_P(PSTR("strip buffer size: %uB\n"), getLengthTotal() * sizeof(uint32_t));
Expand Down
114 changes: 44 additions & 70 deletions wled00/bus_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@

extern char cmDNS[];
extern bool cctICused;
extern bool useParallelI2S;

// functions to get/set bits in an array - based on functions created by Brandon for GOL
// toDo : make this a class that's completely defined in a header file
Expand Down Expand Up @@ -117,15 +116,17 @@ uint32_t Bus::autoWhiteCalc(uint32_t c) const {
}


BusDigital::BusDigital(const BusConfig &bc, uint8_t nr)
BusDigital::BusDigital(const BusConfig &bc)
: 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)
, _driverType(bc.driverType) // Store driver preference (0=RMT, 1=I2S)
{
DEBUGBUS_PRINTLN(F("Bus: Creating digital bus."));
if (!isDigital(bc.type) || !bc.count) { DEBUGBUS_PRINTLN(F("Not digial or empty bus!")); return; }

if (!PinManager::allocatePin(bc.pins[0], true, PinOwner::BusDigital)) { DEBUGBUS_PRINTLN(F("Pin 0 allocated!")); return; }
_frequencykHz = 0U;
_colorSum = 0;
Expand All @@ -139,28 +140,32 @@ BusDigital::BusDigital(const BusConfig &bc, uint8_t nr)
_pins[1] = bc.pins[1];
_frequencykHz = bc.frequency ? bc.frequency : 2000U; // 2MHz clock if undefined
}
_iType = PolyBus::getI(bc.type, _pins, nr);

_iType = bc.iType; // reuse the iType that was determined by polyBus in getI() in finalizeInit()
if (_iType == I_NONE) { DEBUGBUS_PRINTLN(F("Incorrect iType!")); return; }
_hasRgb = hasRGB(bc.type);
_hasWhite = hasWhite(bc.type);
_hasCCT = hasCCT(bc.type);
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);
_busPtr = PolyBus::create(_iType, _pins, lenToCreate + _skip);
_valid = (_busPtr != nullptr) && bc.count > 0;
// fix for wled#4759
if (_valid) for (unsigned i = 0; i < _skip; i++) {
PolyBus::setPixelColor(_busPtr, _iType, i, 0, COL_ORDER_GRB); // set sacrificial pixels to black (CO does not matter here)
}
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,
else {
cleanup();
}
Comment on lines +144 to +159
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Release allocated pins when iType is invalid.

Line 145 returns early on I_NONE after pin allocation, leaving pins reserved and the bus partially constructed. This is particularly risky for usermods that forget to populate iType.

🛠️ Suggested fix
   _pins[0] = bc.pins[0];
+  _pins[1] = 255U; // default for 1‑pin buses
   if (is2Pin(bc.type)) {
     if (!PinManager::allocatePin(bc.pins[1], true, PinOwner::BusDigital)) {
       cleanup();
       DEBUGBUS_PRINTLN(F("Pin 1 allocated!"));
       return;
     }
     _pins[1] = bc.pins[1];
     _frequencykHz = bc.frequency ? bc.frequency : 2000U; // 2MHz clock if undefined
   }

   _iType = bc.iType; // reuse the iType that was determined by polyBus in getI() in finalizeInit()
-  if (_iType == I_NONE) { DEBUGBUS_PRINTLN(F("Incorrect iType!")); return; }
+  if (_iType == I_NONE) {
+    DEBUGBUS_PRINTLN(F("Incorrect iType!"));
+    cleanup(); // release pins allocated above
+    return;
+  }
🤖 Prompt for AI Agents
In `@wled00/bus_manager.cpp` around lines 144 - 159, The code returns early when
_iType == I_NONE after pins have been allocated, leaking the reserved pins;
update the finalizeInit() flow so that when _iType == I_NONE you release
resources before returning (e.g., call cleanup() or otherwise free
_pins/_busPtr) instead of returning immediately—modify the block that currently
does "if (_iType == I_NONE) { DEBUGBUS_PRINTLN(...); return; }" to invoke
cleanup() (or the appropriate pin-release routine) then return, ensuring
_busPtr, _pins and related state are cleared; reference symbols: _iType, I_NONE,
finalizeInit(), _pins, _busPtr, cleanup(), PolyBus::create.

DEBUGBUS_PRINTF_P(PSTR("Bus len:%u, type:%u (RGB:%d, W:%d, CCT:%d), pins:%u,%u [itype:%u, driver:%s] mA=%d/%d %s\n"),
(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
isI2S() ? "I2S" : "RMT",
(int)_milliAmpsPerLed, (int)_milliAmpsMax,
_valid ? " " : "FAILED"
);
}

Expand Down Expand Up @@ -351,6 +356,10 @@ std::vector<LEDType> BusDigital::getLEDTypes() {
};
}

bool BusDigital::isI2S() {
return (_iType & 0x01) == 0; // I2S types have even iType values
}

void BusDigital::begin() {
if (!_valid) return;
PolyBus::begin(_busPtr, _iType, _pins, _frequencykHz);
Expand Down Expand Up @@ -1105,47 +1114,20 @@ size_t BusHub75Matrix::getPins(uint8_t* pinArray) const {
#endif
// ***************************************************************************

//utility to get the approx. memory usage of a given BusConfig
size_t BusConfig::memUsage(unsigned nr) const {
//utility to get the approx. memory usage of a given BusConfig inclduding segmentbuffer and global buffer (4 bytes per pixel)
size_t BusConfig::memUsage() const {
size_t mem = (count + skipAmount) * 8; // 8 bytes per pixel for segment + global buffer
if (Bus::isVirtual(type)) {
return sizeof(BusNetwork) + (count * Bus::getNumberOfChannels(type));
mem += sizeof(BusNetwork) + (count * Bus::getNumberOfChannels(type)); // note: getNumberOfChannels() includes CCT channel if applicable but virtual buses do not use CCT channel buffer
} else if (Bus::isDigital(type)) {
// if any of digital buses uses I2S, there is additional common I2S DMA buffer not accounted for here
return sizeof(BusDigital) + PolyBus::memUsage(count + skipAmount, PolyBus::getI(type, pins, nr));
mem += sizeof(BusDigital) + PolyBus::memUsage(count + skipAmount, iType);
} else if (Bus::isOnOff(type)) {
return sizeof(BusOnOff);
mem += sizeof(BusOnOff);
} else {
return sizeof(BusPwm);
}
}


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;
unsigned maxI2S = 0;
#if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(ESP8266)
unsigned digitalCount = 0;
#endif
for (const auto &bus : busses) {
size += bus->getBusSize();
#if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(ESP8266)
if (bus->isDigital() && !bus->is2Pin()) {
digitalCount++;
if ((PolyBus::isParallelI2S1Output() && digitalCount <= 8) || (!PolyBus::isParallelI2S1Output() && digitalCount == 1)) {
#ifdef NPB_CONF_4STEP_CADENCE
constexpr unsigned stepFactor = 4; // 4 step cadence (4 bits per pixel bit)
#else
constexpr unsigned stepFactor = 3; // 3 step cadence (3 bits per pixel bit)
#endif
unsigned i2sCommonSize = stepFactor * bus->getLength() * bus->getNumberOfChannels() * (bus->is16bit()+1);
if (i2sCommonSize > maxI2S) maxI2S = i2sCommonSize;
}
}
#endif
mem += sizeof(BusPwm);
}
return size + maxI2S;
return mem;
}

int BusManager::add(const BusConfig &bc) {
Expand All @@ -1166,7 +1148,7 @@ int BusManager::add(const BusConfig &bc) {
busses.push_back(make_unique<BusHub75Matrix>(bc));
#endif
} else if (Bus::isDigital(bc.type)) {
busses.push_back(make_unique<BusDigital>(bc, Bus::is2Pin(bc.type) ? twoPin : digital));
busses.push_back(make_unique<BusDigital>(bc));
} else if (Bus::isOnOff(bc.type)) {
busses.push_back(make_unique<BusOnOff>(bc));
} else {
Expand Down Expand Up @@ -1204,49 +1186,35 @@ String BusManager::getLEDTypesJSONString() {
return json;
}

void BusManager::useParallelOutput() {
DEBUGBUS_PRINTLN(F("Bus: Enabling parallel I2S."));
PolyBus::setParallelI2S1Output();
uint8_t BusManager::getI(uint8_t busType, const uint8_t* pins, uint8_t driverPreference) {
return PolyBus::getI(busType, pins, driverPreference);
}

bool BusManager::hasParallelOutput() {
return PolyBus::isParallelI2S1Output();
}

//do not call this method from system context (network callback)
void BusManager::removeAll() {
DEBUGBUS_PRINTLN(F("Removing all."));
//prevents crashes due to deleting busses while in use.
while (!canAllShow()) yield();
busses.clear();
PolyBus::setParallelI2S1Output(false);
#ifndef ESP8266
// Reset channel tracking for fresh allocation
PolyBus::resetChannelTracking();
#endif
}

#ifdef ESP32_DATA_IDLE_HIGH
// #2478
// If enabled, RMT idle level is set to HIGH when off
// to prevent leakage current when using an N-channel MOSFET to toggle LED power
// since I2S outputs are known only during config of buses, lets just assume RMT is used for digital buses
// unused RMT channels should have no effect
void BusManager::esp32RMTInvertIdle() {
bool idle_out;
unsigned rmt = 0;
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;
#elif defined(CONFIG_IDF_TARGET_ESP32S2) // 4 RMT, only has 1 I2S bus, supported in NPB
if (u > 3) return;
rmt = u;
#elif defined(CONFIG_IDF_TARGET_ESP32S3) // 4 RMT, has 2 I2S but NPB does not support them ATM
if (u > 3) return;
rmt = u;
#else
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 (static_cast<BusDigital*>(bus.get())->isI2S()) continue;
if (u >= WLED_MAX_RMT_CHANNELS) return;
//assumes that bus number to rmt channel mapping stays 1:1
rmt_channel_t ch = static_cast<rmt_channel_t>(rmt);
rmt_idle_level_t lvl;
Expand Down Expand Up @@ -1421,9 +1389,15 @@ void BusManager::applyABL() {

ColorOrderMap& BusManager::getColorOrderMap() { return _colorOrderMap; }


#ifndef ESP8266
// PolyBus channel tracking for dynamic allocation
bool PolyBus::_useParallelI2S = false;

uint8_t PolyBus::_rmtChannelsAssigned = 0; // number of RMT channels assigned durig getI() check
uint8_t PolyBus::_rmtChannel = 0; // number of RMT channels actually used during bus creation in create()
uint8_t PolyBus::_i2sChannelsAssigned = 0; // number of I2S channels assigned durig getI() check
uint8_t PolyBus::_parallelBusItype = 0; // type I_NONE
uint8_t PolyBus::_2PchannelsAssigned = 0;
#endif
// Bus static member definition
int16_t Bus::_cct = -1;
uint8_t Bus::_cctBlend = 0; // 0 - 127
Expand Down
Loading