Skip to content

Commit adead9b

Browse files
committed
Bus wrapper modifications
- NeoPixelBus update 2.8.3 - automatic selection of appropriate I2S bus (`X1xxxxxxMethod`) - removed I2S0 on ESP32 (used by AudioReactive) - renumbered internal bus numbers (iType) - added buffer size reporting Bus modifications - WWA strip support - bus initialisation rewrite - optional parallel I2S (ESP32, S2 & S3)
1 parent 8873832 commit adead9b

File tree

12 files changed

+548
-545
lines changed

12 files changed

+548
-545
lines changed

platformio.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ lib_compat_mode = strict
138138
lib_deps =
139139
fastled/FastLED @ 3.6.0
140140
IRremoteESP8266 @ 2.8.2
141-
makuna/NeoPixelBus @ 2.8.0
141+
makuna/NeoPixelBus @ 2.8.3
142142
#https://github.com/makuna/NeoPixelBus.git#CoreShaderBeta
143143
https://github.com/Aircoookie/ESPAsyncWebServer.git#v2.2.1
144144
# for I2C interface

wled00/FX_fcn.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1219,6 +1219,50 @@ void WS2812FX::finalizeInit() {
12191219

12201220
_hasWhiteChannel = _isOffRefreshRequired = false;
12211221

1222+
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3)
1223+
// determine if it is sensible to use parallel I2S outputs on ESP32 (i.e. more than 5 outputs = 1 I2S + 4 RMT)
1224+
unsigned digitalCount = 0;
1225+
unsigned maxLedsOnBus = 0;
1226+
//unsigned maxChannels = 0;
1227+
for (unsigned i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
1228+
if (busConfigs[i] == nullptr) break;
1229+
if (Bus::isDigital(busConfigs[i]->type) && !Bus::is2Pin(busConfigs[i]->type)) {
1230+
digitalCount++;
1231+
if (busConfigs[i]->count > maxLedsOnBus) maxLedsOnBus = busConfigs[i]->count;
1232+
//unsigned channels = Bus::getNumberOfChannels(busConfigs[i]->type);
1233+
//if (channels > maxChannels) maxChannels = channels;
1234+
}
1235+
}
1236+
DEBUG_PRINTF_P(PSTR("Maximum LEDs on a bus: %u\nDigital buses: %u\n"), maxLedsOnBus, digitalCount);
1237+
// we may remove 300 LEDs per bus limit when NeoPixelBus is updated beyond 2.9.0
1238+
if (maxLedsOnBus <= 300 && useParallelI2S) BusManager::useParallelOutput(); // must call before creating buses
1239+
else useParallelI2S = false; // enforce single I2S
1240+
#endif
1241+
1242+
// create buses/outputs
1243+
unsigned mem = 0;
1244+
for (unsigned i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) {
1245+
if (busConfigs[i] == nullptr) break;
1246+
#if defined(ARDUINO_ARCH_ESP32) && !defined(CONFIG_IDF_TARGET_ESP32C3)
1247+
#if defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32S2)
1248+
// TODO: once I2S memory is larger than RMT it will ignore RMT
1249+
if (BusManager::hasParallelOutput() && i > 3) { // will use RMT and then x8 I2S
1250+
unsigned memT = BusManager::memUsage(*busConfigs[i]); // includes x8 memory allocation for parallel I2S
1251+
if (memT > mem) mem = memT; // if we have unequal LED count use the largest
1252+
} else
1253+
#else // classic ESP32
1254+
if (BusManager::hasParallelOutput() && i < 8) { // 1-8 are RMT if using x1 I2S
1255+
unsigned memT = BusManager::memUsage(*busConfigs[i]); // includes x8 memory allocation for parallel I2S
1256+
if (memT > mem) mem = memT; // if we have unequal LED count use the largest
1257+
} else
1258+
#endif
1259+
#endif
1260+
mem += BusManager::memUsage(*busConfigs[i]); // includes global buffer
1261+
if (mem <= MAX_LED_MEMORY) BusManager::add(*busConfigs[i]);
1262+
delete busConfigs[i];
1263+
busConfigs[i] = nullptr;
1264+
}
1265+
12221266
//if busses failed to load, add default (fresh install, FS issue, ...)
12231267
if (BusManager::getNumBusses() == 0) {
12241268
DEBUG_PRINTLN(F("No busses, init default"));
@@ -1298,9 +1342,11 @@ void WS2812FX::finalizeInit() {
12981342
if (Bus::isPWM(dataType) || Bus::isOnOff(dataType)) count = 1;
12991343
prevLen += count;
13001344
BusConfig defCfg = BusConfig(dataType, defPin, start, count, DEFAULT_LED_COLOR_ORDER, false, 0, RGBW_MODE_MANUAL_ONLY, 0, useGlobalLedBuffer);
1345+
mem += BusManager::memUsage(defCfg);
13011346
if (BusManager::add(defCfg) == -1) break;
13021347
}
13031348
}
1349+
DEBUG_PRINTF_P(PSTR("LED buffer size: %uB/%uB\n"), mem, BusManager::getTotalBuffers());
13041350

