From 6655e2664e81e59a5ffbedf3a4121ab0f7c61d95 Mon Sep 17 00:00:00 2001 From: PaoloTK Date: Thu, 29 Aug 2024 13:18:48 +0200 Subject: [PATCH 01/14] rebase to bus-config --- wled00/FX_fcn.cpp | 39 +++++++++++++++++++++++++++------------ wled00/bus_manager.cpp | 2 +- wled00/bus_manager.h | 9 +++------ wled00/const.h | 7 +++++++ 4 files changed, 38 insertions(+), 19 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index a06d4f843..69a742c3d 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -44,8 +44,8 @@ */ //factory defaults LED setup -//#define PIXEL_COUNTS 30, 30, 30, 30 -//#define DATA_PINS 16, 1, 3, 4 +//#define PIXEL_COUNTS 30 +//#define DATA_PINS 2 (8266/C3) or 16 //#define DEFAULT_LED_TYPE TYPE_WS2812_RGB #ifndef PIXEL_COUNTS @@ -56,8 +56,8 @@ #define DATA_PINS LEDPIN #endif -#ifndef DEFAULT_LED_TYPE - #define DEFAULT_LED_TYPE TYPE_WS2812_RGB +#ifndef LED_TYPES + #define LED_TYPES DEFAULT_LED_TYPE #endif #ifndef DEFAULT_LED_COLOR_ORDER @@ -1215,17 +1215,30 @@ void WS2812FX::finalizeInit(void) { //if busses failed to load, add default (fresh install, FS issue, ...) if (BusManager::getNumBusses() == 0) { DEBUG_PRINTLN(F("No busses, init default")); + constexpr unsigned defDataTypes[] = {LED_TYPES}; const unsigned defDataPins[] = {DATA_PINS}; const unsigned defCounts[] = {PIXEL_COUNTS}; - const unsigned defNumPins = ((sizeof defDataPins) / (sizeof defDataPins[0])); + const unsigned defNumTypes = ((sizeof defDataTypes) / (sizeof defDataTypes[0])); + constexpr unsigned defNumPins = ((sizeof defDataPins) / (sizeof defDataPins[0])); const unsigned defNumCounts = ((sizeof defCounts) / (sizeof defCounts[0])); - // if number of pins is divisible by counts, use number of counts to determine number of buses, otherwise use pins - const unsigned defNumBusses = defNumPins > defNumCounts && defNumPins%defNumCounts == 0 ? defNumCounts : defNumPins; - const unsigned pinsPerBus = defNumPins / defNumBusses; + + static_assert(Bus::getNumberOfPins(defDataTypes[0]) <= defNumPins, + "The first LED type configured requires more pins than have been defined!"); + unsigned prevLen = 0; - for (unsigned i = 0; i < defNumBusses && i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) { - uint8_t defPin[5]; // max 5 pins - for (unsigned j = 0; j < pinsPerBus; j++) defPin[j] = defDataPins[i*pinsPerBus + j]; + unsigned pinsIndex = 0; + for (unsigned i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) { + uint8_t defPin[OUTPUT_MAX_PINS]; // max 5 pins + // if we have less types than requested outputs and they do not align, use last known type to set current type + unsigned dataType = defDataTypes[(i < defNumTypes) ? i : defNumTypes -1]; + unsigned busPins = Bus::getNumberOfPins(dataType); + // check if we have enough pins left to configure an output of this type + if (pinsIndex + busPins > defNumPins) { + DEBUG_PRINTLN(F("LED outputs misaligned with defined pins. Some pins will remain unused.")); + break; + } + for (unsigned j = 0; j < busPins && j < OUTPUT_MAX_PINS; j++) defPin[j] = defDataPins[pinsIndex + j]; + pinsIndex += busPins; // when booting without config (1st boot) we need to make sure GPIOs defined for LED output don't clash with hardware // i.e. DEBUG (GPIO1), DMX (2), SPI RAM/FLASH (16&17 on ESP32-WROVER/PICO), etc if (pinManager.isPinAllocated(defPin[0])) { @@ -1235,8 +1248,10 @@ void WS2812FX::finalizeInit(void) { unsigned start = prevLen; // if we have less counts than pins and they do not align, use last known count to set current count unsigned count = defCounts[(i < defNumCounts) ? i : defNumCounts -1]; + // analog always has length 1 + if (Bus::isPWM(dataType)) count = 1; prevLen += count; - BusConfig defCfg = BusConfig(DEFAULT_LED_TYPE, defPin, start, count, DEFAULT_LED_COLOR_ORDER, false, 0, RGBW_MODE_MANUAL_ONLY, 0, useGlobalLedBuffer); + BusConfig defCfg = BusConfig(dataType, defPin, start, count, DEFAULT_LED_COLOR_ORDER, false, 0, RGBW_MODE_MANUAL_ONLY, 0, useGlobalLedBuffer); if (BusManager::add(defCfg) == -1) break; } } diff --git a/wled00/bus_manager.cpp b/wled00/bus_manager.cpp index e5918ce95..77e60ba4f 100644 --- a/wled00/bus_manager.cpp +++ b/wled00/bus_manager.cpp @@ -673,7 +673,7 @@ void BusNetwork::cleanup(void) { //utility to get the approx. memory usage of a given BusConfig uint32_t BusManager::memUsage(BusConfig &bc) { - if (Bus::isOnOff(bc.type) || Bus::isPWM(bc.type)) return 5; + if (Bus::isOnOff(bc.type) || Bus::isPWM(bc.type)) return OUTPUT_MAX_PINS; unsigned len = bc.count + bc.skipAmount; unsigned channels = Bus::getNumberOfChannels(bc.type); diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index 1d1136fd8..f6aa4b579 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -267,8 +267,8 @@ class BusPwm : public Bus { void cleanup(void) { deallocatePins(); } private: - uint8_t _pins[5]; - uint8_t _pwmdata[5]; + uint8_t _pins[OUTPUT_MAX_PINS]; + uint8_t _pwmdata[OUTPUT_MAX_PINS]; #ifdef ARDUINO_ARCH_ESP32 uint8_t _ledcStart; #endif @@ -346,10 +346,7 @@ struct BusConfig { { refreshReq = (bool) GET_BIT(busType,7); type = busType & 0x7F; // bit 7 may be/is hacked to include refresh info (1=refresh in off state, 0=no refresh) - size_t nPins = 1; - if (Bus::isVirtual(type)) nPins = 4; //virtual network bus. 4 "pins" store IP address - else if (Bus::is2Pin(type)) nPins = 2; - else if (Bus::isPWM(type)) nPins = Bus::numPWMPins(type); + size_t nPins = Bus::getNumberOfPins(type); for (size_t i = 0; i < nPins; i++) pins[i] = ppins[i]; } diff --git a/wled00/const.h b/wled00/const.h index bdc80aab9..cb50a108f 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -466,6 +466,9 @@ #define NTP_PACKET_SIZE 48 // size of NTP receive buffer #define NTP_MIN_PACKET_SIZE 48 // min expected size - NTP v4 allows for "extended information" appended to the standard fields +// Maximum number of pins per output. 5 for RGBCCT analog LEDs. +#define OUTPUT_MAX_PINS 5 + //maximum number of rendered LEDs - this does not have to match max. physical LEDs, e.g. if there are virtual busses #ifndef MAX_LEDS #ifdef ESP8266 @@ -577,6 +580,10 @@ #endif #endif +#ifndef DEFAULT_LED_TYPE + #define DEFAULT_LED_TYPE TYPE_WS2812_RGB +#endif + #ifndef DEFAULT_LED_COUNT #define DEFAULT_LED_COUNT 30 #endif From fcc344ba995b6c39d8004d440057046fb5ecefa6 Mon Sep 17 00:00:00 2001 From: PaoloTK Date: Sun, 1 Sep 2024 21:31:19 +0200 Subject: [PATCH 02/14] add read/only pin logic and check --- wled00/FX_fcn.cpp | 26 +++++++++++++++----------- wled00/const.h | 13 +++++++++++++ wled00/pin_manager.cpp | 23 +++++++++++++++++++++++ wled00/pin_manager.h | 3 +++ wled00/xml.cpp | 20 +++++++++----------- 5 files changed, 63 insertions(+), 22 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 69a742c3d..331ceb077 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1216,11 +1216,11 @@ void WS2812FX::finalizeInit(void) { if (BusManager::getNumBusses() == 0) { DEBUG_PRINTLN(F("No busses, init default")); constexpr unsigned defDataTypes[] = {LED_TYPES}; - const unsigned defDataPins[] = {DATA_PINS}; - const unsigned defCounts[] = {PIXEL_COUNTS}; - const unsigned defNumTypes = ((sizeof defDataTypes) / (sizeof defDataTypes[0])); + constexpr unsigned defDataPins[] = {DATA_PINS}; + constexpr unsigned defCounts[] = {PIXEL_COUNTS}; + constexpr unsigned defNumTypes = ((sizeof defDataTypes) / (sizeof defDataTypes[0])); constexpr unsigned defNumPins = ((sizeof defDataPins) / (sizeof defDataPins[0])); - const unsigned defNumCounts = ((sizeof defCounts) / (sizeof defCounts[0])); + constexpr unsigned defNumCounts = ((sizeof defCounts) / (sizeof defCounts[0])); static_assert(Bus::getNumberOfPins(defDataTypes[0]) <= defNumPins, "The first LED type configured requires more pins than have been defined!"); @@ -1237,14 +1237,18 @@ void WS2812FX::finalizeInit(void) { DEBUG_PRINTLN(F("LED outputs misaligned with defined pins. Some pins will remain unused.")); break; } - for (unsigned j = 0; j < busPins && j < OUTPUT_MAX_PINS; j++) defPin[j] = defDataPins[pinsIndex + j]; - pinsIndex += busPins; - // when booting without config (1st boot) we need to make sure GPIOs defined for LED output don't clash with hardware - // i.e. DEBUG (GPIO1), DMX (2), SPI RAM/FLASH (16&17 on ESP32-WROVER/PICO), etc - if (pinManager.isPinAllocated(defPin[0])) { - defPin[0] = 1; // start with GPIO1 and work upwards - while (pinManager.isPinAllocated(defPin[0]) && defPin[0] < WLED_NUM_PINS) defPin[0]++; + for (unsigned j = 0; j < busPins && j < OUTPUT_MAX_PINS; j++) { + defPin[j] = defDataPins[pinsIndex + j]; + + // when booting without config (1st boot) we need to make sure GPIOs defined for LED output don't clash with hardware + // i.e. DEBUG (GPIO1), DMX (2), SPI RAM/FLASH (16&17 on ESP32-WROVER/PICO), read/only pins, etc. + if (pinManager.isPinAllocated(defPin[j]) || pinManager.isReadOnlyPin(defPin[j])) { + defPin[j] = 1; // start with GPIO1 and work upwards + while (pinManager.isPinAllocated(defPin[j]) && pinManager.isReadOnlyPin(defPin[j]) && defPin[j] < WLED_NUM_PINS) defPin[j]++; + } } + pinsIndex += busPins; + unsigned start = prevLen; // if we have less counts than pins and they do not align, use last known count to set current count unsigned count = defCounts[(i < defNumCounts) ? i : defNumCounts -1]; diff --git a/wled00/const.h b/wled00/const.h index cb50a108f..ffefb3ca5 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -572,6 +572,19 @@ #endif #endif +// List of read only pins. Cannot be used for LED outputs. +#if defined(CONFIG_IDF_TARGET_ESP32S2) + #define READ_ONLY_PINS 46 +#elif defined(CONFIG_IDF_TARGET_ESP32S3) +// none for S3 +#elif defined(CONFIG_IDF_TARGET_ESP32C3) +// none for C3 +#elif defined(ESP32) + #define READ_ONLY_PINS 34,35,36,37,38,39 +#else +// none for ESP8266 +#endif + #ifdef WLED_ENABLE_DMX #if (LEDPIN == 2) #undef LEDPIN diff --git a/wled00/pin_manager.cpp b/wled00/pin_manager.cpp index 0a0f39155..507ce81cd 100644 --- a/wled00/pin_manager.cpp +++ b/wled00/pin_manager.cpp @@ -267,6 +267,29 @@ bool PinManagerClass::isPinOk(byte gpio, bool output) const return false; } +unsigned *PinManagerClass::getReadOnlyPins() +{ + #ifdef READ_ONLY_PINS + static unsigned readOnlyPins[] = {READ_ONLY_PINS}; + #elif + static unsigned readOnlyPins[] = 255; + #endif + return readOnlyPins; +} + +bool PinManagerClass::isReadOnlyPin(byte gpio) +{ + const unsigned* pins = PinManagerClass::getReadOnlyPins(); + const unsigned numPins = sizeof(pins) / sizeof(pins[0]); + + for (unsigned i = 0; i < numPins; i++) { + if (pins[i] == gpio) { + return true; + } + } + return false; +} + PinOwner PinManagerClass::getPinOwner(byte gpio) const { if (!isPinOk(gpio, false)) return PinOwner::None; diff --git a/wled00/pin_manager.h b/wled00/pin_manager.h index 986964a7f..b441b89af 100644 --- a/wled00/pin_manager.h +++ b/wled00/pin_manager.h @@ -112,6 +112,9 @@ class PinManagerClass { // will return false for reserved pins bool isPinOk(byte gpio, bool output = true) const; + static unsigned* getReadOnlyPins(); + static bool isReadOnlyPin(byte gpio); + PinOwner getPinOwner(byte gpio) const; #ifdef ARDUINO_ARCH_ESP32 diff --git a/wled00/xml.cpp b/wled00/xml.cpp index c06b02768..8f8dd9e7d 100644 --- a/wled00/xml.cpp +++ b/wled00/xml.cpp @@ -191,17 +191,15 @@ void appendGPIOinfo() { // add info for read-only GPIO oappend(SET_F("d.ro_gpio=[")); - #if defined(CONFIG_IDF_TARGET_ESP32S2) - oappendi(46); - #elif defined(CONFIG_IDF_TARGET_ESP32S3) - // none for S3 - #elif defined(CONFIG_IDF_TARGET_ESP32C3) - // none for C3 - #elif defined(ESP32) - oappend(SET_F("34,35,36,37,38,39")); - #else - // none for ESP8266 - #endif + const unsigned* readOnlyPins = pinManager.getReadOnlyPins(); + const unsigned numReadOnlyPins = ((sizeof readOnlyPins) / (sizeof readOnlyPins[0])); + for (unsigned i = 0; i < numReadOnlyPins; i++) { + // Ignore 255 + if (readOnlyPins[i] <= WLED_NUM_PINS) { + oappendi(readOnlyPins[i]); + if (i != numReadOnlyPins) oappend(SET_F(",")); + } + } oappend(SET_F("];")); // add info about max. # of pins From c9423454531999027d6b7e50444a51d036cd5794 Mon Sep 17 00:00:00 2001 From: PaoloTK Date: Sun, 1 Sep 2024 21:35:09 +0200 Subject: [PATCH 03/14] bug fix --- wled00/pin_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/pin_manager.cpp b/wled00/pin_manager.cpp index 507ce81cd..3ba48ead4 100644 --- a/wled00/pin_manager.cpp +++ b/wled00/pin_manager.cpp @@ -271,7 +271,7 @@ unsigned *PinManagerClass::getReadOnlyPins() { #ifdef READ_ONLY_PINS static unsigned readOnlyPins[] = {READ_ONLY_PINS}; - #elif + #else static unsigned readOnlyPins[] = 255; #endif return readOnlyPins; From 9bb979f2e8cab910ad0a2ec8a7d3026b1f93413c Mon Sep 17 00:00:00 2001 From: PaoloTK Date: Sun, 1 Sep 2024 21:46:30 +0200 Subject: [PATCH 04/14] bug fix --- wled00/pin_manager.cpp | 4 ++-- wled00/xml.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/wled00/pin_manager.cpp b/wled00/pin_manager.cpp index 3ba48ead4..527e4470c 100644 --- a/wled00/pin_manager.cpp +++ b/wled00/pin_manager.cpp @@ -272,7 +272,7 @@ unsigned *PinManagerClass::getReadOnlyPins() #ifdef READ_ONLY_PINS static unsigned readOnlyPins[] = {READ_ONLY_PINS}; #else - static unsigned readOnlyPins[] = 255; + static unsigned readOnlyPins[] = {255}; #endif return readOnlyPins; } @@ -280,7 +280,7 @@ unsigned *PinManagerClass::getReadOnlyPins() bool PinManagerClass::isReadOnlyPin(byte gpio) { const unsigned* pins = PinManagerClass::getReadOnlyPins(); - const unsigned numPins = sizeof(pins) / sizeof(pins[0]); + const unsigned numPins = (sizeof *pins) / (sizeof pins[0]); for (unsigned i = 0; i < numPins; i++) { if (pins[i] == gpio) { diff --git a/wled00/xml.cpp b/wled00/xml.cpp index 8f8dd9e7d..dfb3d67df 100644 --- a/wled00/xml.cpp +++ b/wled00/xml.cpp @@ -192,7 +192,7 @@ void appendGPIOinfo() { // add info for read-only GPIO oappend(SET_F("d.ro_gpio=[")); const unsigned* readOnlyPins = pinManager.getReadOnlyPins(); - const unsigned numReadOnlyPins = ((sizeof readOnlyPins) / (sizeof readOnlyPins[0])); + const unsigned numReadOnlyPins = (sizeof *readOnlyPins) / (sizeof readOnlyPins[0]); for (unsigned i = 0; i < numReadOnlyPins; i++) { // Ignore 255 if (readOnlyPins[i] <= WLED_NUM_PINS) { From d79d5dbadd99925d792f907464e8279808438c0a Mon Sep 17 00:00:00 2001 From: PaoloTK Date: Tue, 3 Sep 2024 17:29:38 +0200 Subject: [PATCH 05/14] remove getReadOnlyPins() function --- wled00/pin_manager.cpp | 23 +++++++---------------- wled00/pin_manager.h | 3 +-- wled00/xml.cpp | 16 ++++++++-------- 3 files changed, 16 insertions(+), 26 deletions(-) diff --git a/wled00/pin_manager.cpp b/wled00/pin_manager.cpp index 527e4470c..2bd82115f 100644 --- a/wled00/pin_manager.cpp +++ b/wled00/pin_manager.cpp @@ -267,26 +267,17 @@ bool PinManagerClass::isPinOk(byte gpio, bool output) const return false; } -unsigned *PinManagerClass::getReadOnlyPins() -{ - #ifdef READ_ONLY_PINS - static unsigned readOnlyPins[] = {READ_ONLY_PINS}; - #else - static unsigned readOnlyPins[] = {255}; - #endif - return readOnlyPins; -} - bool PinManagerClass::isReadOnlyPin(byte gpio) { - const unsigned* pins = PinManagerClass::getReadOnlyPins(); - const unsigned numPins = (sizeof *pins) / (sizeof pins[0]); + #ifdef READ_ONLY_PINS + const unsigned pins[] = {READ_ONLY_PINS}; + const unsigned numPins = ((sizeof pins) / (sizeof pins[0])); - for (unsigned i = 0; i < numPins; i++) { - if (pins[i] == gpio) { - return true; - } + if (gpio <= WLED_NUM_PINS) { + for (unsigned i = 0; i < numPins; i++) if (gpio == pins[i]) return true; } + #endif + return false; } diff --git a/wled00/pin_manager.h b/wled00/pin_manager.h index b441b89af..a8ddf5f75 100644 --- a/wled00/pin_manager.h +++ b/wled00/pin_manager.h @@ -111,8 +111,7 @@ class PinManagerClass { bool isPinAllocated(byte gpio, PinOwner tag = PinOwner::None) const; // will return false for reserved pins bool isPinOk(byte gpio, bool output = true) const; - - static unsigned* getReadOnlyPins(); + static bool isReadOnlyPin(byte gpio); PinOwner getPinOwner(byte gpio) const; diff --git a/wled00/xml.cpp b/wled00/xml.cpp index dfb3d67df..fce453cfc 100644 --- a/wled00/xml.cpp +++ b/wled00/xml.cpp @@ -191,14 +191,14 @@ void appendGPIOinfo() { // add info for read-only GPIO oappend(SET_F("d.ro_gpio=[")); - const unsigned* readOnlyPins = pinManager.getReadOnlyPins(); - const unsigned numReadOnlyPins = (sizeof *readOnlyPins) / (sizeof readOnlyPins[0]); - for (unsigned i = 0; i < numReadOnlyPins; i++) { - // Ignore 255 - if (readOnlyPins[i] <= WLED_NUM_PINS) { - oappendi(readOnlyPins[i]); - if (i != numReadOnlyPins) oappend(SET_F(",")); - } + bool firstPin = true; + for (unsigned i = 0; i < WLED_NUM_PINS; i++) { + if (pinManager.isReadOnlyPin(i)) { + // No comma before the first pin + if (!firstPin) oappend(SET_F(",")); + oappendi(i); + firstPin = false; + } } oappend(SET_F("];")); From 8e4f8fcbc8eb069306a7eca3c7750061734da8fc Mon Sep 17 00:00:00 2001 From: PaoloTK Date: Wed, 4 Sep 2024 13:27:04 +0200 Subject: [PATCH 06/14] fix bug in reassignment logic --- wled00/FX_fcn.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 331ceb077..a737fb549 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1237,6 +1237,7 @@ void WS2812FX::finalizeInit(void) { DEBUG_PRINTLN(F("LED outputs misaligned with defined pins. Some pins will remain unused.")); break; } + for (unsigned j = 0; j < busPins && j < OUTPUT_MAX_PINS; j++) { defPin[j] = defDataPins[pinsIndex + j]; @@ -1244,7 +1245,7 @@ void WS2812FX::finalizeInit(void) { // i.e. DEBUG (GPIO1), DMX (2), SPI RAM/FLASH (16&17 on ESP32-WROVER/PICO), read/only pins, etc. if (pinManager.isPinAllocated(defPin[j]) || pinManager.isReadOnlyPin(defPin[j])) { defPin[j] = 1; // start with GPIO1 and work upwards - while (pinManager.isPinAllocated(defPin[j]) && pinManager.isReadOnlyPin(defPin[j]) && defPin[j] < WLED_NUM_PINS) defPin[j]++; + while ((pinManager.isPinAllocated(defPin[j]) || pinManager.isReadOnlyPin(defPin[j])) && defPin[j] < WLED_NUM_PINS) defPin[j]++; } } pinsIndex += busPins; From 329173e1452846a419feb426bcbc041e9163cc93 Mon Sep 17 00:00:00 2001 From: PaoloTK Date: Wed, 4 Sep 2024 13:28:43 +0200 Subject: [PATCH 07/14] added complete validation logic for pins and types --- wled00/FX_fcn.cpp | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index a737fb549..74d612564 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -69,6 +69,18 @@ #error "Max segments must be at least max number of busses!" #endif +static constexpr unsigned sumPinsRequired(const unsigned* current, size_t count) { + return (count > 0) ? (Bus::getNumberOfPins(*current) + sumPinsRequired(current+1,count-1)) : 0; +} + +static constexpr bool validatePinsAndTypes(const unsigned* types, unsigned numTypes, unsigned numPins ) { + // Pins provided < pins required -> always invalid + // Pins provided = pins required -> always valid + // Pins provided > pins required -> last type will repeat until we run out of pins, make sure excess pins modulo last type pins == 0 + return (sumPinsRequired(types, numTypes) > numPins) ? false : + (numPins - sumPinsRequired(types, numTypes)) % Bus::getNumberOfPins(types[numTypes-1]) == 0; +} + /////////////////////////////////////////////////////////////////////////////// // Segment class implementation @@ -1222,9 +1234,9 @@ void WS2812FX::finalizeInit(void) { constexpr unsigned defNumPins = ((sizeof defDataPins) / (sizeof defDataPins[0])); constexpr unsigned defNumCounts = ((sizeof defCounts) / (sizeof defCounts[0])); - static_assert(Bus::getNumberOfPins(defDataTypes[0]) <= defNumPins, - "The first LED type configured requires more pins than have been defined!"); - + static_assert(validatePinsAndTypes(defDataTypes, defNumTypes, defNumPins), + "The default pin list defined in DATA_PINS does not match the pin requirements for the default buses defined in LED_TYPES"); + unsigned prevLen = 0; unsigned pinsIndex = 0; for (unsigned i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) { @@ -1232,7 +1244,9 @@ void WS2812FX::finalizeInit(void) { // if we have less types than requested outputs and they do not align, use last known type to set current type unsigned dataType = defDataTypes[(i < defNumTypes) ? i : defNumTypes -1]; unsigned busPins = Bus::getNumberOfPins(dataType); + // check if we have enough pins left to configure an output of this type + // should never happen due to static assert above if (pinsIndex + busPins > defNumPins) { DEBUG_PRINTLN(F("LED outputs misaligned with defined pins. Some pins will remain unused.")); break; @@ -1245,6 +1259,7 @@ void WS2812FX::finalizeInit(void) { // i.e. DEBUG (GPIO1), DMX (2), SPI RAM/FLASH (16&17 on ESP32-WROVER/PICO), read/only pins, etc. if (pinManager.isPinAllocated(defPin[j]) || pinManager.isReadOnlyPin(defPin[j])) { defPin[j] = 1; // start with GPIO1 and work upwards + // @FIX pins are allocated after the loop, so if we reassign to a pin that's already in the array for this output 2 fields will have the same pin while ((pinManager.isPinAllocated(defPin[j]) || pinManager.isReadOnlyPin(defPin[j])) && defPin[j] < WLED_NUM_PINS) defPin[j]++; } } From eb06e575c2ff00aa663ca9d1ee0c5d5ce9ad2bf6 Mon Sep 17 00:00:00 2001 From: PaoloTK Date: Tue, 10 Sep 2024 01:03:19 +0200 Subject: [PATCH 08/14] add check for pin reassigned conflict with pin defined for current bus --- wled00/FX_fcn.cpp | 19 +++++++++++++++---- wled00/pin_manager.cpp | 7 +++++++ wled00/pin_manager.h | 1 + 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 74d612564..6f218c93f 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -76,7 +76,7 @@ static constexpr unsigned sumPinsRequired(const unsigned* current, size_t count) static constexpr bool validatePinsAndTypes(const unsigned* types, unsigned numTypes, unsigned numPins ) { // Pins provided < pins required -> always invalid // Pins provided = pins required -> always valid - // Pins provided > pins required -> last type will repeat until we run out of pins, make sure excess pins modulo last type pins == 0 + // Pins provided > pins required -> valid if excess pins are a product of last type pins since it will be repeated return (sumPinsRequired(types, numTypes) > numPins) ? false : (numPins - sumPinsRequired(types, numTypes)) % Bus::getNumberOfPins(types[numTypes-1]) == 0; } @@ -1240,7 +1240,7 @@ void WS2812FX::finalizeInit(void) { unsigned prevLen = 0; unsigned pinsIndex = 0; for (unsigned i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) { - uint8_t defPin[OUTPUT_MAX_PINS]; // max 5 pins + uint8_t defPin[OUTPUT_MAX_PINS]; // if we have less types than requested outputs and they do not align, use last known type to set current type unsigned dataType = defDataTypes[(i < defNumTypes) ? i : defNumTypes -1]; unsigned busPins = Bus::getNumberOfPins(dataType); @@ -1259,8 +1259,19 @@ void WS2812FX::finalizeInit(void) { // i.e. DEBUG (GPIO1), DMX (2), SPI RAM/FLASH (16&17 on ESP32-WROVER/PICO), read/only pins, etc. if (pinManager.isPinAllocated(defPin[j]) || pinManager.isReadOnlyPin(defPin[j])) { defPin[j] = 1; // start with GPIO1 and work upwards - // @FIX pins are allocated after the loop, so if we reassign to a pin that's already in the array for this output 2 fields will have the same pin - while ((pinManager.isPinAllocated(defPin[j]) || pinManager.isReadOnlyPin(defPin[j])) && defPin[j] < WLED_NUM_PINS) defPin[j]++; + while ( + ( + pinManager.isPinAllocated(defPin[j]) || + pinManager.isReadOnlyPin(defPin[j]) || + // Check if pin is defined for current bus + pinManager.isPinDefined(defPin[j], defDataPins, pinsIndex + j, pinsIndex + busPins) + ) + && + defPin[j] < WLED_NUM_PINS + ) + { + defPin[j]++; + } } } pinsIndex += busPins; diff --git a/wled00/pin_manager.cpp b/wled00/pin_manager.cpp index 2bd82115f..814d81c1a 100644 --- a/wled00/pin_manager.cpp +++ b/wled00/pin_manager.cpp @@ -281,6 +281,13 @@ bool PinManagerClass::isReadOnlyPin(byte gpio) return false; } +bool PinManagerClass::isPinDefined(byte gpio, const unsigned *pins, unsigned start, unsigned end) { + for (unsigned i = start; i < end; i++) { + if (pins[i] == gpio) return true; + } + return false; +} + PinOwner PinManagerClass::getPinOwner(byte gpio) const { if (!isPinOk(gpio, false)) return PinOwner::None; diff --git a/wled00/pin_manager.h b/wled00/pin_manager.h index a8ddf5f75..1263dc4f4 100644 --- a/wled00/pin_manager.h +++ b/wled00/pin_manager.h @@ -113,6 +113,7 @@ class PinManagerClass { bool isPinOk(byte gpio, bool output = true) const; static bool isReadOnlyPin(byte gpio); + static bool isPinDefined(byte gpio, const unsigned* pins, unsigned start = 0, unsigned end = WLED_NUM_PINS); PinOwner getPinOwner(byte gpio) const; From 5869627b32366e4784435f84f6d4cbf10e0041c6 Mon Sep 17 00:00:00 2001 From: PaoloTK Date: Wed, 11 Sep 2024 01:27:19 +0200 Subject: [PATCH 09/14] - More optimization on bus configuration logic. - Renamed LEDPIN to DEFAULT_LED_PIN. - Removed ability to override DEFAULT_LED_PIN, DEFAULT_LED_TYPE and DEFAULT_LED_COUNT. Use DATA_PINS, LED_TYPES and PIXEL_COUNTS instead. --- wled00/FX_fcn.cpp | 39 +++++++++++++++++---------------------- wled00/const.h | 22 +++++++--------------- 2 files changed, 24 insertions(+), 37 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 6f218c93f..435998cdc 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -53,7 +53,7 @@ #endif #ifndef DATA_PINS - #define DATA_PINS LEDPIN + #define DATA_PINS DEFAULT_LED_PIN #endif #ifndef LED_TYPES @@ -1245,32 +1245,27 @@ void WS2812FX::finalizeInit(void) { unsigned dataType = defDataTypes[(i < defNumTypes) ? i : defNumTypes -1]; unsigned busPins = Bus::getNumberOfPins(dataType); - // check if we have enough pins left to configure an output of this type - // should never happen due to static assert above - if (pinsIndex + busPins > defNumPins) { - DEBUG_PRINTLN(F("LED outputs misaligned with defined pins. Some pins will remain unused.")); - break; - } + // if we need more pins than available all outputs have been configured + if (pinsIndex + busPins > defNumPins) break; for (unsigned j = 0; j < busPins && j < OUTPUT_MAX_PINS; j++) { defPin[j] = defDataPins[pinsIndex + j]; - // when booting without config (1st boot) we need to make sure GPIOs defined for LED output don't clash with hardware + bool validPin = true; + // When booting without config (1st boot) we need to make sure GPIOs defined for LED output don't clash with hardware // i.e. DEBUG (GPIO1), DMX (2), SPI RAM/FLASH (16&17 on ESP32-WROVER/PICO), read/only pins, etc. - if (pinManager.isPinAllocated(defPin[j]) || pinManager.isReadOnlyPin(defPin[j])) { - defPin[j] = 1; // start with GPIO1 and work upwards - while ( - ( - pinManager.isPinAllocated(defPin[j]) || - pinManager.isReadOnlyPin(defPin[j]) || - // Check if pin is defined for current bus - pinManager.isPinDefined(defPin[j], defDataPins, pinsIndex + j, pinsIndex + busPins) - ) - && - defPin[j] < WLED_NUM_PINS - ) - { - defPin[j]++; + // Pin should not be already allocated, read/only or defined for current bus + while (pinManager.isPinAllocated(defPin[j]) || pinManager.isReadOnlyPin(defPin[j]) || + pinManager.isPinDefined(defPin[j], defDataPins, pinsIndex + j + 1, pinsIndex + busPins)) { + if (validPin) { + defPin[j] = 1; // start with GPIO1 and work upwards + validPin = false; + } + if (defPin[j] < WLED_NUM_PINS) { + defPin[j]++; + } else { + DEBUG_PRINTLN(F("No available pins left! Can't configure output.")); + return; } } } diff --git a/wled00/const.h b/wled00/const.h index ffefb3ca5..1bfca0758 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -563,15 +563,6 @@ #define WLED_MAX_NODES 150 #endif -//this is merely a default now and can be changed at runtime -#ifndef LEDPIN -#if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) //|| (defined(ARDUINO_ARCH_ESP32) && defined(BOARD_HAS_PSRAM)) || defined(ARDUINO_ESP32_PICO) - #define LEDPIN 2 // GPIO2 (D4) on Wemos D1 mini compatible boards, safe to use on any board -#else - #define LEDPIN 16 // aligns with GPIO2 (D4) on Wemos D1 mini32 compatible boards (if it is unusable it will be reassigned in WS2812FX::finalizeInit()) -#endif -#endif - // List of read only pins. Cannot be used for LED outputs. #if defined(CONFIG_IDF_TARGET_ESP32S2) #define READ_ONLY_PINS 46 @@ -593,13 +584,14 @@ #endif #endif -#ifndef DEFAULT_LED_TYPE - #define DEFAULT_LED_TYPE TYPE_WS2812_RGB -#endif - -#ifndef DEFAULT_LED_COUNT - #define DEFAULT_LED_COUNT 30 +// Defaults pins, type and counts to configure LED output +#if defined(ESP8266) || defined(CONFIG_IDF_TARGET_ESP32C3) + #define DEFAULT_LED_PIN 2 // GPIO2 (D4) on Wemos D1 mini compatible boards, safe to use on any board +#else + #define DEFAULT_LED_PIN 16 // aligns with GPIO2 (D4) on Wemos D1 mini32 compatible boards (if it is unusable it will be reassigned in WS2812FX::finalizeInit()) #endif +#define DEFAULT_LED_TYPE TYPE_WS2812_RGB +#define DEFAULT_LED_COUNT 30 #define INTERFACE_UPDATE_COOLDOWN 1000 // time in ms to wait between websockets, alexa, and MQTT updates From daf0bcfac364846928a2277c1f16475c8d32ac42 Mon Sep 17 00:00:00 2001 From: PaoloTK Date: Thu, 12 Sep 2024 18:12:38 +0200 Subject: [PATCH 10/14] bug fix on pin already defined check --- wled00/FX_fcn.cpp | 9 +++++---- wled00/pin_manager.cpp | 8 +++++--- wled00/pin_manager.h | 2 +- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 435998cdc..6b01d9a32 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1248,15 +1248,16 @@ void WS2812FX::finalizeInit(void) { // if we need more pins than available all outputs have been configured if (pinsIndex + busPins > defNumPins) break; - for (unsigned j = 0; j < busPins && j < OUTPUT_MAX_PINS; j++) { - defPin[j] = defDataPins[pinsIndex + j]; + // Assign all pins first so we can check for conflicts on this bus + for (unsigned j = 0; j < busPins && j < OUTPUT_MAX_PINS; j++) defPin[j] = defDataPins[pinsIndex + j]; + for (unsigned j = 0; j < busPins && j < OUTPUT_MAX_PINS; j++) { bool validPin = true; // When booting without config (1st boot) we need to make sure GPIOs defined for LED output don't clash with hardware // i.e. DEBUG (GPIO1), DMX (2), SPI RAM/FLASH (16&17 on ESP32-WROVER/PICO), read/only pins, etc. // Pin should not be already allocated, read/only or defined for current bus - while (pinManager.isPinAllocated(defPin[j]) || pinManager.isReadOnlyPin(defPin[j]) || - pinManager.isPinDefined(defPin[j], defDataPins, pinsIndex + j + 1, pinsIndex + busPins)) { + while (pinManager.isPinAllocated(defPin[j]) || pinManager.isReadOnlyPin(defPin[j]) || pinManager.isPinDefined(defPin[j], defPin, j)) { + DEBUG_PRINTLN(F("Some of the provided pins cannot be used to configure this LED output.")); if (validPin) { defPin[j] = 1; // start with GPIO1 and work upwards validPin = false; diff --git a/wled00/pin_manager.cpp b/wled00/pin_manager.cpp index 9398ec94f..815bddee3 100644 --- a/wled00/pin_manager.cpp +++ b/wled00/pin_manager.cpp @@ -281,9 +281,11 @@ bool PinManagerClass::isReadOnlyPin(byte gpio) return false; } -bool PinManagerClass::isPinDefined(byte gpio, const unsigned *pins, unsigned start, unsigned end) { - for (unsigned i = start; i < end; i++) { - if (pins[i] == gpio) return true; +// Given an array of pins, check if a given pin is defined except at given index +bool PinManagerClass::isPinDefined(const byte gpio, const uint8_t *pins, const unsigned index) { + unsigned numPins = ((sizeof pins) / (sizeof pins[0])); + for (unsigned i = 0; i < numPins; i++) { + if ((pins[i] == gpio) && (i != index)) return true; } return false; } diff --git a/wled00/pin_manager.h b/wled00/pin_manager.h index 1263dc4f4..6c5726130 100644 --- a/wled00/pin_manager.h +++ b/wled00/pin_manager.h @@ -113,7 +113,7 @@ class PinManagerClass { bool isPinOk(byte gpio, bool output = true) const; static bool isReadOnlyPin(byte gpio); - static bool isPinDefined(byte gpio, const unsigned* pins, unsigned start = 0, unsigned end = WLED_NUM_PINS); + static bool isPinDefined(const byte gpio, const uint8_t* pins, const unsigned index = 255); PinOwner getPinOwner(byte gpio) const; From fa82e759bd0e2e29683f1b33ed7eff26311586db Mon Sep 17 00:00:00 2001 From: PaoloTK Date: Thu, 12 Sep 2024 18:27:51 +0200 Subject: [PATCH 11/14] bug fix --- wled00/FX_fcn.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 6b01d9a32..d652435b7 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1257,8 +1257,8 @@ void WS2812FX::finalizeInit(void) { // i.e. DEBUG (GPIO1), DMX (2), SPI RAM/FLASH (16&17 on ESP32-WROVER/PICO), read/only pins, etc. // Pin should not be already allocated, read/only or defined for current bus while (pinManager.isPinAllocated(defPin[j]) || pinManager.isReadOnlyPin(defPin[j]) || pinManager.isPinDefined(defPin[j], defPin, j)) { - DEBUG_PRINTLN(F("Some of the provided pins cannot be used to configure this LED output.")); if (validPin) { + DEBUG_PRINTLN(F("Some of the provided pins cannot be used to configure this LED output.")); defPin[j] = 1; // start with GPIO1 and work upwards validPin = false; } From e3d9417b84dfac3e64b8634b78e3cd29ed3027de Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Sat, 14 Sep 2024 11:39:56 +0200 Subject: [PATCH 12/14] Minor fixes - rely on HAL for RO pins and max pins - remove isPinDefined() and do clash check inline - JS fix to use HAL max pins --- wled00/FX_fcn.cpp | 16 +++++++++++++--- wled00/const.h | 13 ------------- wled00/data/settings_leds.htm | 2 +- wled00/data/settings_um.htm | 12 ++++++------ wled00/pin_manager.cpp | 21 +++------------------ wled00/pin_manager.h | 1 - wled00/xml.cpp | 12 +----------- 7 files changed, 24 insertions(+), 53 deletions(-) diff --git a/wled00/FX_fcn.cpp b/wled00/FX_fcn.cpp index 447ceaab9..09b2ee517 100644 --- a/wled00/FX_fcn.cpp +++ b/wled00/FX_fcn.cpp @@ -1256,18 +1256,28 @@ void WS2812FX::finalizeInit() { // When booting without config (1st boot) we need to make sure GPIOs defined for LED output don't clash with hardware // i.e. DEBUG (GPIO1), DMX (2), SPI RAM/FLASH (16&17 on ESP32-WROVER/PICO), read/only pins, etc. // Pin should not be already allocated, read/only or defined for current bus - while (pinManager.isPinAllocated(defPin[j]) || pinManager.isReadOnlyPin(defPin[j]) || pinManager.isPinDefined(defPin[j], defPin, j)) { + while (pinManager.isPinAllocated(defPin[j]) || !pinManager.isPinOk(defPin[j],true)) { if (validPin) { DEBUG_PRINTLN(F("Some of the provided pins cannot be used to configure this LED output.")); defPin[j] = 1; // start with GPIO1 and work upwards validPin = false; - } - if (defPin[j] < WLED_NUM_PINS) { + } else if (defPin[j] < WLED_NUM_PINS) { defPin[j]++; } else { DEBUG_PRINTLN(F("No available pins left! Can't configure output.")); return; } + // is the newly assigned pin already defined? try next in line until there are no clashes + bool clash; + do { + clash = false; + for (const auto pin : defDataPins) { + if (pin == defPin[j]) { + defPin[j]++; + if (defPin[j] < WLED_NUM_PINS) clash = true; + } + } + } while (clash); } } pinsIndex += busPins; diff --git a/wled00/const.h b/wled00/const.h index a0ebeebb1..47ec02c6a 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -567,19 +567,6 @@ #define WLED_MAX_NODES 150 #endif -// List of read only pins. Cannot be used for LED outputs. -#if defined(CONFIG_IDF_TARGET_ESP32S2) - #define READ_ONLY_PINS 46 -#elif defined(CONFIG_IDF_TARGET_ESP32S3) -// none for S3 -#elif defined(CONFIG_IDF_TARGET_ESP32C3) -// none for C3 -#elif defined(ESP32) - #define READ_ONLY_PINS 34,35,36,37,38,39 -#else -// none for ESP8266 -#endif - #ifdef WLED_ENABLE_DMX #if (LEDPIN == 2) #undef LEDPIN diff --git a/wled00/data/settings_leds.htm b/wled00/data/settings_leds.htm index b3188f0d8..206d4a8c7 100644 --- a/wled00/data/settings_leds.htm +++ b/wled00/data/settings_leds.htm @@ -671,7 +671,7 @@ Swap: