Merge fixes & updates

- Segment::setName()
- S2 limits
- bus debug macros
- remove cctBlending from strip
This commit is contained in:
Blaž Kristan 2025-01-19 11:37:57 +01:00
parent 0c84235a95
commit 7daea18907
10 changed files with 129 additions and 132 deletions

View File

@ -1,3 +1,4 @@
#pragma once
/* /*
WS2812FX.h - Library for WS2812 LED effects. WS2812FX.h - Library for WS2812 LED effects.
Harm Aldick - 2016 Harm Aldick - 2016
@ -8,12 +9,15 @@
Adapted from code originally licensed under the MIT license Adapted from code originally licensed under the MIT license
Modified for WLED Modified for WLED
Segment class/struct (c) 2022 Blaz Kristan (@blazoncek)
*/ */
#ifndef WS2812FX_h #ifndef WS2812FX_h
#define WS2812FX_h #define WS2812FX_h
#include <vector> #include <vector>
#include "wled.h"
#include "const.h" #include "const.h"
#include "bus_manager.h" #include "bus_manager.h"
@ -74,16 +78,13 @@ extern byte realtimeMode; // used in getMappedPixelIndex()
#define MAX_NUM_SEGMENTS 16 #define MAX_NUM_SEGMENTS 16
/* How much data bytes all segments combined may allocate */ /* How much data bytes all segments combined may allocate */
#define MAX_SEGMENT_DATA 5120 #define MAX_SEGMENT_DATA 5120
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
#define MAX_NUM_SEGMENTS 20
#define MAX_SEGMENT_DATA (MAX_NUM_SEGMENTS*512) // 10k by default (S2 is short on free RAM)
#else #else
#ifndef MAX_NUM_SEGMENTS #define MAX_NUM_SEGMENTS 32 // warning: going beyond 32 may consume too much RAM for stable operation
#define MAX_NUM_SEGMENTS 32
#endif
#if defined(ARDUINO_ARCH_ESP32S2)
#define MAX_SEGMENT_DATA (MAX_NUM_SEGMENTS*768) // 24k by default (S2 is short on free RAM)
#else
#define MAX_SEGMENT_DATA (MAX_NUM_SEGMENTS*1280) // 40k by default #define MAX_SEGMENT_DATA (MAX_NUM_SEGMENTS*1280) // 40k by default
#endif #endif
#endif
/* How much data bytes each segment should max allocate to leave enough space for other segments, /* How much data bytes each segment should max allocate to leave enough space for other segments,
assuming each segment uses the same amount of data. 256 for ESP8266, 640 for ESP32. */ assuming each segment uses the same amount of data. 256 for ESP8266, 640 for ESP32. */
@ -543,6 +544,8 @@ typedef struct Segment {
inline uint16_t groupLength() const { return grouping + spacing; } inline uint16_t groupLength() const { return grouping + spacing; }
inline uint8_t getLightCapabilities() const { return _capabilities; } inline uint8_t getLightCapabilities() const { return _capabilities; }
inline void deactivate() { setGeometry(0,0); } inline void deactivate() { setGeometry(0,0); }
inline Segment &clearName() { if (name) free(name); name = nullptr; return *this; }
inline Segment &setName(const String &name) { return setName(name.c_str()); }
inline static unsigned getUsedSegmentData() { return Segment::_usedSegmentData; } inline static unsigned getUsedSegmentData() { return Segment::_usedSegmentData; }
inline static void addUsedSegmentData(int len) { Segment::_usedSegmentData += len; } inline static void addUsedSegmentData(int len) { Segment::_usedSegmentData += len; }
@ -565,6 +568,7 @@ typedef struct Segment {
Segment &setOption(uint8_t n, bool val); Segment &setOption(uint8_t n, bool val);
Segment &setMode(uint8_t fx, bool loadDefaults = false); Segment &setMode(uint8_t fx, bool loadDefaults = false);
Segment &setPalette(uint8_t pal); Segment &setPalette(uint8_t pal);
Segment &setName(const char* name);
uint8_t differs(const Segment& b) const; uint8_t differs(const Segment& b) const;
void refreshLightCapabilities(); void refreshLightCapabilities();
@ -736,7 +740,6 @@ class WS2812FX { // 96 bytes
WS2812FX() : WS2812FX() :
paletteFade(0), paletteFade(0),
paletteBlend(0), paletteBlend(0),
cctBlending(0),
now(millis()), now(millis()),
timebase(0), timebase(0),
isMatrix(false), isMatrix(false),
@ -839,7 +842,6 @@ class WS2812FX { // 96 bytes
uint8_t uint8_t
paletteBlend, paletteBlend,
cctBlending,
getActiveSegmentsNum() const, getActiveSegmentsNum() const,
getFirstSelectedSegId() const, getFirstSelectedSegId() const,
getLastActiveSegmentId() const, getLastActiveSegmentId() const,

View File

@ -613,6 +613,19 @@ Segment &Segment::setPalette(uint8_t pal) {
return *this; return *this;
} }
Segment &Segment::setName(const char *newName) {
if (newName) {
const int newLen = min(strlen(newName), (size_t)WLED_MAX_SEGNAME_LEN);
if (newLen) {
if (name) name = static_cast<char*>(realloc(name, newLen+1));
else name = static_cast<char*>(malloc(newLen+1));
if (name) strlcpy(name, newName, newLen);
return *this;
}
}
return clearName();
}
// 2D matrix // 2D matrix
unsigned Segment::virtualWidth() const { unsigned Segment::virtualWidth() const {
unsigned groupLen = groupLength(); unsigned groupLen = groupLength();

View File

@ -18,10 +18,11 @@
#endif #endif
#include "const.h" #include "const.h"
#include "pin_manager.h" #include "pin_manager.h"
#include "bus_wrapper.h"
#include "bus_manager.h" #include "bus_manager.h"
#include "bus_wrapper.h"
extern bool cctICused; extern bool cctICused;
extern bool useParallelI2S;
//colors.cpp //colors.cpp
uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb); uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
@ -29,28 +30,6 @@ uint32_t colorBalanceFromKelvin(uint16_t kelvin, uint32_t rgb);
//udp.cpp //udp.cpp
uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, const uint8_t* buffer, uint8_t bri=255, bool isRGBW=false); uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, const uint8_t* buffer, uint8_t bri=255, bool isRGBW=false);
// enable additional debug output
#if defined(WLED_DEBUG_HOST)
#include "net_debug.h"
#define DEBUGOUT NetDebug
#else
#define DEBUGOUT Serial
#endif
#ifdef WLED_DEBUG
#ifndef ESP8266
#include <rom/rtc.h>
#endif
#define DEBUG_PRINT(x) DEBUGOUT.print(x)
#define DEBUG_PRINTLN(x) DEBUGOUT.println(x)
#define DEBUG_PRINTF(x...) DEBUGOUT.printf(x)
#define DEBUG_PRINTF_P(x...) DEBUGOUT.printf_P(x)
#else
#define DEBUG_PRINT(x)
#define DEBUG_PRINTLN(x)
#define DEBUG_PRINTF(x...)
#define DEBUG_PRINTF_P(x...)
#endif
//color mangling macros //color mangling macros
#define RGBW32(r,g,b,w) (uint32_t((byte(w) << 24) | (byte(r) << 16) | (byte(g) << 8) | (byte(b)))) #define RGBW32(r,g,b,w) (uint32_t((byte(w) << 24) | (byte(r) << 16) | (byte(g) << 8) | (byte(b))))
@ -63,6 +42,7 @@ uint8_t realtimeBroadcast(uint8_t type, IPAddress client, uint16_t length, const
bool ColorOrderMap::add(uint16_t start, uint16_t len, uint8_t colorOrder) { 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 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}); _mappings.push_back({start,len,colorOrder});
DEBUGBUS_PRINTF_P(PSTR("Bus: Add COM (%d,%d,%d)\n"), (int)start, (int)len, (int)colorOrder);
return true; return true;
} }
@ -116,7 +96,7 @@ uint32_t Bus::autoWhiteCalc(uint32_t c) const {
} }
uint8_t *Bus::allocateData(size_t size) { uint8_t *Bus::allocateData(size_t size) {
if (_data) free(_data); // should not happen, but for safety freeData(); // should not happen, but for safety
return _data = (uint8_t *)(size>0 ? calloc(size, sizeof(uint8_t)) : nullptr); return _data = (uint8_t *)(size>0 ? calloc(size, sizeof(uint8_t)) : nullptr);
} }
@ -134,32 +114,32 @@ BusDigital::BusDigital(const BusConfig &bc, uint8_t nr, const ColorOrderMap &com
, _milliAmpsMax(bc.milliAmpsMax) , _milliAmpsMax(bc.milliAmpsMax)
, _colorOrderMap(com) , _colorOrderMap(com)
{ {
DEBUG_PRINTLN(F("Bus: Creating digital bus.")); DEBUGBUS_PRINTLN(F("Bus: Creating digital bus."));
if (!isDigital(bc.type) || !bc.count) { DEBUG_PRINTLN(F("Not digial or empty bus!")); return; } if (!isDigital(bc.type) || !bc.count) { DEBUGBUS_PRINTLN(F("Not digial or empty bus!")); return; }
if (!PinManager::allocatePin(bc.pins[0], true, PinOwner::BusDigital)) { DEBUG_PRINTLN(F("Pin 0 allocated!")); return; } if (!PinManager::allocatePin(bc.pins[0], true, PinOwner::BusDigital)) { DEBUGBUS_PRINTLN(F("Pin 0 allocated!")); return; }
_frequencykHz = 0U; _frequencykHz = 0U;
_pins[0] = bc.pins[0]; _pins[0] = bc.pins[0];
if (is2Pin(bc.type)) { if (is2Pin(bc.type)) {
if (!PinManager::allocatePin(bc.pins[1], true, PinOwner::BusDigital)) { if (!PinManager::allocatePin(bc.pins[1], true, PinOwner::BusDigital)) {
cleanup(); cleanup();
DEBUG_PRINTLN(F("Pin 1 allocated!")); DEBUGBUS_PRINTLN(F("Pin 1 allocated!"));
return; return;
} }
_pins[1] = bc.pins[1]; _pins[1] = bc.pins[1];
_frequencykHz = bc.frequency ? bc.frequency : 2000U; // 2MHz clock if undefined _frequencykHz = bc.frequency ? bc.frequency : 2000U; // 2MHz clock if undefined
} }
_iType = PolyBus::getI(bc.type, _pins, nr); _iType = PolyBus::getI(bc.type, _pins, nr);
if (_iType == I_NONE) { DEBUG_PRINTLN(F("Incorrect iType!")); return; } if (_iType == I_NONE) { DEBUGBUS_PRINTLN(F("Incorrect iType!")); return; }
_hasRgb = hasRGB(bc.type); _hasRgb = hasRGB(bc.type);
_hasWhite = hasWhite(bc.type); _hasWhite = hasWhite(bc.type);
_hasCCT = hasCCT(bc.type); _hasCCT = hasCCT(bc.type);
if (bc.doubleBuffer && !allocateData(bc.count * Bus::getNumberOfChannels(bc.type))) { DEBUG_PRINTLN(F("Buffer allocation failed!")); return; } if (bc.doubleBuffer && !allocateData(bc.count * Bus::getNumberOfChannels(bc.type))) { DEBUGBUS_PRINTLN(F("Buffer allocation failed!")); return; }
//_buffering = bc.doubleBuffer; //_buffering = bc.doubleBuffer;
uint16_t lenToCreate = bc.count; 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 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, nr);
_valid = (_busPtr != nullptr); _valid = (_busPtr != nullptr);
DEBUG_PRINTF_P(PSTR("Bus: %successfully inited #%u (len:%u, type:%u (RGB:%d, W:%d, CCT:%d), pins:%u,%u [itype:%u] mA=%d/%d)\n"), 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", _valid?"S":"Uns",
(int)nr, (int)nr,
(int)bc.count, (int)bc.count,
@ -261,6 +241,7 @@ void BusDigital::show() {
// TODO: there is an issue if CCT is calculated from RGB value (_cct==-1), we cannot do that with double buffer // TODO: there is an issue if CCT is calculated from RGB value (_cct==-1), we cannot do that with double buffer
Bus::_cct = _data[offset+channels-1]; Bus::_cct = _data[offset+channels-1];
Bus::calculateCCT(c, cctWW, cctCW); Bus::calculateCCT(c, cctWW, cctCW);
if (_type == TYPE_WS2812_WWA) c = RGBW32(cctWW, cctCW, 0, W(c));
} }
unsigned pix = i; unsigned pix = i;
if (_reversed) pix = _len - pix -1; if (_reversed) pix = _len - pix -1;
@ -346,8 +327,8 @@ void IRAM_ATTR BusDigital::setPixelColor(unsigned pix, uint32_t c) {
uint8_t cctWW = 0, cctCW = 0; uint8_t cctWW = 0, cctCW = 0;
Bus::calculateCCT(c, cctWW, cctCW); Bus::calculateCCT(c, cctWW, cctCW);
wwcw = (cctCW<<8) | cctWW; wwcw = (cctCW<<8) | cctWW;
if (_type == TYPE_WS2812_WWA) c = RGBW32(cctWW, cctCW, 0, W(c));
} }
PolyBus::setPixelColor(_busPtr, _iType, pix, c, co, wwcw); PolyBus::setPixelColor(_busPtr, _iType, pix, c, co, wwcw);
} }
} }
@ -379,11 +360,15 @@ uint32_t IRAM_ATTR BusDigital::getPixelColor(unsigned pix) const {
case 2: c = RGBW32(b, b, b, b); break; case 2: c = RGBW32(b, b, b, b); break;
} }
} }
if (_type == TYPE_WS2812_WWA) {
uint8_t w = R(c) | G(c);
c = RGBW32(w, w, 0, w);
}
return c; return c;
} }
} }
uint8_t BusDigital::getPins(uint8_t* pinArray) const { unsigned BusDigital::getPins(uint8_t* pinArray) const {
unsigned numPins = is2Pin(_type) + 1; unsigned numPins = is2Pin(_type) + 1;
if (pinArray) for (unsigned i = 0; i < numPins; i++) pinArray[i] = _pins[i]; if (pinArray) for (unsigned i = 0; i < numPins; i++) pinArray[i] = _pins[i];
return numPins; return numPins;
@ -431,7 +416,7 @@ void BusDigital::begin() {
} }
void BusDigital::cleanup() { void BusDigital::cleanup() {
DEBUG_PRINTLN(F("Digital Cleanup.")); DEBUGBUS_PRINTLN(F("Digital Cleanup."));
PolyBus::cleanup(_busPtr, _iType); PolyBus::cleanup(_busPtr, _iType);
_iType = I_NONE; _iType = I_NONE;
_valid = false; _valid = false;
@ -514,7 +499,7 @@ BusPwm::BusPwm(const BusConfig &bc)
_hasCCT = hasCCT(bc.type); _hasCCT = hasCCT(bc.type);
_data = _pwmdata; // avoid malloc() and use already allocated memory _data = _pwmdata; // avoid malloc() and use already allocated memory
_valid = true; _valid = true;
DEBUG_PRINTF_P(PSTR("%successfully inited PWM strip with type %u, frequency %u, bit depth %u and pins %u,%u,%u,%u,%u\n"), _valid?"S":"Uns", bc.type, _frequency, _depth, _pins[0], _pins[1], _pins[2], _pins[3], _pins[4]); 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]);
} }
void BusPwm::setPixelColor(unsigned pix, uint32_t c) { void BusPwm::setPixelColor(unsigned pix, uint32_t c) {
@ -630,7 +615,7 @@ void BusPwm::show() {
} }
} }
uint8_t BusPwm::getPins(uint8_t* pinArray) const { unsigned BusPwm::getPins(uint8_t* pinArray) const {
if (!_valid) return 0; if (!_valid) return 0;
unsigned numPins = numPWMPins(_type); unsigned numPins = numPWMPins(_type);
if (pinArray) for (unsigned i = 0; i < numPins; i++) pinArray[i] = _pins[i]; if (pinArray) for (unsigned i = 0; i < numPins; i++) pinArray[i] = _pins[i];
@ -683,7 +668,7 @@ BusOnOff::BusOnOff(const BusConfig &bc)
_hasCCT = false; _hasCCT = false;
_data = &_onoffdata; // avoid malloc() and use stack _data = &_onoffdata; // avoid malloc() and use stack
_valid = true; _valid = true;
DEBUG_PRINTF_P(PSTR("%successfully inited On/Off strip with pin %u\n"), _valid?"S":"Uns", _pin); DEBUGBUS_PRINTF_P(PSTR("%successfully inited On/Off strip with pin %u\n"), _valid?"S":"Uns", _pin);
} }
void BusOnOff::setPixelColor(unsigned pix, uint32_t c) { void BusOnOff::setPixelColor(unsigned pix, uint32_t c) {
@ -706,7 +691,7 @@ void BusOnOff::show() {
digitalWrite(_pin, _reversed ? !(bool)_data[0] : (bool)_data[0]); digitalWrite(_pin, _reversed ? !(bool)_data[0] : (bool)_data[0]);
} }
uint8_t BusOnOff::getPins(uint8_t* pinArray) const { unsigned BusOnOff::getPins(uint8_t* pinArray) const {
if (!_valid) return 0; if (!_valid) return 0;
if (pinArray) pinArray[0] = _pin; if (pinArray) pinArray[0] = _pin;
return 1; return 1;
@ -743,7 +728,7 @@ BusNetwork::BusNetwork(const BusConfig &bc)
_UDPchannels = _hasWhite + 3; _UDPchannels = _hasWhite + 3;
_client = IPAddress(bc.pins[0],bc.pins[1],bc.pins[2],bc.pins[3]); _client = IPAddress(bc.pins[0],bc.pins[1],bc.pins[2],bc.pins[3]);
_valid = (allocateData(_len * _UDPchannels) != nullptr); _valid = (allocateData(_len * _UDPchannels) != nullptr);
DEBUG_PRINTF_P(PSTR("%successfully inited virtual strip with type %u and IP %u.%u.%u.%u\n"), _valid?"S":"Uns", bc.type, bc.pins[0], bc.pins[1], bc.pins[2], bc.pins[3]); 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]);
} }
void BusNetwork::setPixelColor(unsigned pix, uint32_t c) { void BusNetwork::setPixelColor(unsigned pix, uint32_t c) {
@ -770,7 +755,7 @@ void BusNetwork::show() {
_broadcastLock = false; _broadcastLock = false;
} }
uint8_t BusNetwork::getPins(uint8_t* pinArray) const { unsigned BusNetwork::getPins(uint8_t* pinArray) const {
if (pinArray) for (unsigned i = 0; i < 4; i++) pinArray[i] = _client[i]; if (pinArray) for (unsigned i = 0; i < 4; i++) pinArray[i] = _client[i];
return 4; return 4;
} }
@ -791,7 +776,7 @@ std::vector<LEDType> BusNetwork::getLEDTypes() {
} }
void BusNetwork::cleanup() { void BusNetwork::cleanup() {
DEBUG_PRINTLN(F("Virtual Cleanup.")); DEBUGBUS_PRINTLN(F("Virtual Cleanup."));
_type = I_NONE; _type = I_NONE;
_valid = false; _valid = false;
freeData(); freeData();
@ -826,7 +811,6 @@ unsigned BusManager::memUsage() {
#endif #endif
#endif #endif
for (const auto &bus : busses) { for (const auto &bus : busses) {
//for (unsigned i=0; i<numBusses; i++) {
unsigned busSize = bus->getBusSize(); unsigned busSize = bus->getBusSize();
#if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(ESP8266) #if !defined(CONFIG_IDF_TARGET_ESP32C3) && !defined(ESP8266)
if (bus->isDigital() && !bus->is2Pin()) digitalCount++; if (bus->isDigital() && !bus->is2Pin()) digitalCount++;
@ -842,18 +826,18 @@ unsigned BusManager::memUsage() {
} }
int BusManager::add(const BusConfig &bc) { int BusManager::add(const BusConfig &bc) {
DEBUG_PRINTF_P(PSTR("Bus: Adding bus #%d (%d - %d >= %d)\n"), numBusses, getNumBusses(), getNumVirtualBusses(), WLED_MAX_BUSSES); DEBUGBUS_PRINTF_P(PSTR("Bus: Adding bus #%d (%d - %d >= %d)\n"), busses.size(), getNumBusses(), getNumVirtualBusses(), WLED_MAX_BUSSES);
if (getNumBusses() - getNumVirtualBusses() >= WLED_MAX_BUSSES) return -1; if (getNumBusses() - getNumVirtualBusses() >= WLED_MAX_BUSSES) return -1;
if (Bus::isVirtual(bc.type)) { if (Bus::isVirtual(bc.type)) {
busses.push_back(new BusNetwork(bc)); busses.push_back(new BusNetwork(bc));
} else if (Bus::isDigital(bc.type)) { } else if (Bus::isDigital(bc.type)) {
busses.push_back(new BusDigital(bc, numBusses, colorOrderMap)); busses.push_back(new BusDigital(bc, busses.size(), colorOrderMap));
} else if (Bus::isOnOff(bc.type)) { } else if (Bus::isOnOff(bc.type)) {
busses.push_back(new BusOnOff(bc)); busses.push_back(new BusOnOff(bc));
} else { } else {
busses.push_back(new BusPwm(bc)); busses.push_back(new BusPwm(bc));
} }
return numBusses++; return busses.size();
} }
// credit @willmmiles // credit @willmmiles
@ -882,19 +866,21 @@ String BusManager::getLEDTypesJSONString() {
} }
void BusManager::useParallelOutput() { void BusManager::useParallelOutput() {
DEBUG_PRINTLN(F("Bus: Enabling parallel I2S.")); DEBUGBUS_PRINTLN(F("Bus: Enabling parallel I2S."));
PolyBus::setParallelI2S1Output(); PolyBus::setParallelI2S1Output();
} }
bool BusManager::hasParallelOutput() {
return PolyBus::isParallelI2S1Output();
}
//do not call this method from system context (network callback) //do not call this method from system context (network callback)
void BusManager::removeAll() { void BusManager::removeAll() {
DEBUG_PRINTLN(F("Removing all.")); DEBUGBUS_PRINTLN(F("Removing all."));
//prevents crashes due to deleting busses while in use. //prevents crashes due to deleting busses while in use.
while (!canAllShow()) yield(); while (!canAllShow()) yield();
for (auto &bus : busses) delete bus; for (auto &bus : busses) delete bus;
busses.clear(); busses.clear();
numBusses = 0;
_parallelOutputs = 1;
PolyBus::setParallelI2S1Output(false); PolyBus::setParallelI2S1Output(false);
} }
@ -905,7 +891,7 @@ void BusManager::removeAll() {
void BusManager::esp32RMTInvertIdle() { void BusManager::esp32RMTInvertIdle() {
bool idle_out; bool idle_out;
unsigned rmt = 0; unsigned rmt = 0;
for (unsigned u = 0; u < numBusses(); u++) { for (unsigned u = 0; u < busses.size(); u++) {
#if defined(CONFIG_IDF_TARGET_ESP32C3) // 2 RMT, only has 1 I2S but NPB does not support it ATM #if defined(CONFIG_IDF_TARGET_ESP32C3) // 2 RMT, only has 1 I2S but NPB does not support it ATM
if (u > 1) return; if (u > 1) return;
rmt = u; rmt = u;
@ -916,9 +902,10 @@ void BusManager::esp32RMTInvertIdle() {
if (u > 3) return; if (u > 3) return;
rmt = u; rmt = u;
#else #else
if (u < _parallelOutputs) continue; unsigned numI2S = 1 + PolyBus::isParallelI2S1Output()*7;
if (u >= _parallelOutputs + 8) return; // only 8 RMT channels if (u < numI2S) continue;
rmt = u - _parallelOutputs; if (u >= numI2S + 8) return; // only 8 RMT channels
rmt = u - numI2S;
#endif #endif
if (busses[u]->getLength()==0 || !busses[u]->isDigital() || busses[u]->is2Pin()) continue; if (busses[u]->getLength()==0 || !busses[u]->isDigital() || busses[u]->is2Pin()) continue;
//assumes that bus number to rmt channel mapping stays 1:1 //assumes that bus number to rmt channel mapping stays 1:1
@ -1028,7 +1015,7 @@ uint16_t BusManager::getTotalLength() {
return len; return len;
} }
bool PolyBus::useParallelI2S = false; bool PolyBus::_useParallelI2S = false;
// Bus static member definition // Bus static member definition
int16_t Bus::_cct = -1; int16_t Bus::_cct = -1;
@ -1037,9 +1024,7 @@ uint8_t Bus::_gAWM = 255;
uint16_t BusDigital::_milliAmpsTotal = 0; uint16_t BusDigital::_milliAmpsTotal = 0;
uint8_t BusManager::numBusses = 0;
std::vector<Bus*> BusManager::busses; std::vector<Bus*> BusManager::busses;
ColorOrderMap BusManager::colorOrderMap = {}; ColorOrderMap BusManager::colorOrderMap = {};
uint16_t BusManager::_milliAmpsUsed = 0; uint16_t BusManager::_milliAmpsUsed = 0;
uint16_t BusManager::_milliAmpsMax = ABL_MILLIAMPS_DEFAULT; uint16_t BusManager::_milliAmpsMax = ABL_MILLIAMPS_DEFAULT;
uint8_t BusManager::_parallelOutputs = 1;

View File

@ -10,6 +10,29 @@
#include "pin_manager.h" #include "pin_manager.h"
#include <vector> #include <vector>
// enable additional debug output
#if defined(WLED_DEBUG_HOST)
#include "net_debug.h"
#define DEBUGOUT NetDebug
#else
#define DEBUGOUT Serial
#endif
#ifdef WLED_DEBUG_BUS
#ifndef ESP8266
#include <rom/rtc.h>
#endif
#define DEBUGBUS_PRINT(x) DEBUGOUT.print(x)
#define DEBUGBUS_PRINTLN(x) DEBUGOUT.println(x)
#define DEBUGBUS_PRINTF(x...) DEBUGOUT.printf(x)
#define DEBUGBUS_PRINTF_P(x...) DEBUGOUT.printf_P(x)
#else
#define DEBUGBUS_PRINT(x)
#define DEBUGBUS_PRINTLN(x)
#define DEBUGBUS_PRINTF(x...)
#define DEBUGBUS_PRINTF_P(x...)
#endif
//colors.cpp //colors.cpp
uint16_t approximateKelvinFromRGB(uint32_t rgb); uint16_t approximateKelvinFromRGB(uint32_t rgb);
@ -113,7 +136,7 @@ class Bus {
inline void setStart(uint16_t start) { _start = start; } inline void setStart(uint16_t start) { _start = start; }
inline void setAutoWhiteMode(uint8_t m) { if (m < 5) _autoWhiteMode = m; } inline void setAutoWhiteMode(uint8_t m) { if (m < 5) _autoWhiteMode = m; }
inline uint8_t getAutoWhiteMode() const { return _autoWhiteMode; } inline uint8_t getAutoWhiteMode() const { return _autoWhiteMode; }
inline uint32_t getNumberOfChannels() const { return hasWhite() + 3*hasRGB() + hasCCT(); } inline unsigned getNumberOfChannels() const { return hasWhite() + 3*hasRGB() + hasCCT(); }
inline uint16_t getStart() const { return _start; } inline uint16_t getStart() const { return _start; }
inline uint8_t getType() const { return _type; } inline uint8_t getType() const { return _type; }
inline bool isOk() const { return _valid; } inline bool isOk() const { return _valid; }
@ -122,8 +145,8 @@ class Bus {
inline bool containsPixel(uint16_t pix) const { return pix >= _start && pix < _start + _len; } inline bool containsPixel(uint16_t pix) const { return pix >= _start && pix < _start + _len; }
static inline std::vector<LEDType> getLEDTypes() { return {{TYPE_NONE, "", PSTR("None")}}; } // not used. just for reference for derived classes static inline std::vector<LEDType> getLEDTypes() { return {{TYPE_NONE, "", PSTR("None")}}; } // not used. just for reference for derived classes
static constexpr uint32_t getNumberOfPins(uint8_t type) { return isVirtual(type) ? 4 : isPWM(type) ? numPWMPins(type) : is2Pin(type) + 1; } // credit @PaoloTK static constexpr unsigned getNumberOfPins(uint8_t type) { return isVirtual(type) ? 4 : isPWM(type) ? numPWMPins(type) : is2Pin(type) + 1; } // credit @PaoloTK
static constexpr uint32_t getNumberOfChannels(uint8_t type) { return hasWhite(type) + 3*hasRGB(type) + hasCCT(type); } static constexpr unsigned getNumberOfChannels(uint8_t type) { return hasWhite(type) + 3*hasRGB(type) + hasCCT(type); }
static constexpr bool hasRGB(uint8_t type) { static constexpr bool hasRGB(uint8_t type) {
return !((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_ANALOG_1CH || type == TYPE_ANALOG_2CH || type == TYPE_ONOFF); return !((type >= TYPE_WS2812_1CH && type <= TYPE_WS2812_WWA) || type == TYPE_ANALOG_1CH || type == TYPE_ANALOG_2CH || type == TYPE_ONOFF);
} }
@ -211,8 +234,8 @@ class BusDigital : public Bus {
void setColorOrder(uint8_t colorOrder) override; void setColorOrder(uint8_t colorOrder) override;
[[gnu::hot]] uint32_t getPixelColor(unsigned pix) const override; [[gnu::hot]] uint32_t getPixelColor(unsigned pix) const override;
uint8_t getColorOrder() const override { return _colorOrder; } uint8_t getColorOrder() const override { return _colorOrder; }
uint8_t getPins(uint8_t* pinArray = nullptr) const override; unsigned getPins(uint8_t* pinArray = nullptr) const override;
uint8_t skippedLeds() const override { return _skip; } unsigned skippedLeds() const override { return _skip; }
uint16_t getFrequency() const override { return _frequencykHz; } uint16_t getFrequency() const override { return _frequencykHz; }
uint16_t getLEDCurrent() const override { return _milliAmpsPerLed; } uint16_t getLEDCurrent() const override { return _milliAmpsPerLed; }
uint16_t getUsedCurrent() const override { return _milliAmpsTotal; } uint16_t getUsedCurrent() const override { return _milliAmpsTotal; }
@ -258,7 +281,7 @@ class BusPwm : public Bus {
void setPixelColor(unsigned pix, uint32_t c) override; void setPixelColor(unsigned pix, uint32_t c) override;
uint32_t getPixelColor(unsigned pix) const override; //does no index check uint32_t getPixelColor(unsigned pix) const override; //does no index check
uint8_t getPins(uint8_t* pinArray = nullptr) const override; unsigned getPins(uint8_t* pinArray = nullptr) const override;
uint16_t getFrequency() const override { return _frequency; } uint16_t getFrequency() const override { return _frequency; }
unsigned getBusSize() const override { return sizeof(BusPwm); } unsigned getBusSize() const override { return sizeof(BusPwm); }
void show() override; void show() override;
@ -354,7 +377,7 @@ struct BusConfig {
type = busType & 0x7F; // bit 7 may be/is hacked to include refresh info (1=refresh in off state, 0=no refresh) type = busType & 0x7F; // bit 7 may be/is hacked to include refresh info (1=refresh in off state, 0=no refresh)
size_t nPins = Bus::getNumberOfPins(type); size_t nPins = Bus::getNumberOfPins(type);
for (size_t i = 0; i < nPins; i++) pins[i] = ppins[i]; for (size_t i = 0; i < nPins; i++) pins[i] = ppins[i];
DEBUG_PRINTF_P(PSTR("Bus: Config (%d-%d, type:%d, CO:%d, rev:%d, skip:%d, AW:%d kHz:%d, mA:%d/%d)\n"), DEBUGBUS_PRINTF_P(PSTR("Bus: Config (%d-%d, type:%d, CO:%d, rev:%d, skip:%d, AW:%d kHz:%d, mA:%d/%d)\n"),
(int)start, (int)(start+len), (int)start, (int)(start+len),
(int)type, (int)type,
(int)colorOrder, (int)colorOrder,
@ -402,6 +425,7 @@ class BusManager {
static int add(const BusConfig &bc); static int add(const BusConfig &bc);
static void useParallelOutput(); // workaround for inaccessible PolyBus static void useParallelOutput(); // workaround for inaccessible PolyBus
static bool hasParallelOutput(); // workaround for inaccessible PolyBus
//do not call this method from system context (network callback) //do not call this method from system context (network callback)
static void removeAll(); static void removeAll();
@ -425,18 +449,16 @@ class BusManager {
//semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit()) //semi-duplicate of strip.getLengthTotal() (though that just returns strip._length, calculated in finalizeInit())
static uint16_t getTotalLength(); static uint16_t getTotalLength();
static inline uint8_t getNumBusses() { return numBusses; } static inline uint8_t getNumBusses() { return busses.size(); }
static String getLEDTypesJSONString(); static String getLEDTypesJSONString();
static inline ColorOrderMap& getColorOrderMap() { return colorOrderMap; } static inline ColorOrderMap& getColorOrderMap() { return colorOrderMap; }
private: private:
static uint8_t numBusses;
static std::vector<Bus*> busses; static std::vector<Bus*> busses;
static ColorOrderMap colorOrderMap; static ColorOrderMap colorOrderMap;
static uint16_t _milliAmpsUsed; static uint16_t _milliAmpsUsed;
static uint16_t _milliAmpsMax; static uint16_t _milliAmpsMax;
static uint8_t _parallelOutputs;
#ifdef ESP32_DATA_IDLE_HIGH #ifdef ESP32_DATA_IDLE_HIGH
static void esp32RMTInvertIdle() ; static void esp32RMTInvertIdle() ;

View File

@ -4,7 +4,6 @@
//#define NPB_CONF_4STEP_CADENCE //#define NPB_CONF_4STEP_CADENCE
#include "NeoPixelBusLg.h" #include "NeoPixelBusLg.h"
#include "bus_manager.h"
//Hardware SPI Pins //Hardware SPI Pins
#define P_8266_HS_MOSI 13 #define P_8266_HS_MOSI 13
@ -332,11 +331,11 @@
//handles pointer type conversion for all possible bus types //handles pointer type conversion for all possible bus types
class PolyBus { class PolyBus {
private: private:
static bool useParallelI2S; static bool _useParallelI2S;
public: public:
static inline void setParallelI2S1Output(bool b = true) { useParallelI2S = b; } static inline void setParallelI2S1Output(bool b = true) { _useParallelI2S = b; }
static inline bool isParallelI2S1Output(void) { return useParallelI2S; } static inline bool isParallelI2S1Output(void) { return _useParallelI2S; }
// initialize SPI bus speed for DotStar methods // initialize SPI bus speed for DotStar methods
template <class T> template <class T>
@ -768,7 +767,7 @@ class PolyBus {
return true; return true;
} }
static void setPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint32_t c, uint8_t co, uint16_t wwcw = 0) { [[gnu::hot]] static void setPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint32_t c, uint8_t co, uint16_t wwcw = 0) {
uint8_t r = c >> 16; uint8_t r = c >> 16;
uint8_t g = c >> 8; uint8_t g = c >> 8;
uint8_t b = c >> 0; uint8_t b = c >> 0;
@ -985,7 +984,7 @@ class PolyBus {
} }
} }
static uint32_t getPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint8_t co) { [[gnu::hot]] static uint32_t getPixelColor(void* busPtr, uint8_t busType, uint16_t pix, uint8_t co) {
RgbwColor col(0,0,0,0); RgbwColor col(0,0,0,0);
switch (busType) { switch (busType) {
case I_NONE: break; case I_NONE: break;

View File

@ -114,8 +114,9 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(strip.correctWB, hw_led["cct"]); CJSON(strip.correctWB, hw_led["cct"]);
CJSON(strip.cctFromRgb, hw_led[F("cr")]); CJSON(strip.cctFromRgb, hw_led[F("cr")]);
CJSON(cctICused, hw_led[F("ic")]); CJSON(cctICused, hw_led[F("ic")]);
CJSON(strip.cctBlending, hw_led[F("cb")]); int cctBlending = 0;
Bus::setCCTBlend(strip.cctBlending); CJSON(cctBlending, hw_led[F("cb")]);
Bus::setCCTBlend(cctBlending);
strip.setTargetFps(hw_led["fps"]); //NOP if 0, default 42 FPS strip.setTargetFps(hw_led["fps"]); //NOP if 0, default 42 FPS
CJSON(useGlobalLedBuffer, hw_led[F("ld")]); CJSON(useGlobalLedBuffer, hw_led[F("ld")]);
@ -162,34 +163,6 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
DEBUG_PRINTF_P(PSTR("Heap before buses: %d\n"), ESP.getFreeHeap()); DEBUG_PRINTF_P(PSTR("Heap before buses: %d\n"), ESP.getFreeHeap());
int s = 0; // bus iterator int s = 0; // bus iterator
if (fromFS) BusManager::removeAll(); // can't safely manipulate busses directly in network callback if (fromFS) BusManager::removeAll(); // can't safely manipulate busses directly in network callback
unsigned mem = 0;
// determine if it is sensible to use parallel I2S outputs on ESP32 (i.e. more than 5 outputs = 1 I2S + 4 RMT)
bool useParallel = false;
#if defined(ARDUINO_ARCH_ESP32) && !defined(ARDUINO_ARCH_ESP32S2) && !defined(ARDUINO_ARCH_ESP32S3) && !defined(ARDUINO_ARCH_ESP32C3)
unsigned digitalCount = 0;
unsigned maxLedsOnBus = 0;
unsigned maxChannels = 0;
for (JsonObject elm : ins) {
unsigned type = elm["type"] | TYPE_WS2812_RGB;
unsigned len = elm["len"] | DEFAULT_LED_COUNT;
if (!Bus::isDigital(type)) continue;
if (!Bus::is2Pin(type)) {
digitalCount++;
unsigned channels = Bus::getNumberOfChannels(type);
if (len > maxLedsOnBus) maxLedsOnBus = len;
if (channels > maxChannels) maxChannels = channels;
}
}
DEBUG_PRINTF_P(PSTR("Maximum LEDs on a bus: %u\nDigital buses: %u\n"), maxLedsOnBus, digitalCount);
// we may remove 300 LEDs per bus limit when NeoPixelBus is updated beyond 2.9.0
if (maxLedsOnBus <= 300 && digitalCount > 5) {
DEBUG_PRINTLN(F("Switching to parallel I2S."));
useParallel = true;
BusManager::useParallelOutput();
mem = BusManager::memUsage(maxChannels, maxLedsOnBus, 8); // use alternate memory calculation
}
#endif
for (JsonObject elm : ins) { for (JsonObject elm : ins) {
if (s >= WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES) break; if (s >= WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES) break;
@ -816,7 +789,7 @@ void serializeConfig() {
hw_led["cct"] = strip.correctWB; hw_led["cct"] = strip.correctWB;
hw_led[F("cr")] = strip.cctFromRgb; hw_led[F("cr")] = strip.cctFromRgb;
hw_led[F("ic")] = cctICused; hw_led[F("ic")] = cctICused;
hw_led[F("cb")] = strip.cctBlending; hw_led[F("cb")] = Bus::getCCTBlend();
hw_led["fps"] = strip.getTargetFps(); hw_led["fps"] = strip.getTargetFps();
hw_led[F("rgbwm")] = Bus::getGlobalAWMode(); // global auto white mode override hw_led[F("rgbwm")] = Bus::getGlobalAWMode(); // global auto white mode override
hw_led[F("ld")] = useGlobalLedBuffer; hw_led[F("ld")] = useGlobalLedBuffer;

View File

@ -37,7 +37,7 @@
#endif #endif
#ifndef WLED_MAX_USERMODS #ifndef WLED_MAX_USERMODS
#ifdef ESP8266 #if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32S2)
#define WLED_MAX_USERMODS 4 #define WLED_MAX_USERMODS 4
#else #else
#define WLED_MAX_USERMODS 6 #define WLED_MAX_USERMODS 6
@ -59,8 +59,8 @@
#define WLED_MIN_VIRTUAL_BUSSES 4 #define WLED_MIN_VIRTUAL_BUSSES 4
#elif defined(CONFIG_IDF_TARGET_ESP32S2) // 4 RMT, 8 LEDC, only has 1 I2S bus, supported in NPB #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) // the 5th bus (I2S) will prevent Audioreactive usermod from functioning (it is last used though)
#define WLED_MAX_BUSSES 7 // will allow 5 digital & 2 analog RGB #define WLED_MAX_BUSSES 14 // will allow 12 digital & 2 analog RGB
#define WLED_MAX_DIGITAL_CHANNELS 5 #define WLED_MAX_DIGITAL_CHANNELS 12 // x4 RMT + x1/x8 I2S0
//#define WLED_MAX_ANALOG_CHANNELS 8 //#define WLED_MAX_ANALOG_CHANNELS 8
#define WLED_MIN_VIRTUAL_BUSSES 4 #define WLED_MIN_VIRTUAL_BUSSES 4
#elif defined(CONFIG_IDF_TARGET_ESP32S3) // 4 RMT, 8 LEDC, has 2 I2S but NPB supports parallel x8 LCD on I2S1 #elif defined(CONFIG_IDF_TARGET_ESP32S3) // 4 RMT, 8 LEDC, has 2 I2S but NPB supports parallel x8 LCD on I2S1
@ -115,7 +115,7 @@
#endif #endif
#endif #endif
#ifdef ESP8266 #if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32S2)
#define WLED_MAX_COLOR_ORDER_MAPPINGS 5 #define WLED_MAX_COLOR_ORDER_MAPPINGS 5
#else #else
#define WLED_MAX_COLOR_ORDER_MAPPINGS 10 #define WLED_MAX_COLOR_ORDER_MAPPINGS 10
@ -125,7 +125,7 @@
#undef WLED_MAX_LEDMAPS #undef WLED_MAX_LEDMAPS
#endif #endif
#ifndef WLED_MAX_LEDMAPS #ifndef WLED_MAX_LEDMAPS
#ifdef ESP8266 #if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32S2)
#define WLED_MAX_LEDMAPS 10 #define WLED_MAX_LEDMAPS 10
#else #else
#define WLED_MAX_LEDMAPS 16 #define WLED_MAX_LEDMAPS 16
@ -476,6 +476,8 @@
#ifndef MAX_LEDS #ifndef MAX_LEDS
#ifdef ESP8266 #ifdef ESP8266
#define MAX_LEDS 1664 //can't rely on memory limit to limit this to 1600 LEDs #define MAX_LEDS 1664 //can't rely on memory limit to limit this to 1600 LEDs
#elif defined(CONFIG_IDF_TARGET_ESP32S2)
#define MAX_LEDS 2048 //due to memory constraints
#else #else
#define MAX_LEDS 8192 #define MAX_LEDS 8192
#endif #endif
@ -485,7 +487,9 @@
#ifdef ESP8266 #ifdef ESP8266
#define MAX_LED_MEMORY 4000 #define MAX_LED_MEMORY 4000
#else #else
#if defined(ARDUINO_ARCH_ESP32S2) || defined(ARDUINO_ARCH_ESP32C3) #if defined(ARDUINO_ARCH_ESP32S2)
#define MAX_LED_MEMORY 16000
#elif defined(ARDUINO_ARCH_ESP32C3)
#define MAX_LED_MEMORY 32000 #define MAX_LED_MEMORY 32000
#else #else
#define MAX_LED_MEMORY 64000 #define MAX_LED_MEMORY 64000

View File

@ -42,10 +42,10 @@
if (loc) d.Sf.action = getURL('/settings/leds'); if (loc) d.Sf.action = getURL('/settings/leds');
} }
function bLimits(b,v,p,m,l,o=5,d=2,a=6) { 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) 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) maxD = d; // maxD - max digital channels (can be changed if using ESP32 parallel I2S): 16 - ESP32, 12 - S3/S2, 2 - C3, 3 - 8266
maxA = a; // maxA - max analog channels maxA = a; // maxA - max analog channels: 16 - ESP32, 8 - S3/S2, 6 - C3, 5 - 8266
maxV = v; // maxV - min virtual buses maxV = v; // maxV - min virtual buses: 4 - ESP32/S3, 3 - S2/C3, 2 - ESP8266
maxPB = p; // maxPB - max LEDs per bus maxPB = p; // maxPB - max LEDs per bus
maxM = m; // maxM - max LED memory maxM = m; // maxM - max LED memory
maxL = l; // maxL - max LEDs (will serve to determine ESP >1664 == ESP32) maxL = l; // maxL - max LEDs (will serve to determine ESP >1664 == ESP32)
@ -250,6 +250,7 @@
} }
// enable/disable LED fields // enable/disable LED fields
let dC = 0; // count of digital buses (for parallel I2S)
let LTs = d.Sf.querySelectorAll("#mLC select[name^=LT]"); let LTs = d.Sf.querySelectorAll("#mLC select[name^=LT]");
LTs.forEach((s,i)=>{ LTs.forEach((s,i)=>{
if (i < LTs.length-1) s.disabled = true; // prevent changing type (as we can't update options) if (i < LTs.length-1) s.disabled = true; // prevent changing type (as we can't update options)
@ -257,6 +258,7 @@
var n = s.name.substring(2); var n = s.name.substring(2);
var t = parseInt(s.value); var t = parseInt(s.value);
memu += getMem(t, n); // calc memory memu += getMem(t, n); // calc memory
dC += (isDig(t) && !isD2P(t));
setPinConfig(n,t); setPinConfig(n,t);
gId("abl"+n).style.display = (!abl || !isDig(t)) ? "none" : "inline"; // show/hide individual ABL settings gId("abl"+n).style.display = (!abl || !isDig(t)) ? "none" : "inline"; // show/hide individual ABL settings
if (change) { // did we change LED type? if (change) { // did we change LED type?
@ -295,7 +297,6 @@
// do we have a led count field // do we have a led count field
if (nm=="LC") { if (nm=="LC") {
let c = parseInt(LC.value,10); //get LED count let c = parseInt(LC.value,10); //get LED count
if (c > 300 && i < 8) maxB = oMaxB - Math.max(maxD-7,0); //TODO: hard limit for buses when using ESP32 parallel I2S
if (!customStarts || !startsDirty[n]) gId("ls"+n).value = sLC; //update start value if (!customStarts || !startsDirty[n]) gId("ls"+n).value = sLC; //update start value
gId("ls"+n).disabled = !customStarts; //enable/disable field editing gId("ls"+n).disabled = !customStarts; //enable/disable field editing
if (c) { if (c) {
@ -481,14 +482,13 @@ mA/LED: <select name="LAsel${s}" onchange="enLA(this,'${s}');UI();">
} }
} }
}); });
enLA(d.Sf["LAsel"+s],s); // update LED mA
// disable inappropriate LED types // disable inappropriate LED types
let sel = d.getElementsByName("LT"+s)[0] let sel = d.getElementsByName("LT"+s)[0]
if (i >= maxB || digitalB >= maxD) disable(sel,'option[data-type="D"]'); // NOTE: see isDig() if (i >= maxB || digitalB >= maxD) disable(sel,'option[data-type="D"]'); // NOTE: see isDig()
if (i >= maxB || twopinB >= 1) disable(sel,'option[data-type="2P"]'); // NOTE: see isD2P() if (i >= maxB || twopinB >= 1) disable(sel,'option[data-type="2P"]'); // NOTE: see isD2P()
disable(sel,`option[data-type^="${'A'.repeat(maxA-analogB+1)}"]`); // NOTE: see isPWM() disable(sel,`option[data-type^="${'A'.repeat(maxA-analogB+1)}"]`); // NOTE: see isPWM()
sel.selectedIndex = sel.querySelector('option:not(:disabled)').index; sel.selectedIndex = sel.querySelector('option:not(:disabled)').index;
// initialize current limiter
enLA(d.Sf["LAsel"+s],s);
} }
if (n==-1) { if (n==-1) {
o[--i].remove();--i; o[--i].remove();--i;

View File

@ -134,8 +134,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
strip.correctWB = request->hasArg(F("CCT")); strip.correctWB = request->hasArg(F("CCT"));
strip.cctFromRgb = request->hasArg(F("CR")); strip.cctFromRgb = request->hasArg(F("CR"));
cctICused = request->hasArg(F("IC")); cctICused = request->hasArg(F("IC"));
strip.cctBlending = request->arg(F("CB")).toInt(); Bus::setCCTBlend(request->arg(F("CB")).toInt());
Bus::setCCTBlend(strip.cctBlending);
Bus::setGlobalAWMode(request->arg(F("AW")).toInt()); Bus::setGlobalAWMode(request->arg(F("AW")).toInt());
strip.setTargetFps(request->arg(F("FR")).toInt()); strip.setTargetFps(request->arg(F("FR")).toInt());
useGlobalLedBuffer = request->hasArg(F("LD")); useGlobalLedBuffer = request->hasArg(F("LD"));

View File

@ -285,7 +285,7 @@ void getSettingsJS(byte subPage, Print& settingsScript)
printSetFormCheckbox(settingsScript,PSTR("CCT"),strip.correctWB); printSetFormCheckbox(settingsScript,PSTR("CCT"),strip.correctWB);
printSetFormCheckbox(settingsScript,PSTR("IC"),cctICused); printSetFormCheckbox(settingsScript,PSTR("IC"),cctICused);
printSetFormCheckbox(settingsScript,PSTR("CR"),strip.cctFromRgb); printSetFormCheckbox(settingsScript,PSTR("CR"),strip.cctFromRgb);
printSetFormValue(settingsScript,PSTR("CB"),strip.cctBlending); printSetFormValue(settingsScript,PSTR("CB"),Bus::getCCTBlend());
printSetFormValue(settingsScript,PSTR("FR"),strip.getTargetFps()); printSetFormValue(settingsScript,PSTR("FR"),strip.getTargetFps());
printSetFormValue(settingsScript,PSTR("AW"),Bus::getGlobalAWMode()); printSetFormValue(settingsScript,PSTR("AW"),Bus::getGlobalAWMode());
printSetFormCheckbox(settingsScript,PSTR("LD"),useGlobalLedBuffer); printSetFormCheckbox(settingsScript,PSTR("LD"),useGlobalLedBuffer);