Merge pull request #4529 from blazoncek/unlimited-bus

"Unlimited" buses
This commit is contained in:
Blaž Kristan 2025-03-08 12:43:25 +01:00 committed by GitHub
commit b27a4a94e4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 217 additions and 215 deletions

View File

@ -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; i<BusManager::getNumBusses(); i++) {
Bus *bus = BusManager::getBus(i);
if (bus == nullptr) continue;
if (bus->getStart() + 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; b<BusManager::getNumBusses(); b++) {
Bus *bus = BusManager::getBus(b);
const Bus *bus = BusManager::getBus(b);
if (!bus || !bus->isOk()) break;
if (seg.start == bus->getStart() && seg.stop == bus->getStart() + bus->getLength()) aligned = true;
}
if (seg.start == 0 && seg.stop == _length) aligned = true;

View File

@ -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;
}
@ -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);
}
@ -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
@ -648,7 +640,7 @@ std::vector<LEDType> 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<LEDType> 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<BusNetwork>(bc)); // when C++ >11
busses.push_back(new BusNetwork(bc));
busses.push_back(make_unique<BusNetwork>(bc));
//busses.push_back(new BusNetwork(bc));
} else if (Bus::isDigital(bc.type)) {
//busses.push_back(std::make_unique<BusDigital>(bc, numDigital, colorOrderMap));
busses.push_back(new BusDigital(bc, numDigital, colorOrderMap));
busses.push_back(make_unique<BusDigital>(bc, numDigital));
//busses.push_back(new BusDigital(bc, numDigital));
} else if (Bus::isOnOff(bc.type)) {
//busses.push_back(std::make_unique<BusOnOff>(bc));
busses.push_back(new BusOnOff(bc));
busses.push_back(make_unique<BusOnOff>(bc));
//busses.push_back(new BusOnOff(bc));
} else {
//busses.push_back(std::make_unique<BusPwm>(bc));
busses.push_back(new BusPwm(bc));
busses.push_back(make_unique<BusPwm>(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<BusDigital*>(bus);
b->begin();
BusDigital &b = static_cast<BusDigital&>(*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<std::unique_ptr<Bus>> BusManager::busses;
std::vector<Bus*> BusManager::busses;
ColorOrderMap BusManager::colorOrderMap = {};
uint16_t BusManager::_milliAmpsUsed = 0;
uint16_t BusManager::_milliAmpsMax = ABL_MILLIAMPS_DEFAULT;
std::vector<std::unique_ptr<Bus>> BusManager::busses;
//std::vector<Bus*> BusManager::busses;
uint16_t BusManager::_gMilliAmpsUsed = 0;
uint16_t BusManager::_gMilliAmpsMax = ABL_MILLIAMPS_DEFAULT;

View File

@ -11,6 +11,18 @@
#include <vector>
#include <memory>
#if __cplusplus >= 201402L
using std::make_unique;
#else
// Really simple C++11 shim for non-array case; implementation from cppreference.com
template<class T, class... Args>
std::unique_ptr<T>
make_unique(Args&&... args)
{
return std::unique_ptr<T>(new T(std::forward<Args>(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;
@ -255,8 +263,8 @@ class BusDigital : public Bus {
uint16_t _frequencykHz;
uint8_t _milliAmpsPerLed;
uint16_t _milliAmpsMax;
uint8_t *_data;
void *_busPtr;
const ColorOrderMap &_colorOrderMap;
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<LEDType> 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<LEDType> 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; }
static int add(const BusConfig &bc);
static void useParallelOutput(); // workaround for inaccessible PolyBus
static bool hasParallelOutput(); // 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(); }
static Bus* getBus(uint8_t busNr);
//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();
static inline ColorOrderMap& getColorOrderMap() { return colorOrderMap; }
private:
//static std::vector<std::unique_ptr<Bus>> busses; // we'd need C++ >11
static std::vector<Bus*> busses;
static ColorOrderMap colorOrderMap;
static uint16_t _milliAmpsUsed;
static uint16_t _milliAmpsMax;
extern std::vector<std::unique_ptr<Bus>> busses;
//extern std::vector<Bus*> busses;
extern uint16_t _gMilliAmpsUsed;
extern uint16_t _gMilliAmpsMax;
#ifdef ESP32_DATA_IDLE_HIGH
static void esp32RMTInvertIdle() ;
void esp32RMTInvertIdle() ;
#endif
static uint8_t getNumVirtualBusses() {
int j = 0;
inline size_t getNumVirtualBusses() {
size_t j = 0;
for (const auto &bus : busses) j += bus->isVirtual();
return j;
}
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;}
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(size_t busNr) { return busNr < busses.size() ? busses[busNr].get() : nullptr; }
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) {
unsigned len = 0;
for (const auto &bus : busses) if (!(bus->isVirtual() && onlyPhysical)) len += bus->getLength();
return len;
}
String getLEDTypesJSONString();
ColorOrderMap& getColorOrderMap();
};
#endif

View File

@ -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),
@ -858,8 +859,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;

View File

@ -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

View File

@ -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: <select name="LAsel${s}" onchange="enLA(this,'${s}');UI();">
o[i].querySelector("[name^=LT]").disabled = false;
}
gId("+").style.display = (i<maxB+maxV-1) ? "inline":"none";
gId("+").style.display = (i<35) ? "inline":"none"; // was maxB+maxV-1 when virtual buses were limited (now :"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")
gId("-").style.display = (i>0) ? "inline":"none";
if (!init) {
@ -614,22 +615,32 @@ Swap: <select id="xw${s}" name="XW${s}">
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<maxb+maxV when number of virtual buses was limited (now :"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ")
let l = c.hw.led;
l.ins.forEach((v,i,a)=>{
addLEDs(1);
for (var j=0; j<v.pin.length; j++) d.getElementsByName(`L${j}${i}`)[0].value = v.pin[j];
d.getElementsByName("LT"+i)[0].value = v.type;
d.getElementsByName("LS"+i)[0].value = v.start;
d.getElementsByName("LC"+i)[0].value = v.len;
d.getElementsByName("CO"+i)[0].value = v.order;
d.getElementsByName("CO"+i)[0].value = v.order & 0x0F;
d.getElementsByName("SL"+i)[0].value = v.skip;
d.getElementsByName("RF"+i)[0].checked = v.ref;
d.getElementsByName("CV"+i)[0].checked = v.rev;
d.getElementsByName("AW"+i)[0].value = v.rgbwm;
d.getElementsByName("WO"+i)[0].value = (v.order>>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: <select id="xw${s}" name="XW${s}">
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;
}
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 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;
}
let li = c.light;
if (li) {
d.getElementsByName("MS")[0].checked = li.aseg;
}
UI();
}

View File

@ -145,7 +145,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
#endif
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
@ -161,7 +161,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++) {
@ -212,7 +212,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
busConfigs.push_back(std::move(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

View File

@ -895,7 +895,6 @@ 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 std::vector<BusConfig> busConfigs; //temporary, to remember values from network callback until after
WLED_GLOBAL bool doInitBusses _INIT(false);

View File

@ -275,7 +275,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,
@ -296,8 +296,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
@ -316,7 +316,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());
@ -361,7 +361,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);
}