Ethernet configuration fix, improve PinManager (#2123)

* Improved pin manager, ethernet config

* Ethernet is configured prior even to LED pins
* Pin Manager allocation / deallocation functions
   now take an "ownership" tag parameter, helping
   avoid accidentally free'ing pins that were allocated
   by other code
* Pin Manager now has ability to allocate multiple
  pins at once; Simplifies error handling

* Fix operator precedence error

Bitwise AND has lower precedence than the
relational "greater than" operator.

* PinManager update for some user modules

* don't build everything...

* Final step to reduce RAM overhead

* update comment

* remove macros

* Remove leftover allocated

* Init ethernet after settings saved

Co-authored-by: Christian Schwinne <dev.aircoookie@gmail.com>
This commit is contained in:
Henry Gabryjelski 2021-08-23 05:14:48 -07:00 committed by GitHub
parent ff8145b745
commit 1d4487b6cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 450 additions and 206 deletions

View File

@ -8,12 +8,15 @@
# Please uncomment one of the lines below to select your board(s) # Please uncomment one of the lines below to select your board(s)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Travis CI binaries (comment this out with a ';' when building for your own board) # Travis CI binaries (use `platformio_override.ini` when building for your own board; see `platformio_override.ini.sample` for an example)
;default_envs = travis_esp8266, travis_esp32 ; default_envs = travis_esp8266, travis_esp32
# Release binaries # Release binaries
default_envs = nodemcuv2, esp01_1m_full, esp32dev, esp32_eth default_envs = nodemcuv2, esp01_1m_full, esp32dev, esp32_eth
# Build everything
; default_envs = esp32dev, esp8285_4CH_MagicHome, esp8285_4CH_H801, codm-controller-0.6-rev2, codm-controller-0.6, esp32s2_saola, d1_mini_5CH_Shojo_PCB, d1_mini, sp501e, travis_esp8266, travis_esp32, nodemcuv2, esp32_eth, anavi_miracle_controller, esp07, esp01_1m_full, m5atom, h803wf, d1_mini_ota, heltec_wifi_kit_8, esp8285_5CH_H801, d1_mini_debug, wemos_shield_esp32, elekstube_ips
# Single binaries (uncomment your board) # Single binaries (uncomment your board)
; default_envs = elekstube_ips ; default_envs = elekstube_ips
; default_envs = nodemcuv2 ; default_envs = nodemcuv2
@ -399,7 +402,7 @@ build_flags = ${common.build_flags_esp32}
-D TEMPERATURE_PIN=23 -D TEMPERATURE_PIN=23
lib_deps = ${esp32.lib_deps} lib_deps = ${esp32.lib_deps}
OneWire@~2.3.5 OneWire@~2.3.5
U8g2@~2.28.11 olikraus/U8g2 @ ^2.28.8
[env:m5atom] [env:m5atom]
board = esp32dev board = esp32dev
@ -482,4 +485,6 @@ build_flags = ${common.build_flags_esp32} -D WLED_DISABLE_BROWNOUT_DET -D WLED_D
-D SPI_FREQUENCY=40000000 -D SPI_FREQUENCY=40000000
-D USER_SETUP_LOADED -D USER_SETUP_LOADED
monitor_filters = esp32_exception_decoder monitor_filters = esp32_exception_decoder
lib_deps = ${esp32.lib_deps} TFT_eSPI lib_deps =
${esp32.lib_deps}
TFT_eSPI @ ^2.3.70

View File

@ -306,22 +306,26 @@ class Animated_Staircase : public Usermod {
public: public:
void setup() { void setup() {
// standardize invalid pin numbers to -1
if (topPIRorTriggerPin < 0) topPIRorTriggerPin = -1;
if (topEchoPin < 0) topEchoPin = -1;
if (bottomPIRorTriggerPin < 0) bottomPIRorTriggerPin = -1;
if (bottomEchoPin < 0) bottomEchoPin = -1;
// allocate pins // allocate pins
if (topPIRorTriggerPin >= 0) { PinManagerPinType pins[4] = {
if (!pinManager.allocatePin(topPIRorTriggerPin,useUSSensorTop)) { topPIRorTriggerPin, useUSSensorTop },
topPIRorTriggerPin = -1; { topEchoPin, false },
} { bottomPIRorTriggerPin, useUSSensorBottom },
if (topEchoPin >= 0) { { bottomEchoPin, false },
if (!pinManager.allocatePin(topEchoPin,false)) };
topEchoPin = -1; // NOTE: this *WILL* return TRUE if all the pins are set to -1.
} // this is *BY DESIGN*.
if (bottomPIRorTriggerPin >= 0) { if (!pinManager.allocateMultiplePins(pins, 4, PinOwner::UM_AnimatedStaircase)) {
if (!pinManager.allocatePin(bottomPIRorTriggerPin,useUSSensorBottom)) topPIRorTriggerPin = -1;
bottomPIRorTriggerPin = -1; topEchoPin = -1;
} bottomPIRorTriggerPin = -1;
if (bottomEchoPin >= 0) { bottomEchoPin = -1;
if (!pinManager.allocatePin(bottomEchoPin,false)) enabled = false;
bottomEchoPin = -1;
} }
enable(enabled); enable(enabled);
initDone = true; initDone = true;
@ -480,10 +484,10 @@ class Animated_Staircase : public Usermod {
(oldBottomAPin != bottomPIRorTriggerPin) || (oldBottomAPin != bottomPIRorTriggerPin) ||
(oldBottomBPin != bottomEchoPin)) { (oldBottomBPin != bottomEchoPin)) {
changed = true; changed = true;
pinManager.deallocatePin(oldTopAPin); pinManager.deallocatePin(oldTopAPin, PinOwner::UM_AnimatedStaircase);
pinManager.deallocatePin(oldTopBPin); pinManager.deallocatePin(oldTopBPin, PinOwner::UM_AnimatedStaircase);
pinManager.deallocatePin(oldBottomAPin); pinManager.deallocatePin(oldBottomAPin, PinOwner::UM_AnimatedStaircase);
pinManager.deallocatePin(oldBottomBPin); pinManager.deallocatePin(oldBottomBPin, PinOwner::UM_AnimatedStaircase);
} }
if (changed) setup(); if (changed) setup();
} }

View File

@ -58,12 +58,12 @@ public:
void setMinutesTens() { setDigit(MINUTES_TENS); } void setMinutesTens() { setDigit(MINUTES_TENS); }
void setHoursOnes() { setDigit(HOURS_ONES); } void setHoursOnes() { setDigit(HOURS_ONES); }
void setHoursTens() { setDigit(HOURS_TENS); } void setHoursTens() { setDigit(HOURS_TENS); }
bool isSecondsOnes() { return (digits_map&SECONDS_ONES_MAP > 0); } bool isSecondsOnes() { return ((digits_map & SECONDS_ONES_MAP) > 0); }
bool isSecondsTens() { return (digits_map&SECONDS_TENS_MAP > 0); } bool isSecondsTens() { return ((digits_map & SECONDS_TENS_MAP) > 0); }
bool isMinutesOnes() { return (digits_map&MINUTES_ONES_MAP > 0); } bool isMinutesOnes() { return ((digits_map & MINUTES_ONES_MAP) > 0); }
bool isMinutesTens() { return (digits_map&MINUTES_TENS_MAP > 0); } bool isMinutesTens() { return ((digits_map & MINUTES_TENS_MAP) > 0); }
bool isHoursOnes() { return (digits_map&HOURS_ONES_MAP > 0); } bool isHoursOnes() { return ((digits_map & HOURS_ONES_MAP) > 0); }
bool isHoursTens() { return (digits_map&HOURS_TENS_MAP > 0); } bool isHoursTens() { return ((digits_map & HOURS_TENS_MAP) > 0); }
}; };

View File

