-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Configure different kinds of busses at compile #4107
Changes from 12 commits
6655e26
fcc344b
c942345
9bb979f
d79d5db
8e4f8fc
329173e
eb06e57
5869627
c5435ec
daf0bcf
fa82e75
6a18803
e3d9417
81e412f
f16ed46
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,20 +44,20 @@ | |
*/ | ||
|
||
//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 | ||
#define PIXEL_COUNTS DEFAULT_LED_COUNT | ||
#endif | ||
|
||
#ifndef DATA_PINS | ||
#define DATA_PINS LEDPIN | ||
#define DATA_PINS DEFAULT_LED_PIN | ||
#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 | ||
|
@@ -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 -> 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; | ||
} | ||
|
||
|
||
/////////////////////////////////////////////////////////////////////////////// | ||
// Segment class implementation | ||
|
@@ -1215,28 +1227,58 @@ 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")); | ||
const unsigned defDataPins[] = {DATA_PINS}; | ||
const unsigned defCounts[] = {PIXEL_COUNTS}; | ||
const 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; | ||
constexpr unsigned defDataTypes[] = {LED_TYPES}; | ||
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])); | ||
constexpr unsigned defNumCounts = ((sizeof defCounts) / (sizeof defCounts[0])); | ||
|
||
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; | ||
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]; | ||
// 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]++; | ||
unsigned pinsIndex = 0; | ||
for (unsigned i = 0; i < WLED_MAX_BUSSES+WLED_MIN_VIRTUAL_BUSSES; i++) { | ||
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); | ||
|
||
// if we need more pins than available all outputs have been configured | ||
if (pinsIndex + busPins > defNumPins) break; | ||
|
||
// 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], defPin, j)) { | ||
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; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this needs |
||
if (defPin[j] < WLED_NUM_PINS) { | ||
defPin[j]++; | ||
} else { | ||
DEBUG_PRINTLN(F("No available pins left! Can't configure output.")); | ||
return; | ||
} | ||
} | ||
} | ||
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]; | ||
// analog always has length 1 | ||
if (Bus::isPWM(dataType)) count = 1; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
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; | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -787,7 +787,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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reviewing original code, |
||
|
||
unsigned len = bc.count + bc.skipAmount; | ||
unsigned channels = Bus::getNumberOfChannels(bc.type); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
|
@@ -564,13 +567,17 @@ | |
#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 | ||
// List of read only pins. Cannot be used for LED outputs. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can't we rely on HAL? Do we really need |
||
#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 | ||
#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 | ||
// none for ESP8266 | ||
#endif | ||
|
||
#ifdef WLED_ENABLE_DMX | ||
|
@@ -581,9 +588,14 @@ | |
#endif | ||
#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 | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -267,6 +267,29 @@ bool PinManagerClass::isPinOk(byte gpio, bool output) const | |
return false; | ||
} | ||
|
||
bool PinManagerClass::isReadOnlyPin(byte gpio) | ||
{ | ||
#ifdef READ_ONLY_PINS | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would prefer to query HAL here. #ifdef ARDUINO_ARCH_ESP32
if (gpio < WLED_NUM_PINS) return digitalPinCanOutput(gpio);
#endif There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have tested digitalPinCanOutput but unfortunately it does not return FALSE only for read/only pins but also for other types of pins (e.g. ones that don't have pads). While this doesn't matter for finalizeInit(), the GUI displays read/only pins in orange, which cannot be achieved from what I can see without hardcoding the pins currently. We could of course just leave the pins hardcoded there, but since they might be useful in other places I figured centralizing them in const.h was a better option. EDIT: I guess a possible solution in the GUI is to first color the pins orange if they're not a valid output, and then color them red if they're not a valid pin. That way read/only pins will stay orange while the other invalid pins will stay red. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't forget that GUI also has reserved pins, those are not populated using read-only information. |
||
const unsigned pins[] = {READ_ONLY_PINS}; | ||
const unsigned numPins = ((sizeof pins) / (sizeof pins[0])); | ||
|
||
if (gpio <= WLED_NUM_PINS) { | ||
for (unsigned i = 0; i < numPins; i++) if (gpio == pins[i]) return true; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If not using HAL this could be simplified (as I've learned recently) as: for (const auto pin : pins) if (gpio == pin) return true; |
||
} | ||
#endif | ||
|
||
return false; | ||
} | ||
|
||
// 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])); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will not work correctly if I am not mistaken. I'm also wondering why do you really need to query defined pins here? Why not in the place where this actually matters - in |
||
for (unsigned i = 0; i < numPins; i++) { | ||
if ((pins[i] == gpio) && (i != index)) return true; | ||
} | ||
return false; | ||
} | ||
|
||
PinOwner PinManagerClass::getPinOwner(byte gpio) const | ||
{ | ||
if (!isPinOk(gpio, false)) return PinOwner::None; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not use
pinManager.isPinOk(defPin[j],true)
instead ofpinManager.isReadOnlyPin(defPin[j])
? It serves the same purpose.Use of
pinManager.isPinDefined()
is IMO incorrect (see also other comment).I would rather add
after incrementing
defPin[j]
to check if newly assigned pin clashes with one of the defined ones.