13051351
_length = 0;
13061352
for (int i=0; i<BusManager::getNumBusses(); i++) {
@@ -1317,6 +1363,7 @@ void WS2812FX::finalizeInit() {
13171363
// This must be done after all buses have been created, as some kinds (parallel I2S) interact
13181364
bus->begin();
13191365
}
1366+
DEBUG_PRINTF_P(PSTR("Heap after buses: %d\n"), ESP.getFreeHeap());
13201367

13211368
Segment::maxWidth = _length;
13221369
Segment::maxHeight = 1;

wled00/bus_manager.cpp

Lines changed: 45 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@
1818
#endif
1919
#include "const.h"
2020
#include "pin_manager.h"
21-
#include "bus_wrapper.h"
2221
#include "bus_manager.h"
22+
#include "bus_wrapper.h"
2323

2424
extern bool cctICused;
25+
extern bool useParallelI2S;
2526

2627
//colors.cpp
2728
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
@@ -121,7 +122,7 @@ uint8_t *Bus::allocateData(size_t size) {
121122
}
122123

123124

124-
BusDigital::BusDigital(BusConfig &bc, uint8_t nr, const ColorOrderMap &com)
125+
BusDigital::BusDigital(const BusConfig &bc, uint8_t nr, const ColorOrderMap &com)
125126
: Bus(bc.type, bc.start, bc.autoWhite, bc.count, bc.reversed, (bc.refreshReq || bc.type == TYPE_TM1814))
126127
, _skip(bc.skipAmount) //sacrificial pixels
127128
, _colorOrder(bc.colorOrder)
@@ -220,7 +221,7 @@ void BusDigital::show() {
220221
if (!_valid) return;
221222

222223
uint8_t cctWW = 0, cctCW = 0;
223-
unsigned newBri = estimateCurrentAndLimitBri(); // will fill _milliAmpsTotal
224+
unsigned newBri = estimateCurrentAndLimitBri(); // will fill _milliAmpsTotal (TODO: could use PolyBus::CalcTotalMilliAmpere())
224225
if (newBri < _bri) PolyBus::setBrightness(_busPtr, _iType, newBri); // limit brightness to stay within current limits
225226

226227
if (_data) {
@@ -246,6 +247,7 @@ void BusDigital::show() {
246247
// TODO: there is an issue if CCT is calculated from RGB value (_cct==-1), we cannot do that with double buffer
247248
Bus::_cct = _data[offset+channels-1];
248249
Bus::calculateCCT(c, cctWW, cctCW);
250+
if (_type == TYPE_WS2812_WWA) c = RGBW32(cctWW, cctCW, 0, W(c)); // may need swapping
249251
}
250252
unsigned pix = i;
251253
if (_reversed) pix = _len - pix -1;
@@ -331,8 +333,8 @@ void IRAM_ATTR BusDigital::setPixelColor(unsigned pix, uint32_t c) {
331333
uint8_t cctWW = 0, cctCW = 0;
332334
Bus::calculateCCT(c, cctWW, cctCW);
333335
wwcw = (cctCW<<8) | cctWW;
336+
if (_type == TYPE_WS2812_WWA) c = RGBW32(cctWW, cctCW, 0, W(c)); // may need swapping
334337
}
335-
336338
PolyBus::setPixelColor(_busPtr, _iType, pix, c, co, wwcw);
337339
}
338340
}
@@ -364,16 +366,24 @@ uint32_t IRAM_ATTR BusDigital::getPixelColor(unsigned pix) const {
364366
case 2: c = RGBW32(b, b, b, b); break;
365367
}
366368
}
369+
if (_type == TYPE_WS2812_WWA) {
370+
uint8_t w = R(c) | G(c);
371+
c = RGBW32(w, w, 0, w);
372+
}
367373
return c;
368374
}
369375
}
370376