@ -201,12 +201,14 @@ public:
{ {
if (enabled) { if (enabled) {
// pin retrieved from cfg.json (readFromConfig()) prior to running setup() // pin retrieved from cfg.json (readFromConfig()) prior to running setup()
if (PIRsensorPin >= 0 && pinManager.allocatePin(PIRsensorPin,false)) { if (PIRsensorPin >= 0 && pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) {
// PIR Sensor mode INPUT_PULLUP // PIR Sensor mode INPUT_PULLUP
pinMode(PIRsensorPin, INPUT_PULLUP); pinMode(PIRsensorPin, INPUT_PULLUP);
sensorPinState = digitalRead(PIRsensorPin); sensorPinState = digitalRead(PIRsensorPin);
} else { } else {
if (PIRsensorPin >= 0) DEBUG_PRINTLN(F("PIRSensorSwitch pin allocation failed.")); if (PIRsensorPin >= 0) {
DEBUG_PRINTLN(F("PIRSensorSwitch pin allocation failed."));
}
PIRsensorPin = -1; // allocation failed PIRsensorPin = -1; // allocation failed
enabled = false; enabled = false;
} }
@ -367,8 +369,8 @@ public:
if (oldPin != PIRsensorPin && oldPin >= 0) { if (oldPin != PIRsensorPin && oldPin >= 0) {
// if we are changing pin in settings page // if we are changing pin in settings page
// deallocate old pin // deallocate old pin
pinManager.deallocatePin(oldPin); pinManager.deallocatePin(oldPin, PinOwner::UM_PIR);
if (pinManager.allocatePin(PIRsensorPin,false)) { if (pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) {
pinMode(PIRsensorPin, INPUT_PULLUP); pinMode(PIRsensorPin, INPUT_PULLUP);
} else { } else {
// allocation failed // allocation failed

View File

@ -115,14 +115,19 @@ class UsermodTemperature : public Usermod {
// config says we are enabled // config says we are enabled
DEBUG_PRINTLN(F("Allocating temperature pin...")); DEBUG_PRINTLN(F("Allocating temperature pin..."));
// pin retrieved from cfg.json (readFromConfig()) prior to running setup() // pin retrieved from cfg.json (readFromConfig()) prior to running setup()
if (temperaturePin >= 0 && pinManager.allocatePin(temperaturePin)) { if (temperaturePin >= 0 && pinManager.allocatePin(temperaturePin, true, PinOwner::UM_Temperature)) {
oneWire = new OneWire(temperaturePin); oneWire = new OneWire(temperaturePin);
if (!oneWire->reset()) if (!oneWire->reset()) {
sensorFound = false; // resetting 1-Wire bus yielded an error sensorFound = false; // resetting 1-Wire bus yielded an error
else } else {
while ((sensorFound=findSensor()) && retries--) delay(25); // try to find sensor while ((sensorFound=findSensor()) && retries--) {
delay(25); // try to find sensor
}
}
} else { } else {
if (temperaturePin >= 0) DEBUG_PRINTLN(F("Temperature pin allocation failed.")); if (temperaturePin >= 0) {
DEBUG_PRINTLN(F("Temperature pin allocation failed."));
}
temperaturePin = -1; // allocation failed temperaturePin = -1; // allocation failed
sensorFound = false; sensorFound = false;
} }
@ -273,7 +278,7 @@ class UsermodTemperature : public Usermod {
DEBUG_PRINTLN(F("Re-init temperature.")); DEBUG_PRINTLN(F("Re-init temperature."));
// deallocate pin and release memory // deallocate pin and release memory
delete oneWire; delete oneWire;
pinManager.deallocatePin(temperaturePin); pinManager.deallocatePin(temperaturePin, PinOwner::UM_Temperature);
temperaturePin = newTemperaturePin; temperaturePin = newTemperaturePin;
// initialise // initialise
setup(); setup();

View File

@ -258,7 +258,7 @@ class MultiRelay : public Usermod {
// pins retrieved from cfg.json (readFromConfig()) prior to running setup() // pins retrieved from cfg.json (readFromConfig()) prior to running setup()
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) { for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
if (_relay[i].pin<0) continue; if (_relay[i].pin<0) continue;
if (!pinManager.allocatePin(_relay[i].pin,true)) { if (!pinManager.allocatePin(_relay[i].pin,true, PinOwner::UM_MultiRelay)) {
_relay[i].pin = -1; // allocation failed _relay[i].pin = -1; // allocation failed
} else { } else {
switchRelay(i, _relay[i].state = (bool)bri); switchRelay(i, _relay[i].state = (bool)bri);
@ -380,12 +380,14 @@ class MultiRelay : public Usermod {
// deallocate all pins 1st // deallocate all pins 1st
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++)
if (oldPin[i]>=0) { if (oldPin[i]>=0) {
pinManager.deallocatePin(oldPin[i]); pinManager.deallocatePin(oldPin[i], PinOwner::UM_MultiRelay);
} }
// allocate new pins // allocate new pins
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) { for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
if (_relay[i].pin>=0 && pinManager.allocatePin(_relay[i].pin,true)) { if (_relay[i].pin>=0 && pinManager.allocatePin(_relay[i].pin, true, PinOwner::UM_MultiRelay)) {
if (!_relay[i].external) switchRelay(i, _relay[i].state = (bool)bri); if (!_relay[i].external) {
switchRelay(i, _relay[i].state = (bool)bri);
}
} else { } else {
_relay[i].pin = -1; _relay[i].pin = -1;
} }

View File

@ -13,7 +13,7 @@ class RgbRotaryEncoderUsermod : public Usermod
BusDigital *ledBus; BusDigital *ledBus;
/* /*
* Green - eb - Q4 - 32 * Green - eb - Q4 - 32
* Red - ea - Q1 - 15 * Red - ea - Q1 - 15
* Black - sw - Q2 - 12 * Black - sw - Q2 - 12
*/ */
ESPRotary *rotaryEncoder; ESPRotary *rotaryEncoder;
@ -39,13 +39,10 @@ class RgbRotaryEncoderUsermod : public Usermod
void initRotaryEncoder() void initRotaryEncoder()
{ {
if (!pinManager.allocatePin(eaIo, false)) { PinManagerPinType pins[2] = { { eaIo, false }, { ebIo, false } };
if (!pinManager.allocateMultiplePins(pins, 2, UM_RGBRotaryEncoder)) {
eaIo = -1; eaIo = -1;
}
if (!pinManager.allocatePin(ebIo, false)) {
ebIo = -1; ebIo = -1;
}
if (eaIo == -1 || ebIo == -1) {
cleanup(); cleanup();
return; return;
} }
@ -111,10 +108,12 @@ class RgbRotaryEncoderUsermod : public Usermod
{ {
// Only deallocate pins if we allocated them ;) // Only deallocate pins if we allocated them ;)
if (eaIo != -1) { if (eaIo != -1) {
pinManager.deallocatePin(eaIo); pinManager.deallocatePin(eaIo, PinOwner::UM_RGBRotaryEncoder);
eaIo = -1;
} }
if (ebIo != -1) { if (ebIo != -1) {
pinManager.deallocatePin(ebIo); pinManager.deallocatePin(ebIo, PinOwner::UM_RGBRotaryEncoder);
ebIo = -1;
} }
delete rotaryEncoder; delete rotaryEncoder;
@ -304,8 +303,8 @@ class RgbRotaryEncoderUsermod : public Usermod
} }
if (eaIo != oldEaIo || ebIo != oldEbIo || stepsPerClick != oldStepsPerClick || incrementPerClick != oldIncrementPerClick) { if (eaIo != oldEaIo || ebIo != oldEbIo || stepsPerClick != oldStepsPerClick || incrementPerClick != oldIncrementPerClick) {
pinManager.deallocatePin(oldEaIo); pinManager.deallocatePin(oldEaIo, PinOwner::UM_RGBRotaryEncoder);
pinManager.deallocatePin(oldEbIo); pinManager.deallocatePin(oldEbIo, PinOwner::UM_RGBRotaryEncoder);
delete rotaryEncoder; delete rotaryEncoder;
initRotaryEncoder(); initRotaryEncoder();

View File

@ -168,14 +168,13 @@ class FourLineDisplayUsermod : public Usermod {
// network here // network here
void setup() { void setup() {
if (type == NONE) return; if (type == NONE) return;
bool allocated = false;
byte i; byte i;
if (type == SSD1306_SPI || type == SSD1306_SPI64) { if (type == SSD1306_SPI || type == SSD1306_SPI64) {
for (i=0; i<5; i++) if (!pinManager.allocatePin(ioPin[i])) { allocated=true; break; } PinManagerPinType pins[5] = { { ioPin[0], true }, { ioPin[1], true}, { ioPin[2], true }, { ioPin[3], true}, { ioPin[4], true }};
if (i<5 && allocated) { for (byte i=0; i<5; i++) pinManager.deallocatePin(ioPin[i]); type=NONE; return; } if (!pinManager.allocateMultiplePins(pins, 5, PinOwner::UM_FourLineDisplay)) { type=NONE; return; }
} else { } else {
for (i=0; i<2; i++) if (!pinManager.allocatePin(ioPin[i])) { allocated=true; break; } PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true} };
if (i<2 && allocated) { for (byte i=0; i<5; i++) pinManager.deallocatePin(ioPin[i]); type=NONE; return; } if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::UM_FourLineDisplay)) { type=NONE; return; }
} }
DEBUG_PRINTLN(F("Allocating display.")); DEBUG_PRINTLN(F("Allocating display."));
switch (type) { switch (type) {
@ -240,18 +239,20 @@ class FourLineDisplayUsermod : public Usermod {
break; break;
default: default:
u8x8 = nullptr; u8x8 = nullptr;
}
if (nullptr == u8x8) {
DEBUG_PRINTLN(F("Display init failed."));
pinManager.deallocatePin(sclPin, PinOwner::UM_FourLineDisplay);
pinManager.deallocatePin(sdaPin, PinOwner::UM_FourLineDisplay);
sclPin = -1;
sdaPin = -1;
type = NONE; type = NONE;
return; return;
} }
initDone = true; initDone = true;
if (u8x8 != nullptr) { DEBUG_PRINTLN(F("Starting display."));
DEBUG_PRINTLN(F("Starting display.")); (static_cast<U8X8*>(u8x8))->begin(); // why a static cast here? variable is of this type...
(static_cast<U8X8*>(u8x8))->begin();
} else {
DEBUG_PRINTLN(F("Display init failed."));
type = NONE;
return;
}
setFlipMode(flip); setFlipMode(flip);
setContrast(contrast); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255 setContrast(contrast); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255
setPowerSave(0); setPowerSave(0);
@ -727,7 +728,7 @@ class FourLineDisplayUsermod : public Usermod {
if (pinsChanged || type!=newType) { if (pinsChanged || type!=newType) {
if (type != NONE) delete (static_cast<U8X8*>(u8x8)); if (type != NONE) delete (static_cast<U8X8*>(u8x8));
for (byte i=0; i<5; i++) { for (byte i=0; i<5; i++) {
if (ioPin[i]>=0) pinManager.deallocatePin(ioPin[i]); if (ioPin[i]>=0) pinManager.deallocatePin(ioPin[i], PinOwner::UM_FourLineDisplay);
ioPin[i] = newPin[i]; ioPin[i] = newPin[i];
} }
if (ioPin[0]<0 || ioPin[1]<0) { // data & clock must be > -1 if (ioPin[0]<0 || ioPin[1]<0) { // data & clock must be > -1
@ -735,7 +736,9 @@ class FourLineDisplayUsermod : public Usermod {
return true; return true;
} else type = newType; } else type = newType;
setup(); setup();
needsRedraw |= true; if (sclPin >= 0 && sdaPin >= 0) {
needsRedraw |= true;
}
} }
setContrast(contrast); setContrast(contrast);
setFlipMode(flip); setFlipMode(flip);

View File

@ -76,7 +76,7 @@ private:
unsigned char Enc_B; unsigned char Enc_B;
unsigned char Enc_A_prev = 0; unsigned char Enc_A_prev = 0;
bool currentEffectAndPaleeteInitialized = false; bool currentEffectAndPaletteInitialized = false;
uint8_t effectCurrentIndex = 0; uint8_t effectCurrentIndex = 0;
uint8_t effectPaletteIndex = 0; uint8_t effectPaletteIndex = 0;
@ -97,9 +97,17 @@ public:
*/ */
void setup() void setup()
{ {
if (!pinManager.allocatePin(pinA)) { enabled = false; return;} PinManagerPinType pins[3] = { { pinA, false }, { pinB, false }, { pinC, false } };
if (!pinManager.allocatePin(pinB)) { pinManager.deallocatePin(pinA); enabled = false; return; } if (!pinManager.allocateMultiplePins(pins, 3, PinOwner::UM_RotaryEncoderUI)) {
if (!pinManager.allocatePin(pinC)) { pinManager.deallocatePin(pinA); pinManager.deallocatePin(pinB); enabled = false; return; } // BUG: configuring this usermod with conflicting pins
// will cause it to de-allocate pins it does not own
// (at second config)
// This is the exact type of bug solved by pinManager
// tracking the owner tags....
pinA = pinB = pinC = -1;
enabled = false;
return;
}
pinMode(pinA, INPUT_PULLUP); pinMode(pinA, INPUT_PULLUP);
pinMode(pinB, INPUT_PULLUP); pinMode(pinB, INPUT_PULLUP);
@ -152,7 +160,7 @@ public:
// Initialize effectCurrentIndex and effectPaletteIndex to // Initialize effectCurrentIndex and effectPaletteIndex to
// current state. We do it here as (at least) effectCurrent // current state. We do it here as (at least) effectCurrent
// is not yet initialized when setup is called. // is not yet initialized when setup is called.
if (!currentEffectAndPaleeteInitialized) { if (!currentEffectAndPaletteInitialized) {
findCurrentEffectAndPalette(); findCurrentEffectAndPalette();
} }
@ -248,7 +256,7 @@ public:
} }
void findCurrentEffectAndPalette() { void findCurrentEffectAndPalette() {
currentEffectAndPaleeteInitialized = true; currentEffectAndPaletteInitialized = true;
for (uint8_t i = 0; i < strip.getModeCount(); i++) { for (uint8_t i = 0; i < strip.getModeCount(); i++) {
//byte value = modes_alpha_indexes[i]; //byte value = modes_alpha_indexes[i];
if (modes_alpha_indexes[i] == effectCurrent) { if (modes_alpha_indexes[i] == effectCurrent) {
@ -455,9 +463,9 @@ public:
DEBUG_PRINTLN(F(" config (re)loaded.")); DEBUG_PRINTLN(F(" config (re)loaded."));
// changing parameters from settings page // changing parameters from settings page
if (pinA!=newDTpin || pinB!=newCLKpin || pinC!=newSWpin) { if (pinA!=newDTpin || pinB!=newCLKpin || pinC!=newSWpin) {
pinManager.deallocatePin(pinA); pinManager.deallocatePin(pinA, PinOwner::UM_RotaryEncoderUI);
pinManager.deallocatePin(pinB); pinManager.deallocatePin(pinB, PinOwner::UM_RotaryEncoderUI);
pinManager.deallocatePin(pinC); pinManager.deallocatePin(pinC, PinOwner::UM_RotaryEncoderUI);
pinA = newDTpin; pinA = newDTpin;
pinB = newCLKpin; pinB = newCLKpin;
pinC = newSWpin; pinC = newSWpin;

View File

@ -119,10 +119,10 @@ class BusDigital : public Bus {
public: public:
BusDigital(BusConfig &bc, uint8_t nr) : Bus(bc.type, bc.start) { BusDigital(BusConfig &bc, uint8_t nr) : Bus(bc.type, bc.start) {
if (!IS_DIGITAL(bc.type) || !bc.count) return; if (!IS_DIGITAL(bc.type) || !bc.count) return;
if (!pinManager.allocatePin(bc.pins[0])) return; if (!pinManager.allocatePin(bc.pins[0], true, PinOwner::BusDigital)) return;
_pins[0] = bc.pins[0]; _pins[0] = bc.pins[0];
if (IS_2PIN(bc.type)) { if (IS_2PIN(bc.type)) {
if (!pinManager.allocatePin(bc.pins[1])) { if (!pinManager.allocatePin(bc.pins[1], true, PinOwner::BusDigital)) {
cleanup(); return; cleanup(); return;
} }
_pins[1] = bc.pins[1]; _pins[1] = bc.pins[1];
@ -206,8 +206,8 @@ class BusDigital : public Bus {
_iType = I_NONE; _iType = I_NONE;
_valid = false; _valid = false;
_busPtr = nullptr; _busPtr = nullptr;
pinManager.deallocatePin(_pins[0]); pinManager.deallocatePin(_pins[1], PinOwner::BusDigital);
pinManager.deallocatePin(_pins[1]); pinManager.deallocatePin(_pins[0], PinOwner::BusDigital);
} }
~BusDigital() { ~BusDigital() {
@ -242,7 +242,7 @@ class BusPwm : public Bus {
for (uint8_t i = 0; i < numPins; i++) { for (uint8_t i = 0; i < numPins; i++) {
uint8_t currentPin = bc.pins[i]; uint8_t currentPin = bc.pins[i];
if (!pinManager.allocatePin(currentPin)) { if (!pinManager.allocatePin(currentPin, true, PinOwner::BusPwm)) {
deallocatePins(); return; deallocatePins(); return;
} }
_pins[i] = currentPin; // store only after allocatePin() succeeds _pins[i] = currentPin; // store only after allocatePin() succeeds
@ -334,7 +334,7 @@ class BusPwm : public Bus {
#else #else
if (_ledcStart < 16) ledcDetachPin(_pins[i]); if (_ledcStart < 16) ledcDetachPin(_pins[i]);
#endif #endif
pinManager.deallocatePin(_pins[i]); pinManager.deallocatePin(_pins[i], PinOwner::BusPwm);
} }
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
pinManager.deallocateLedc(_ledcStart, numPins); pinManager.deallocateLedc(_ledcStart, numPins);

View File

@ -18,6 +18,13 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
//long vid = doc[F("vid")]; // 2010020 //long vid = doc[F("vid")]; // 2010020
#ifdef WLED_USE_ETHERNET
JsonObject ethernet = doc[F("eth")];
CJSON(ethernetType, ethernet["type"]);
// NOTE: Ethernet configuration takes priority over other use of pins
WLED::instance().initEthernet();
#endif
JsonObject id = doc["id"]; JsonObject id = doc["id"];
getStringFromJson(cmDNS, id[F("mdns")], 33); getStringFromJson(cmDNS, id[F("mdns")], 33);
getStringFromJson(serverDescription, id[F("name")], 33); getStringFromJson(serverDescription, id[F("name")], 33);
@ -53,10 +60,6 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
CJSON(apBehavior, ap[F("behav")]); CJSON(apBehavior, ap[F("behav")]);
#ifdef WLED_USE_ETHERNET
JsonObject ethernet = doc[F("eth")];
CJSON(ethernetType, ethernet["type"]);
#endif
/* /*
JsonArray ap_ip = ap["ip"]; JsonArray ap_ip = ap["ip"];
@ -71,7 +74,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
JsonObject hw = doc[F("hw")]; JsonObject hw = doc[F("hw")];
// initialize LED pins and lengths prior to other HW // initialize LED pins and lengths prior to other HW (except for ethernet)
JsonObject hw_led = hw[F("led")]; JsonObject hw_led = hw[F("led")];
CJSON(ledCount, hw_led[F("total")]); CJSON(ledCount, hw_led[F("total")]);
@ -130,7 +133,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
for (JsonObject btn : hw_btn_ins) { for (JsonObject btn : hw_btn_ins) {
CJSON(buttonType[s], btn["type"]); CJSON(buttonType[s], btn["type"]);
int8_t pin = btn["pin"][0] | -1; int8_t pin = btn["pin"][0] | -1;
if (pin > -1 && pinManager.allocatePin(pin,false)) { if (pin > -1 && pinManager.allocatePin(pin, false, PinOwner::Button)) {
btnPin[s] = pin; btnPin[s] = pin;
pinMode(btnPin[s], INPUT_PULLUP); pinMode(btnPin[s], INPUT_PULLUP);
} else { } else {
@ -155,7 +158,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
if (fromFS) { if (fromFS) {
// relies upon only being called once with fromFS == true, which is currently true. // relies upon only being called once with fromFS == true, which is currently true.
uint8_t s = 0; uint8_t s = 0;
if (pinManager.allocatePin(btnPin[0],false)) { // initialized to #define value BTNPIN, or zero if not defined(!) if (pinManager.allocatePin(btnPin[0], false, PinOwner::Button)) { // initialized to #define value BTNPIN, or zero if not defined(!)
++s; // do not clear default button if allocated successfully ++s; // do not clear default button if allocated successfully
} }
for (; s<WLED_MAX_BUTTONS; s++) { for (; s<WLED_MAX_BUTTONS; s++) {
@ -172,7 +175,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
int hw_ir_pin = hw["ir"]["pin"] | -2; // 4 int hw_ir_pin = hw["ir"]["pin"] | -2; // 4
if (hw_ir_pin > -2) { if (hw_ir_pin > -2) {
if (pinManager.allocatePin(hw_ir_pin,false)) { if (pinManager.allocatePin(hw_ir_pin, false, PinOwner::IR)) {
irPin = hw_ir_pin; irPin = hw_ir_pin;
} else { } else {
irPin = -1; irPin = -1;
@ -183,7 +186,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) {
JsonObject relay = hw[F("relay")]; JsonObject relay = hw[F("relay")];
int hw_relay_pin = relay["pin"] | -2; int hw_relay_pin = relay["pin"] | -2;
if (hw_relay_pin > -2) { if (hw_relay_pin > -2) {
if (pinManager.allocatePin(hw_relay_pin,true)) { if (pinManager.allocatePin(hw_relay_pin,true, PinOwner::Relay)) {
rlyPin = hw_relay_pin; rlyPin = hw_relay_pin;
pinMode(rlyPin, OUTPUT); pinMode(rlyPin, OUTPUT);
} else { } else {
@ -437,6 +440,8 @@ void deserializeConfigFromFS() {
return; return;
} }
// NOTE: This routine deserializes *and* applies the configuration
// Therefore, must also initialize ethernet from this function
deserializeConfig(doc.as<JsonObject>(), true); deserializeConfig(doc.as<JsonObject>(), true);
} }

View File

@ -291,7 +291,7 @@
#ifdef ESP8266 #ifdef ESP8266
#define LEDPIN 2 // GPIO2 (D4) on Wemod D1 mini compatible boards #define LEDPIN 2 // GPIO2 (D4) on Wemod D1 mini compatible boards
#else #else
#define LEDPIN 16 // alligns with GPIO2 (D4) on Wemos D1 mini32 compatible boards #define LEDPIN 16 // aligns with GPIO2 (D4) on Wemos D1 mini32 compatible boards
#endif #endif
#endif #endif

View File

@ -359,7 +359,7 @@ void _drawOverlayCronixie()
} }
#else // WLED_DISABLE_CRONIXIE #else // WLED_DISABLE_CRONIXIE
byte getSameCodeLength(char code, int index, char const cronixieDisplay[]) {} byte getSameCodeLength(char code, int index, char const cronixieDisplay[]) { return 0; }
void setCronixie() {} void setCronixie() {}
void _overlayCronixie() {} void _overlayCronixie() {}
void _drawOverlayCronixie() {} void _drawOverlayCronixie() {}

View File

@ -1,35 +1,110 @@
#include "pin_manager.h" #include "pin_manager.h"
#include "wled.h" #include "wled.h"
void PinManagerClass::deallocatePin(byte gpio) static void DebugPrintOwnerTag(PinOwner tag)
{ {
if (!isPinOk(gpio, false)) return; uint32_t q = static_cast<uint8_t>(tag);
if (q) {
DEBUG_PRINTF("0x%02x (%d)", q, q);
} else {
DEBUG_PRINT(F("(no owner)"));
}
}
/// Actual allocation/deallocation routines
bool PinManagerClass::deallocatePin(byte gpio, PinOwner tag)
{
if (gpio == 0xFF) return true; // explicitly allow clients to free -1 as a no-op
if (!isPinOk(gpio, false)) return false; // but return false for any other invalid pin
// if a non-zero ownerTag, only allow de-allocation if the owner's tag is provided
if ((ownerTag[gpio] != PinOwner::None) && (ownerTag[gpio] != tag)) {
DEBUG_PRINT(F("PIN DEALLOC: IO "));
DEBUG_PRINT(gpio);
DEBUG_PRINT(F(" allocated by "));
DebugPrintOwnerTag(ownerTag[gpio]);
DEBUG_PRINT(F(", but attempted de-allocation by "));
DebugPrintOwnerTag(tag);
return false;
}
byte by = gpio >> 3; byte by = gpio >> 3;
byte bi = gpio - 8*by; byte bi = gpio - 8*by;
bitWrite(pinAlloc[by], bi, false); bitWrite(pinAlloc[by], bi, false);
ownerTag[gpio] = PinOwner::None;
return true;
} }
bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, byte arrayElementCount, PinOwner tag)
{
bool shouldFail = false;
// first verify the pins are OK and not already allocated
for (int i = 0; i < arrayElementCount; i++) {
byte gpio = mptArray[i].pin;
if (gpio == 0xFF) {
// explicit support for io -1 as a no-op (no allocation of pin),
// as this can greatly simplify configuration arrays
continue;
}
if (!isPinOk(gpio, mptArray[i].isOutput)) {
DEBUG_PRINT(F("PIN ALLOC: Invalid pin attempted to be allocated: "));
DEBUG_PRINT(gpio);
DEBUG_PRINTLN(F(""));
shouldFail = true;
}
if (isPinAllocated(gpio)) {
DEBUG_PRINT(F("PIN ALLOC: FAIL: IO "));
DEBUG_PRINT(gpio);
DEBUG_PRINT(F(" already allocated by "));
DebugPrintOwnerTag(ownerTag[gpio]);
DEBUG_PRINTLN(F(""));
shouldFail = true;
}
}
if (shouldFail) {
return false;
}
bool PinManagerClass::allocatePin(byte gpio, bool output) // all pins are available .. track each one
for (int i = 0; i < arrayElementCount; i++) {
byte gpio = mptArray[i].pin;
if (gpio == 0xFF) {
// allow callers to include -1 value as non-requested pin
// as this can greatly simplify configuration arrays
continue;
}
byte by = gpio >> 3;
byte bi = gpio - 8*by;
bitWrite(pinAlloc[by], bi, true);
ownerTag[gpio] = tag;
}
return true;
}
bool PinManagerClass::allocatePin(byte gpio, bool output, PinOwner tag)
{ {
if (!isPinOk(gpio, output)) return false; if (!isPinOk(gpio, output)) return false;
if (isPinAllocated(gpio)) { if (isPinAllocated(gpio)) {
DEBUG_PRINT(F("Attempted duplicate allocation of pin ")); DEBUG_PRINT(F("PIN ALLOC: Pin "));
DEBUG_PRINTLN(gpio); DEBUG_PRINT(gpio);
DEBUG_PRINT(F(" already allocated by "));
DebugPrintOwnerTag(ownerTag[gpio]);
DEBUG_PRINTLN(F(""));
return false; return false;
} }
byte by = gpio >> 3; byte by = gpio >> 3;
byte bi = gpio - 8*by; byte bi = gpio - 8*by;
bitWrite(pinAlloc[by], bi, true); bitWrite(pinAlloc[by], bi, true);
ownerTag[gpio] = tag;
return true; return true;
} }
bool PinManagerClass::isPinAllocated(byte gpio) // if tag is set to PinOwner::None, checks for ANY owner of the pin.
// if tag is set to any other value, checks if that tag is the current owner of the pin.
bool PinManagerClass::isPinAllocated(byte gpio, PinOwner tag)
{ {
if (!isPinOk(gpio, false)) return true; if (!isPinOk(gpio, false)) return true;
if ((tag != PinOwner::None) && (ownerTag[gpio] != tag)) return false;
byte by = gpio >> 3; byte by = gpio >> 3;
byte bi = gpio - 8*by; byte bi = gpio - 8*by;
return bitRead(pinAlloc[by], bi); return bitRead(pinAlloc[by], bi);

View File

@ -4,21 +4,93 @@
* Registers pins so there is no attempt for two interfaces to use the same pin * Registers pins so there is no attempt for two interfaces to use the same pin
*/ */
#include <Arduino.h> #include <Arduino.h>
#include "const.h" // for USERMOD_* values
typedef struct PinManagerPinType {
int8_t pin;
uint8_t isOutput;
} managed_pin_type;
/*
* Allows PinManager to "lock" an allocation to a specific
* owner, so someone else doesn't accidentally de-allocate
* a pin it hasn't allocated. Also enhances debugging.
*
* RAM Cost:
* 17 bytes on ESP8266
* 40 bytes on ESP32
*/
enum struct PinOwner : uint8_t {
None = 0, // default == legacy == unspecified owner
// High bit is set for all built-in pin owners
// StatusLED -- THIS SHOULD NEVER BE ALLOCATED -- see handleStatusLED()
Ethernet = 0x81,
BusDigital = 0x82,
BusDigital2 = 0x83,
BusPwm = 0x84, // 'BusP' == PWM output using BusPwm
Button = 0x85, // 'Butn' == button from configuration
IR = 0x86, // 'IR' == IR receiver pin from configuration
Relay = 0x87, // 'Rly' == Relay pin from configuration
SPI_RAM = 0x88, // 'SpiR' == SPI RAM
DebugOut = 0x89, // 'Dbg' == debug output always IO1
DMX = 0x8A, // 'DMX' == hard-coded to IO2
// Use UserMod IDs from const.h here
UM_Unspecified = USERMOD_ID_UNSPECIFIED, // 0x01
UM_RGBRotaryEncoder = USERMOD_ID_UNSPECIFIED, // 0x01 // No define in const.h for this user module -- consider adding?
UM_Example = USERMOD_ID_EXAMPLE, // 0x02 // Usermod "usermod_v2_example.h"
UM_Temperature = USERMOD_ID_TEMPERATURE, // 0x03 // Usermod "usermod_temperature.h"
// #define USERMOD_ID_FIXNETSERVICES // 0x04 // Usermod "usermod_Fix_unreachable_netservices.h" -- Does not allocate pins
UM_PIR = USERMOD_ID_PIRSWITCH, // 0x05 // Usermod "usermod_PIR_sensor_switch.h"
// #define USERMOD_ID_IMU // 0x06 // Usermod "usermod_mpu6050_imu.h" -- Uses "standard" I2C pins ... TODO -- enable shared I2C bus use
UM_FourLineDisplay = USERMOD_ID_FOUR_LINE_DISP, // 0x07 // Usermod "usermod_v2_four_line_display.h
UM_RotaryEncoderUI = USERMOD_ID_ROTARY_ENC_UI, // 0x08 // Usermod "usermod_v2_rotary_encoder_ui.h"
// #define USERMOD_ID_AUTO_SAVE // 0x09 // Usermod "usermod_v2_auto_save.h" -- Does not allocate pins
// #define USERMOD_ID_DHT // 0x0A // Usermod "usermod_dht.h" -- Statically allocates pins, not compatible with pinManager?
// #define USERMOD_ID_MODE_SORT // 0x0B // Usermod "usermod_v2_mode_sort.h" -- Does not allocate pins
// #define USERMOD_ID_VL53L0X // 0x0C // Usermod "usermod_vl53l0x_gestures.h" -- Uses "standard" I2C pins ... TODO -- enable shared I2C bus use
UM_MultiRelay = USERMOD_ID_MULTI_RELAY, // 0x0D // Usermod "usermod_multi_relay.h"
UM_AnimatedStaircase = USERMOD_ID_ANIMATED_STAIRCASE, // 0x0E // Usermod "Animated_Staircase.h"
// #define USERMOD_ID_RTC // 0x0F // Usermod "usermod_rtc.h" -- Uses "standard" I2C pins ... TODO -- enable shared I2C bus use
// #define USERMOD_ID_ELEKSTUBE_IPS // 0x10 // Usermod "usermod_elekstube_ips.h" -- Uses quite a few pins ... see Hardware.h and User_Setup.h
// #define USERMOD_ID_SN_PHOTORESISTOR // 0x11 // Usermod "usermod_sn_photoresistor.h" -- Uses hard-coded pin (PHOTORESISTOR_PIN == A0), but could be easily updated to use pinManager
};
static_assert(0u == static_cast<uint8_t>(PinOwner::None), "PinOwner::None must be zero, so default array initialization works as expected");
class PinManagerClass { class PinManagerClass {
private: private:
#ifdef ESP8266 #ifdef ESP8266
uint8_t pinAlloc[3] = {0x00, 0x00, 0x00}; //24bit, 1 bit per pin, we use first 17bits uint8_t pinAlloc[3] = {0x00, 0x00, 0x00}; //24bit, 1 bit per pin, we use first 17bits
PinOwner ownerTag[17] = { PinOwner::None };
#else #else
uint8_t pinAlloc[5] = {0x00, 0x00, 0x00, 0x00, 0x00}; //40bit, 1 bit per pin, we use all bits uint8_t pinAlloc[5] = {0x00, 0x00, 0x00, 0x00, 0x00}; //40bit, 1 bit per pin, we use all bits
uint8_t ledcAlloc[2] = {0x00, 0x00}; //16 LEDC channels uint8_t ledcAlloc[2] = {0x00, 0x00}; //16 LEDC channels
PinOwner ownerTag[40] = { PinOwner::None };
#endif #endif
public: public:
void deallocatePin(byte gpio); // De-allocates a single pin
bool allocatePin(byte gpio, bool output = true); bool deallocatePin(byte gpio, PinOwner tag);
bool isPinAllocated(byte gpio); // Allocates a single pin, with an owner tag.
// De-allocation requires the same owner tag (or override)
bool allocatePin(byte gpio, bool output, PinOwner tag);
// Allocates all the pins, or allocates none of the pins, with owner tag.
// Provided to simplify error condition handling in clients
// using more than one pin, such as I2C, SPI, rotary encoders,
// ethernet, etc..
bool allocateMultiplePins(const managed_pin_type * mptArray, byte arrayElementCount, PinOwner tag );
#if !defined(ESP8266) // ESP8266 compiler doesn't understand deprecated attribute
[[deprecated("Replaced by three-parameter allocatePin(gpio, output, ownerTag), for improved debugging")]]
#endif
inline bool allocatePin(byte gpio, bool output = true) { return allocatePin(gpio, output, PinOwner::None); }
#if !defined(ESP8266) // ESP8266 compiler doesn't understand deprecated attribute
[[deprecated("Replaced by three-parameter deallocatePin(gpio, output, ownerTag), for improved debugging")]]
#endif
inline void deallocatePin(byte gpio) { deallocatePin(gpio, PinOwner::None); }
bool isPinAllocated(byte gpio, PinOwner tag = PinOwner::None);
bool isPinOk(byte gpio, bool output = true); bool isPinOk(byte gpio, bool output = true);
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
byte allocateLedc(byte channels); byte allocateLedc(byte channels);
void deallocateLedc(byte pos, byte channels); void deallocateLedc(byte pos, byte channels);

View File

@ -54,6 +54,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
#ifdef WLED_USE_ETHERNET #ifdef WLED_USE_ETHERNET
ethernetType = request->arg(F("ETH")).toInt(); ethernetType = request->arg(F("ETH")).toInt();
WLED::instance().initEthernet();
#endif #endif
char k[3]; k[2] = 0; char k[3]; k[2] = 0;
@ -77,11 +78,17 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
{ {
int t = 0; int t = 0;
if (rlyPin>=0 && pinManager.isPinAllocated(rlyPin)) pinManager.deallocatePin(rlyPin); if (rlyPin>=0 && pinManager.isPinAllocated(rlyPin, PinOwner::Relay)) {
if (irPin>=0 && pinManager.isPinAllocated(irPin)) pinManager.deallocatePin(irPin); pinManager.deallocatePin(rlyPin, PinOwner::Relay);
for (uint8_t s=0; s<WLED_MAX_BUTTONS; s++) }
if (btnPin[s]>=0 && pinManager.isPinAllocated(btnPin[s])) if (irPin>=0 && pinManager.isPinAllocated(irPin, PinOwner::IR)) {
pinManager.deallocatePin(btnPin[s]); pinManager.deallocatePin(irPin, PinOwner::IR);
}
for (uint8_t s=0; s<WLED_MAX_BUTTONS; s++) {
if (btnPin[s]>=0 && pinManager.isPinAllocated(btnPin[s], PinOwner::Button)) {
pinManager.deallocatePin(btnPin[s], PinOwner::Button);
}
}
strip.isRgbw = false; strip.isRgbw = false;
uint8_t colorOrder, type, skip; uint8_t colorOrder, type, skip;
@ -127,7 +134,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
// upate other pins // upate other pins
int hw_ir_pin = request->arg(F("IR")).toInt(); int hw_ir_pin = request->arg(F("IR")).toInt();
if (pinManager.allocatePin(hw_ir_pin,false)) { if (pinManager.allocatePin(hw_ir_pin,false, PinOwner::IR)) {
irPin = hw_ir_pin; irPin = hw_ir_pin;
} else { } else {
irPin = -1; irPin = -1;
@ -135,7 +142,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
irEnabled = request->arg(F("IT")).toInt(); irEnabled = request->arg(F("IT")).toInt();
int hw_rly_pin = request->arg(F("RL")).toInt(); int hw_rly_pin = request->arg(F("RL")).toInt();
if (pinManager.allocatePin(hw_rly_pin,true)) { if (pinManager.allocatePin(hw_rly_pin,true, PinOwner::Relay)) {
rlyPin = hw_rly_pin; rlyPin = hw_rly_pin;
} else { } else {
rlyPin = -1; rlyPin = -1;
@ -146,7 +153,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
char bt[4] = "BT"; bt[2] = 48+i; bt[3] = 0; // button pin char bt[4] = "BT"; bt[2] = 48+i; bt[3] = 0; // button pin
char be[4] = "BE"; be[2] = 48+i; be[3] = 0; // button type char be[4] = "BE"; be[2] = 48+i; be[3] = 0; // button type
int hw_btn_pin = request->arg(bt).toInt(); int hw_btn_pin = request->arg(bt).toInt();
if (pinManager.allocatePin(hw_btn_pin,false)) { if (pinManager.allocatePin(hw_btn_pin,false,PinOwner::Button)) {
btnPin[i] = hw_btn_pin; btnPin[i] = hw_btn_pin;
pinMode(btnPin[i], INPUT_PULLUP); pinMode(btnPin[i], INPUT_PULLUP);
buttonType[i] = request->arg(be).toInt(); buttonType[i] = request->arg(be).toInt();

View File

@ -105,6 +105,11 @@ void WiFiEvent(WiFiEvent_t event)
break; break;
case SYSTEM_EVENT_ETH_DISCONNECTED: case SYSTEM_EVENT_ETH_DISCONNECTED:
DEBUG_PRINT(F("ETH Disconnected")); DEBUG_PRINT(F("ETH Disconnected"));
// This doesn't really affect ethernet per se,
// as it's only configured once. Rather, it
// may be necessary to reconnect the WiFi when
// ethernet disconnects, as a way to provide
// alternative access to the device.
forceReconnect = true; forceReconnect = true;
break; break;
#endif #endif
@ -275,20 +280,21 @@ void WLED::setup()
registerUsermods(); registerUsermods();
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM) #if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
if (psramFound()) { if (psramFound()) {
pinManager.allocatePin(16); // GPIO16 reserved for SPI RAM // GPIO16/GPIO17 reserved for SPI RAM
pinManager.allocatePin(17); // GPIO17 reserved for SPI RAM managed_pin_type pins[2] = { {16, true}, {17, true} };
} pinManager.allocateMultiplePins(pins, 2, PinOwner::SPI_RAM);
}
#endif #endif
//DEBUG_PRINT(F("LEDs inited. heap usage ~")); //DEBUG_PRINT(F("LEDs inited. heap usage ~"));
//DEBUG_PRINTLN(heapPreAlloc - ESP.getFreeHeap()); //DEBUG_PRINTLN(heapPreAlloc - ESP.getFreeHeap());
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
pinManager.allocatePin(1,true); // GPIO1 reserved for debug output pinManager.allocatePin(1, true, PinOwner::DebugOut); // GPIO1 reserved for debug output
#endif #endif
#ifdef WLED_USE_DMX //reserve GPIO2 as hardcoded DMX pin #ifdef WLED_USE_DMX //reserve GPIO2 as hardcoded DMX pin
pinManager.allocatePin(2); pinManager.allocatePin(2, true, PinOwner::DMX);
#endif #endif
for (uint8_t i=1; i<WLED_MAX_BUTTONS; i++) btnPin[i] = -1; for (uint8_t i=1; i<WLED_MAX_BUTTONS; i++) btnPin[i] = -1;
@ -310,7 +316,11 @@ void WLED::setup()
deserializeConfigFromFS(); deserializeConfigFromFS();
#if STATUSLED #if STATUSLED
if (!pinManager.isPinAllocated(STATUSLED)) pinMode(STATUSLED, OUTPUT); if (!pinManager.isPinAllocated(STATUSLED)) {
// NOTE: Special case: The status LED should *NOT* be allocated.
// See comments in handleStatusLed().
pinMode(STATUSLED, OUTPUT);
}
#endif #endif
DEBUG_PRINTLN(F("Initializing strip")); DEBUG_PRINTLN(F("Initializing strip"));
@ -436,72 +446,97 @@ void WLED::initAP(bool resetAP)
apActive = true; apActive = true;
} }
bool WLED::initEthernet()
{
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
static bool successfullyConfiguredEthernet = false;
if (successfullyConfiguredEthernet) {
// DEBUG_PRINTLN(F("initE: ETH already successfully configured, ignoring"));
return false;
}
if (ethernetType == WLED_ETH_NONE) {
return false;
}
if (ethernetType >= WLED_NUM_ETH_TYPES) {
DEBUG_PRINT(F("initE: Ignoring attempt for invalid ethernetType ")); DEBUG_PRINTLN(ethernetType);
return false;
}
DEBUG_PRINT(F("initE: Attempting ETH config: ")); DEBUG_PRINTLN(ethernetType);
// Ethernet initialization should only succeed once -- else reboot required
ethernet_settings es = ethernetBoards[ethernetType];
managed_pin_type pinsToAllocate[10] = {
// first six pins are non-configurable
esp32_nonconfigurable_ethernet_pins[0],
esp32_nonconfigurable_ethernet_pins[1],
esp32_nonconfigurable_ethernet_pins[2],
esp32_nonconfigurable_ethernet_pins[3],
esp32_nonconfigurable_ethernet_pins[4],
esp32_nonconfigurable_ethernet_pins[5],
{ (int8_t)es.eth_mdc, true }, // [6] = MDC is output and mandatory
{ (int8_t)es.eth_mdio, true }, // [7] = MDIO is bidirectional and mandatory
{ (int8_t)es.eth_power, true }, // [8] = optional pin, not all boards use
{ ((int8_t)0xFE), false }, // [9] = replaced with eth_clk_mode, mandatory
};
// update the clock pin....
if (es.eth_clk_mode == ETH_CLOCK_GPIO0_IN) {
pinsToAllocate[9].pin = 0;
pinsToAllocate[9].isOutput = false;
} else if (es.eth_clk_mode == ETH_CLOCK_GPIO0_OUT) {
pinsToAllocate[9].pin = 0;
pinsToAllocate[9].isOutput = true;
} else if (es.eth_clk_mode == ETH_CLOCK_GPIO16_OUT) {
pinsToAllocate[9].pin = 16;
pinsToAllocate[9].isOutput = true;
} else if (es.eth_clk_mode == ETH_CLOCK_GPIO17_OUT) {
pinsToAllocate[9].pin = 17;
pinsToAllocate[9].isOutput = true;
} else {
DEBUG_PRINT(F("initE: Failing due to invalid eth_clk_mode ("));
DEBUG_PRINT(es.eth_clk_mode);
DEBUG_PRINTLN(F(")"));
return false;
}
if (!pinManager.allocateMultiplePins(pinsToAllocate, 10, PinOwner::Ethernet)) {
DEBUG_PRINTLN(F("initE: Failed to allocate ethernet pins"));
return false;
}
if (!ETH.begin(
(uint8_t) es.eth_address,
(int) es.eth_power,
(int) es.eth_mdc,
(int) es.eth_mdio,
(eth_phy_type_t) es.eth_type,
(eth_clock_mode_t) es.eth_clk_mode
)) {
DEBUG_PRINTLN(F("initC: ETH.begin() failed"));
// de-allocate the allocated pins
for (managed_pin_type mpt : pinsToAllocate) {
pinManager.deallocatePin(mpt.pin, PinOwner::Ethernet);
}
return false;
}
successfullyConfiguredEthernet = true;
DEBUG_PRINTLN(F("initC: *** Ethernet successfully configured! ***"));
return true;
#else
return false; // Ethernet not enabled for build
#endif
}
void WLED::initConnection() void WLED::initConnection()
{ {
#ifdef WLED_ENABLE_WEBSOCKETS #ifdef WLED_ENABLE_WEBSOCKETS
ws.onEvent(wsEvent); ws.onEvent(wsEvent);
#endif #endif
#if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_ETHERNET)
// Only initialize ethernet board if not NONE
if (ethernetType != WLED_ETH_NONE && ethernetType < WLED_NUM_ETH_TYPES) {
ethernet_settings es = ethernetBoards[ethernetType];
// Use PinManager to ensure pins are available for
// ethernet AND to prevent other uses of these pins.
bool s = true;
byte pinsAllocated[4] { 255, 255, 255, 255 };
if (s && (s = pinManager.allocatePin((byte)es.eth_power))) {
pinsAllocated[0] = (byte)es.eth_power;
}
if (s && (s = pinManager.allocatePin((byte)es.eth_mdc))) {
pinsAllocated[1] = (byte)es.eth_mdc;
}
if (s && (s = pinManager.allocatePin((byte)es.eth_mdio))) {
pinsAllocated[2] = (byte)es.eth_mdio;
}
switch(es.eth_clk_mode) {
case ETH_CLOCK_GPIO0_IN:
s = pinManager.allocatePin(0, false);
pinsAllocated[3] = 0;
break;
case ETH_CLOCK_GPIO0_OUT:
s = pinManager.allocatePin(0);
pinsAllocated[3] = 0;
break;
case ETH_CLOCK_GPIO16_OUT:
s = pinManager.allocatePin(16);
pinsAllocated[3] = 16;
break;
case ETH_CLOCK_GPIO17_OUT:
s = pinManager.allocatePin(17);
pinsAllocated[3] = 17;
break;
default:
s = false;
break;
}
if (s) {
s = ETH.begin(
(uint8_t) es.eth_address,
(int) es.eth_power,
(int) es.eth_mdc,
(int) es.eth_mdio,
(eth_phy_type_t) es.eth_type,
(eth_clock_mode_t) es.eth_clk_mode
);
}
if (!s) {
DEBUG_PRINTLN(F("Ethernet init failed"));
// de-allocate only those pins allocated before the failure
for (byte p : pinsAllocated) {
pinManager.deallocatePin(p);
}
}
}
#endif
WiFi.disconnect(true); // close old connections WiFi.disconnect(true); // close old connections
#ifdef ESP8266 #ifdef ESP8266
@ -509,7 +544,7 @@ void WLED::initConnection()
#endif #endif
if (staticIP[0] != 0 && staticGateway[0] != 0) { if (staticIP[0] != 0 && staticGateway[0] != 0) {
WiFi.config(staticIP, staticGateway, staticSubnet, IPAddress(8, 8, 8, 8)); WiFi.config(staticIP, staticGateway, staticSubnet, IPAddress(1, 1, 1, 1));
} else { } else {
WiFi.config(0U, 0U, 0U); WiFi.config(0U, 0U, 0U);
} }
@ -692,14 +727,25 @@ void WLED::handleConnection()
} }
} }
// If status LED pin is allocated for other uses, does nothing
// else blink at 1Hz when WLED_CONNECTED is false (no WiFi, ?? no Ethernet ??)
// else blink at 2Hz when MQTT is enabled but not connected
// else turn the status LED off
void WLED::handleStatusLED() void WLED::handleStatusLED()
{ {
#if STATUSLED #if STATUSLED
if (pinManager.isPinAllocated(STATUSLED)) return; //lower priority if something else uses the same pin static unsigned long ledStatusLastMillis = 0;
static unsigned short ledStatusType = 0; // current status type - corresponds to number of blinks per second
static bool ledStatusState = 0; // the current LED state
if (pinManager.isPinAllocated(STATUSLED)) {
return; //lower priority if something else uses the same pin
}
ledStatusType = WLED_CONNECTED ? 0 : 2; ledStatusType = WLED_CONNECTED ? 0 : 2;
if (mqttEnabled && ledStatusType != 2) // Wi-Fi takes presendence over MQTT if (mqttEnabled && ledStatusType != 2) { // Wi-Fi takes precendence over MQTT
ledStatusType = WLED_MQTT_CONNECTED ? 0 : 4; ledStatusType = WLED_MQTT_CONNECTED ? 0 : 4;
}
if (ledStatusType) { if (ledStatusType) {
if (millis() - ledStatusLastMillis >= (1000/ledStatusType)) { if (millis() - ledStatusLastMillis >= (1000/ledStatusType)) {
ledStatusLastMillis = millis(); ledStatusLastMillis = millis();

View File

@ -594,12 +594,6 @@ WLED_GLOBAL bool doInitBusses _INIT(false);
// Usermod manager // Usermod manager
WLED_GLOBAL UsermodManager usermods _INIT(UsermodManager()); WLED_GLOBAL UsermodManager usermods _INIT(UsermodManager());
// Status LED
#if STATUSLED
WLED_GLOBAL unsigned long ledStatusLastMillis _INIT(0);
WLED_GLOBAL unsigned short ledStatusType _INIT(0); // current status type - corresponds to number of blinks per second
WLED_GLOBAL bool ledStatusState _INIT(0); // the current LED state
#endif
// enable additional debug output // enable additional debug output
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
@ -663,6 +657,7 @@ public:
void beginStrip(); void beginStrip();
void handleConnection(); void handleConnection();
bool initEthernet(); // result is informational
void initAP(bool resetAP = false); void initAP(bool resetAP = false);
void initConnection(); void initConnection();
void initInterfaces(); void initInterfaces();

View File

@ -2,7 +2,32 @@
#define WLED_ETHERNET_H #define WLED_ETHERNET_H
#ifdef WLED_USE_ETHERNET #ifdef WLED_USE_ETHERNET
// settings for various ethernet boards #include "pin_manager.h"
// The following six pins are neither configurable nor
// can they be re-assigned through IOMUX / GPIO matrix.
// See https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-ethernet-kit-v1.1.html#ip101gri-phy-interface
const managed_pin_type esp32_nonconfigurable_ethernet_pins[6] = {
{ 21, true }, // RMII EMAC TX EN == When high, clocks the data on TXD0 and TXD1 to transmitter
{ 19, true }, // RMII EMAC TXD0 == First bit of transmitted data
{ 22, true }, // RMII EMAC TXD1 == Second bit of transmitted data
{ 25, false }, // RMII EMAC RXD0 == First bit of received data
{ 26, false }, // RMII EMAC RXD1 == Second bit of received data
{ 27, true }, // RMII EMAC CRS_DV == Carrier Sense and RX Data Valid
};
// For ESP32, the remaining five pins are at least somewhat configurable.
// eth_address is in range [0..31], indicates which PHY (MAC?) address should be allocated to the interface
// eth_power is an output GPIO pin used to enable/disable the ethernet port (and/or external oscillator)
// eth_mdc is an output GPIO pin used to provide the clock for the management data
// eth_mdio is an input/output GPIO pin used to transfer management data
// eth_type is the physical ethernet module's type (ETH_PHY_LAN8720, ETH_PHY_TLK110)
// eth_clk_mode defines the GPIO pin and GPIO mode for the clock signal
// However, there are really only four configurable options on ESP32:
// ETH_CLOCK_GPIO0_IN == External oscillator, clock input via GPIO0
// ETH_CLOCK_GPIO0_OUT == ESP32 provides 50MHz clock output via GPIO0
// ETH_CLOCK_GPIO16_OUT == ESP32 provides 50MHz clock output via GPIO16
// ETH_CLOCK_GPIO17_OUT == ESP32 provides 50MHz clock output via GPIO17
typedef struct EthernetSettings { typedef struct EthernetSettings {
uint8_t eth_address; uint8_t eth_address;
int eth_power; int eth_power;
@ -18,18 +43,9 @@ ethernet_settings ethernetBoards[] = {
}, },
// WT32-EHT01 // WT32-EHT01
// Please note, from my testing only these pins work for LED outputs: // (*) NOTE: silkscreen on board revision v1.2 may be wrong:
// IO2, IO4, IO12, IO14, IO15 // silkscreen on v1.2 says IO35, but appears to be IO5
// Pins IO34 through IO39 are input-only on ESP32, so // silkscreen on v1.2 says RXD, and appears to be IO35
// the following exposed pins won't work to drive WLEDs
// IO35(*), IO36, IO39
// The following pins are also exposed via the headers,
// and likely can be used as general purpose IO:
// IO05(*), IO32 (CFG), IO33 (485_EN), IO33 (TXD)
//
// (*) silkscreen on board revision v1.2 may be wrong:
// IO5 silkscreen on v1.2 says IO35
// IO35 silkscreen on v1.2 says RXD
{ {
1, // eth_address, 1, // eth_address,
16, // eth_power, 16, // eth_power,