371-
uint8_t BusDigital::getPins(uint8_t* pinArray) const {
377+
unsigned BusDigital::getPins(uint8_t* pinArray) const {
372378
unsigned numPins = is2Pin(_type) + 1;
373379
if (pinArray) for (unsigned i = 0; i < numPins; i++) pinArray[i] = _pins[i];
374380
return numPins;
375381
}
376382

383+
unsigned BusDigital::getBufferSize() const {
384+
return isOk() ? PolyBus::getDataSize(_busPtr, _iType) : 0;
385+
}
386+
377387
void BusDigital::setColorOrder(uint8_t colorOrder) {
378388
// upper nibble contains W swap information
379389
if ((colorOrder & 0x0F) > 5) return;
@@ -397,7 +407,7 @@ std::vector<LEDType> BusDigital::getLEDTypes() {
397407
{TYPE_SM16825, "D", PSTR("SM16825 RGBCW")},
398408
{TYPE_WS2812_1CH_X3, "D", PSTR("WS2811 White")},
399409
//{TYPE_WS2812_2CH_X3, "D", PSTR("WS2811 CCT")}, // not implemented
400-
//{TYPE_WS2812_WWA, "D", PSTR("WS2811 WWA")}, // not implemented
410+
{TYPE_WS2812_WWA, "D", PSTR("WS2811 WWA")}, // amber ignored
401411
{TYPE_WS2801, "2P", PSTR("WS2801")},
402412
{TYPE_APA102, "2P", PSTR("APA102")},
403413
{TYPE_LPD8806, "2P", PSTR("LPD8806")},
@@ -418,8 +428,9 @@ void BusDigital::cleanup() {
418428
_valid = false;
419429
_busPtr = nullptr;
420430
if (_data != nullptr) freeData();
421-
PinManager::deallocatePin(_pins[1], PinOwner::BusDigital);
422-
PinManager::deallocatePin(_pins[0], PinOwner::BusDigital);
431+
PinManager::deallocateMultiplePins(_pins, 2, PinOwner::BusDigital);
432+
//PinManager::deallocatePin(_pins[1], PinOwner::BusDigital);
433+
//PinManager::deallocatePin(_pins[0], PinOwner::BusDigital);
423434
}
424435

425436

@@ -448,7 +459,7 @@ void BusDigital::cleanup() {
448459
#endif
449460
#endif
450461

451-
BusPwm::BusPwm(BusConfig &bc)
462+
BusPwm::BusPwm(const BusConfig &bc)
452463
: Bus(bc.type, bc.start, bc.autoWhite, 1, bc.reversed, bc.refreshReq) // hijack Off refresh flag to indicate usage of dithering
453464
{
454465
if (!isPWM(bc.type)) return;
@@ -610,7 +621,7 @@ void BusPwm::show() {
610621
}
611622
}
612623

613-
uint8_t BusPwm::getPins(uint8_t* pinArray) const {
624+
unsigned BusPwm::getPins(uint8_t* pinArray) const {
614625
if (!_valid) return 0;
615626
unsigned numPins = numPWMPins(_type);
616627
if (pinArray) for (unsigned i = 0; i < numPins; i++) pinArray[i] = _pins[i];
@@ -646,7 +657,7 @@ void BusPwm::deallocatePins() {
646657
}
647658

648659

649-
BusOnOff::BusOnOff(BusConfig &bc)
660+
BusOnOff::BusOnOff(const BusConfig &bc)
650661
: Bus(bc.type, bc.start, bc.autoWhite, 1, bc.reversed)
651662
, _onoffdata(0)
652663
{
@@ -686,7 +697,7 @@ void BusOnOff::show() {
686697
digitalWrite(_pin, _reversed ? !(bool)_data[0] : (bool)_data[0]);
687698
}
688699

689-
uint8_t BusOnOff::getPins(uint8_t* pinArray) const {
700+
unsigned BusOnOff::getPins(uint8_t* pinArray) const {
690701
if (!_valid) return 0;
691702
if (pinArray) pinArray[0] = _pin;
692703
return 1;
@@ -699,7 +710,7 @@ std::vector<LEDType> BusOnOff::getLEDTypes() {
699710
};
700711
}
701712

702-
BusNetwork::BusNetwork(BusConfig &bc)
713+
BusNetwork::BusNetwork(const BusConfig &bc)
703714
: Bus(bc.type, bc.start, bc.autoWhite, bc.count)
704715
, _broadcastLock(false)
705716
{
@@ -750,7 +761,7 @@ void BusNetwork::show() {
750761
_broadcastLock = false;
751762
}
752763

753-
uint8_t BusNetwork::getPins(uint8_t* pinArray) const {
764+
unsigned BusNetwork::getPins(uint8_t* pinArray) const {
754765
if (pinArray) for (unsigned i = 0; i < 4; i++) pinArray[i] = _client[i];
755766
return 4;
756767
}
@@ -778,7 +789,7 @@ void BusNetwork::cleanup() {
778789

779790

780791
//utility to get the approx. memory usage of a given BusConfig
781-
uint32_t BusManager::memUsage(BusConfig &bc) {
792+
uint32_t BusManager::memUsage(const BusConfig &bc) {
782793
if (Bus::isOnOff(bc.type) || Bus::isPWM(bc.type)) return OUTPUT_MAX_PINS;
783794

784795
unsigned len = bc.count + bc.skipAmount;
@@ -791,19 +802,23 @@ uint32_t BusManager::memUsage(BusConfig &bc) {
791802
multiplier = 5;
792803
}
793804
#else //ESP32 RMT uses double buffer, parallel I2S uses 8x buffer (3 times)
794-
multiplier = PolyBus::isParallelI2S1Output() ? 24 : 2;
805+
#ifndef CONFIG_IDF_TARGET_ESP32C3
806+
multiplier = useParallelI2S ? 24 : 2;
807+
#else
808+
multiplier = 2;
809+
#endif
795810
#endif
796811
}
797812
return (len * multiplier + bc.doubleBuffer * (bc.count + bc.skipAmount)) * channels;
798813
}
799814

800-
uint32_t BusManager::memUsage(unsigned maxChannels, unsigned maxCount, unsigned minBuses) {
801-
//ESP32 RMT uses double buffer, parallel I2S uses 8x buffer (3 times)
802-
unsigned multiplier = PolyBus::isParallelI2S1Output() ? 3 : 2;
803-
return (maxChannels * maxCount * minBuses * multiplier);
815+
unsigned BusManager::getTotalBuffers() {
816+
unsigned size = 0;
817+
for (unsigned i=0; i<numBusses; i++) size += busses[i]->getBufferSize();
818+
return size;
804819
}
805820

806-
int BusManager::add(BusConfig &bc) {
821+
int BusManager::add(const BusConfig &bc) {
807822
if (getNumBusses() - getNumVirtualBusses() >= WLED_MAX_BUSSES) return -1;
808823
if (Bus::isVirtual(bc.type)) {
809824
busses[numBusses] = new BusNetwork(bc);
@@ -843,18 +858,20 @@ String BusManager::getLEDTypesJSONString() {
843858
}
844859

845860
void BusManager::useParallelOutput() {
846-
_parallelOutputs = 8; // hardcoded since we use NPB I2S x8 methods
847861
PolyBus::setParallelI2S1Output();
848862
}
849863

864+
bool BusManager::hasParallelOutput() {
865+
return PolyBus::isParallelI2S1Output();
866+
}
867+
850868
//do not call this method from system context (network callback)
851869
void BusManager::removeAll() {
852870
DEBUG_PRINTLN(F("Removing all."));
853871
//prevents crashes due to deleting busses while in use.
854872
while (!canAllShow()) yield();
855873
for (unsigned i = 0; i < numBusses; i++) delete busses[i];
856874
numBusses = 0;
857-
_parallelOutputs = 1;
858875
PolyBus::setParallelI2S1Output(false);
859876
}
860877

@@ -876,9 +893,10 @@ void BusManager::esp32RMTInvertIdle() {
876893
if (u > 3) return;
877894
rmt = u;
878895
#else
879-
if (u < _parallelOutputs) continue;
880-
if (u >= _parallelOutputs + 8) return; // only 8 RMT channels
881-
rmt = u - _parallelOutputs;
896+
unsigned numI2S = 1 + PolyBus::isParallelI2S1Output()*7;
897+
if (u < numI2S) continue;
898+
if (u >= numI2S + 8) return; // only 8 RMT channels
899+
rmt = u - numI2S;
882900
#endif
883901
if (busses[u]->getLength()==0 || !busses[u]->isDigital() || busses[u]->is2Pin()) continue;
884902
//assumes that bus number to rmt channel mapping stays 1:1
@@ -994,7 +1012,7 @@ uint16_t BusManager::getTotalLength() {
9941012
return len;
9951013
}
9961014

997-
bool PolyBus::useParallelI2S = false;
1015+
bool PolyBus::_useParallelI2S = false;
9981016

9991017
// Bus static member definition
10001018
int16_t Bus::_cct = -1;
@@ -1008,4 +1026,3 @@ Bus* BusManager::busses[WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES];
10081026
ColorOrderMap BusManager::colorOrderMap = {};
10091027
uint16_t BusManager::_milliAmpsUsed = 0;
10101028
uint16_t BusManager::_milliAmpsMax = ABL_MILLIAMPS_DEFAULT;
1011-
uint8_t BusManager::_parallelOutputs = 1;

0 commit comments

Comments
 (0)