diff --git a/platformio.ini b/platformio.ini index 30787a9f..36643c8f 100644 --- a/platformio.ini +++ b/platformio.ini @@ -8,12 +8,15 @@ # 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) -;default_envs = travis_esp8266, travis_esp32 +# 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 # Release binaries 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) ; default_envs = elekstube_ips ; default_envs = nodemcuv2 @@ -399,7 +402,7 @@ build_flags = ${common.build_flags_esp32} -D TEMPERATURE_PIN=23 lib_deps = ${esp32.lib_deps} OneWire@~2.3.5 - U8g2@~2.28.11 + olikraus/U8g2 @ ^2.28.8 [env:m5atom] 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 USER_SETUP_LOADED monitor_filters = esp32_exception_decoder -lib_deps = ${esp32.lib_deps} TFT_eSPI +lib_deps = + ${esp32.lib_deps} + TFT_eSPI @ ^2.3.70 diff --git a/usermods/Animated_Staircase/Animated_Staircase.h b/usermods/Animated_Staircase/Animated_Staircase.h index 88aa7793..87b5511d 100644 --- a/usermods/Animated_Staircase/Animated_Staircase.h +++ b/usermods/Animated_Staircase/Animated_Staircase.h @@ -306,22 +306,26 @@ class Animated_Staircase : public Usermod { public: 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 - if (topPIRorTriggerPin >= 0) { - if (!pinManager.allocatePin(topPIRorTriggerPin,useUSSensorTop)) - topPIRorTriggerPin = -1; - } - if (topEchoPin >= 0) { - if (!pinManager.allocatePin(topEchoPin,false)) - topEchoPin = -1; - } - if (bottomPIRorTriggerPin >= 0) { - if (!pinManager.allocatePin(bottomPIRorTriggerPin,useUSSensorBottom)) - bottomPIRorTriggerPin = -1; - } - if (bottomEchoPin >= 0) { - if (!pinManager.allocatePin(bottomEchoPin,false)) - bottomEchoPin = -1; + PinManagerPinType pins[4] = { + { topPIRorTriggerPin, useUSSensorTop }, + { topEchoPin, false }, + { bottomPIRorTriggerPin, useUSSensorBottom }, + { bottomEchoPin, false }, + }; + // NOTE: this *WILL* return TRUE if all the pins are set to -1. + // this is *BY DESIGN*. + if (!pinManager.allocateMultiplePins(pins, 4, PinOwner::UM_AnimatedStaircase)) { + topPIRorTriggerPin = -1; + topEchoPin = -1; + bottomPIRorTriggerPin = -1; + bottomEchoPin = -1; + enabled = false; } enable(enabled); initDone = true; @@ -484,10 +488,10 @@ class Animated_Staircase : public Usermod { (oldBottomAPin != bottomPIRorTriggerPin) || (oldBottomBPin != bottomEchoPin)) { changed = true; - pinManager.deallocatePin(oldTopAPin); - pinManager.deallocatePin(oldTopBPin); - pinManager.deallocatePin(oldBottomAPin); - pinManager.deallocatePin(oldBottomBPin); + pinManager.deallocatePin(oldTopAPin, PinOwner::UM_AnimatedStaircase); + pinManager.deallocatePin(oldTopBPin, PinOwner::UM_AnimatedStaircase); + pinManager.deallocatePin(oldBottomAPin, PinOwner::UM_AnimatedStaircase); + pinManager.deallocatePin(oldBottomBPin, PinOwner::UM_AnimatedStaircase); } if (changed) setup(); } diff --git a/usermods/EleksTube_IPS/ChipSelect.h b/usermods/EleksTube_IPS/ChipSelect.h index c58b2854..ced5eb8e 100644 --- a/usermods/EleksTube_IPS/ChipSelect.h +++ b/usermods/EleksTube_IPS/ChipSelect.h @@ -58,12 +58,12 @@ public: void setMinutesTens() { setDigit(MINUTES_TENS); } void setHoursOnes() { setDigit(HOURS_ONES); } void setHoursTens() { setDigit(HOURS_TENS); } - bool isSecondsOnes() { return (digits_map&SECONDS_ONES_MAP > 0); } - bool isSecondsTens() { return (digits_map&SECONDS_TENS_MAP > 0); } - bool isMinutesOnes() { return (digits_map&MINUTES_ONES_MAP > 0); } - bool isMinutesTens() { return (digits_map&MINUTES_TENS_MAP > 0); } - bool isHoursOnes() { return (digits_map&HOURS_ONES_MAP > 0); } - bool isHoursTens() { return (digits_map&HOURS_TENS_MAP > 0); } + bool isSecondsOnes() { return ((digits_map & SECONDS_ONES_MAP) > 0); } + bool isSecondsTens() { return ((digits_map & SECONDS_TENS_MAP) > 0); } + bool isMinutesOnes() { return ((digits_map & MINUTES_ONES_MAP) > 0); } + bool isMinutesTens() { return ((digits_map & MINUTES_TENS_MAP) > 0); } + bool isHoursOnes() { return ((digits_map & HOURS_ONES_MAP) > 0); } + bool isHoursTens() { return ((digits_map & HOURS_TENS_MAP) > 0); } }; diff --git a/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h b/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h index b36a2891..6682dde3 100644 --- a/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h +++ b/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h @@ -201,12 +201,14 @@ public: { if (enabled) { // 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 pinMode(PIRsensorPin, INPUT_PULLUP); sensorPinState = digitalRead(PIRsensorPin); } 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 enabled = false; } @@ -367,8 +369,8 @@ public: if (oldPin != PIRsensorPin && oldPin >= 0) { // if we are changing pin in settings page // deallocate old pin - pinManager.deallocatePin(oldPin); - if (pinManager.allocatePin(PIRsensorPin,false)) { + pinManager.deallocatePin(oldPin, PinOwner::UM_PIR); + if (pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) { pinMode(PIRsensorPin, INPUT_PULLUP); } else { // allocation failed diff --git a/usermods/Temperature/usermod_temperature.h b/usermods/Temperature/usermod_temperature.h index ad55b49e..7c209f47 100644 --- a/usermods/Temperature/usermod_temperature.h +++ b/usermods/Temperature/usermod_temperature.h @@ -117,14 +117,19 @@ class UsermodTemperature : public Usermod { // config says we are enabled DEBUG_PRINTLN(F("Allocating temperature pin...")); // 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); - if (!oneWire->reset()) + if (!oneWire->reset()) { sensorFound = false; // resetting 1-Wire bus yielded an error - else - while ((sensorFound=findSensor()) && retries--) delay(25); // try to find sensor + } else { + while ((sensorFound=findSensor()) && retries--) { + delay(25); // try to find sensor + } + } } 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 sensorFound = false; } @@ -275,7 +280,7 @@ class UsermodTemperature : public Usermod { DEBUG_PRINTLN(F("Re-init temperature.")); // deallocate pin and release memory delete oneWire; - pinManager.deallocatePin(temperaturePin); + pinManager.deallocatePin(temperaturePin, PinOwner::UM_Temperature); temperaturePin = newTemperaturePin; // initialise setup(); diff --git a/usermods/multi_relay/usermod_multi_relay.h b/usermods/multi_relay/usermod_multi_relay.h index 0820cac3..52fbd7f9 100644 --- a/usermods/multi_relay/usermod_multi_relay.h +++ b/usermods/multi_relay/usermod_multi_relay.h @@ -258,7 +258,7 @@ class MultiRelay : public Usermod { // pins retrieved from cfg.json (readFromConfig()) prior to running setup() for (uint8_t i=0; i=0) { - pinManager.deallocatePin(oldPin[i]); + pinManager.deallocatePin(oldPin[i], PinOwner::UM_MultiRelay); } // allocate new pins for (uint8_t i=0; i=0 && pinManager.allocatePin(_relay[i].pin,true)) { - if (!_relay[i].external) switchRelay(i, _relay[i].state = (bool)bri); + 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); + } } else { _relay[i].pin = -1; } diff --git a/usermods/rgb-rotary-encoder/rgb-rotary-encoder.h b/usermods/rgb-rotary-encoder/rgb-rotary-encoder.h index ab680a8f..cde22012 100644 --- a/usermods/rgb-rotary-encoder/rgb-rotary-encoder.h +++ b/usermods/rgb-rotary-encoder/rgb-rotary-encoder.h @@ -13,7 +13,7 @@ class RgbRotaryEncoderUsermod : public Usermod BusDigital *ledBus; /* * Green - eb - Q4 - 32 - * Red - ea - Q1 - 15 + * Red - ea - Q1 - 15 * Black - sw - Q2 - 12 */ ESPRotary *rotaryEncoder; @@ -39,13 +39,10 @@ class RgbRotaryEncoderUsermod : public Usermod void initRotaryEncoder() { - if (!pinManager.allocatePin(eaIo, false)) { + PinManagerPinType pins[2] = { { eaIo, false }, { ebIo, false } }; + if (!pinManager.allocateMultiplePins(pins, 2, UM_RGBRotaryEncoder)) { eaIo = -1; - } - if (!pinManager.allocatePin(ebIo, false)) { ebIo = -1; - } - if (eaIo == -1 || ebIo == -1) { cleanup(); return; } @@ -111,10 +108,12 @@ class RgbRotaryEncoderUsermod : public Usermod { // Only deallocate pins if we allocated them ;) if (eaIo != -1) { - pinManager.deallocatePin(eaIo); + pinManager.deallocatePin(eaIo, PinOwner::UM_RGBRotaryEncoder); + eaIo = -1; } if (ebIo != -1) { - pinManager.deallocatePin(ebIo); + pinManager.deallocatePin(ebIo, PinOwner::UM_RGBRotaryEncoder); + ebIo = -1; } delete rotaryEncoder; @@ -304,8 +303,8 @@ class RgbRotaryEncoderUsermod : public Usermod } if (eaIo != oldEaIo || ebIo != oldEbIo || stepsPerClick != oldStepsPerClick || incrementPerClick != oldIncrementPerClick) { - pinManager.deallocatePin(oldEaIo); - pinManager.deallocatePin(oldEbIo); + pinManager.deallocatePin(oldEaIo, PinOwner::UM_RGBRotaryEncoder); + pinManager.deallocatePin(oldEbIo, PinOwner::UM_RGBRotaryEncoder); delete rotaryEncoder; initRotaryEncoder(); diff --git a/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h b/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h index 833ed101..2089db93 100644 --- a/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h +++ b/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h @@ -168,14 +168,12 @@ class FourLineDisplayUsermod : public Usermod { // network here void setup() { if (type == NONE) return; - bool allocated = false; - byte i; if (type == SSD1306_SPI || type == SSD1306_SPI64) { - for (i=0; i<5; i++) if (!pinManager.allocatePin(ioPin[i])) { allocated=true; break; } - if (i<5 && allocated) { for (byte i=0; i<5; i++) pinManager.deallocatePin(ioPin[i]); type=NONE; return; } + PinManagerPinType pins[5] = { { ioPin[0], true }, { ioPin[1], true}, { ioPin[2], true }, { ioPin[3], true}, { ioPin[4], true }}; + if (!pinManager.allocateMultiplePins(pins, 5, PinOwner::UM_FourLineDisplay)) { type=NONE; return; } } else { - for (i=0; i<2; i++) if (!pinManager.allocatePin(ioPin[i])) { allocated=true; break; } - if (i<2 && allocated) { for (byte i=0; i<5; i++) pinManager.deallocatePin(ioPin[i]); type=NONE; return; } + PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true} }; + if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::UM_FourLineDisplay)) { type=NONE; return; } } DEBUG_PRINTLN(F("Allocating display.")); switch (type) { @@ -240,18 +238,17 @@ class FourLineDisplayUsermod : public Usermod { break; default: u8x8 = nullptr; + } + if (nullptr == u8x8) { + DEBUG_PRINTLN(F("Display init failed.")); + for (byte i=0; i<5 && ioPin[i]>=0; i++) pinManager.deallocatePin(ioPin[i], PinOwner::UM_FourLineDisplay); type = NONE; return; } + initDone = true; - if (u8x8 != nullptr) { - DEBUG_PRINTLN(F("Starting display.")); - (static_cast(u8x8))->begin(); - } else { - DEBUG_PRINTLN(F("Display init failed.")); - type = NONE; - return; - } + DEBUG_PRINTLN(F("Starting display.")); + u8x8->begin(); setFlipMode(flip); setContrast(contrast); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255 setPowerSave(0); @@ -277,40 +274,40 @@ class FourLineDisplayUsermod : public Usermod { */ void setFlipMode(uint8_t mode) { if (type==NONE) return; - (static_cast(u8x8))->setFlipMode(mode); + u8x8->setFlipMode(mode); } void setContrast(uint8_t contrast) { if (type==NONE) return; - (static_cast(u8x8))->setContrast(contrast); + u8x8->setContrast(contrast); } void drawString(uint8_t col, uint8_t row, const char *string, bool ignoreLH=false) { if (type==NONE) return; - (static_cast(u8x8))->setFont(u8x8_font_chroma48medium8_r); - if (!ignoreLH && lineHeight==2) (static_cast(u8x8))->draw1x2String(col, row, string); - else (static_cast(u8x8))->drawString(col, row, string); + u8x8->setFont(u8x8_font_chroma48medium8_r); + if (!ignoreLH && lineHeight==2) u8x8->draw1x2String(col, row, string); + else u8x8->drawString(col, row, string); } void draw2x2String(uint8_t col, uint8_t row, const char *string) { if (type==NONE) return; - (static_cast(u8x8))->setFont(u8x8_font_chroma48medium8_r); - (static_cast(u8x8))->draw2x2String(col, row, string); + u8x8->setFont(u8x8_font_chroma48medium8_r); + u8x8->draw2x2String(col, row, string); } void drawGlyph(uint8_t col, uint8_t row, char glyph, const uint8_t *font, bool ignoreLH=false) { if (type==NONE) return; - (static_cast(u8x8))->setFont(font); - if (!ignoreLH && lineHeight==2) (static_cast(u8x8))->draw1x2Glyph(col, row, glyph); - else (static_cast(u8x8))->drawGlyph(col, row, glyph); + u8x8->setFont(font); + if (!ignoreLH && lineHeight==2) u8x8->draw1x2Glyph(col, row, glyph); + else u8x8->drawGlyph(col, row, glyph); } uint8_t getCols() { if (type==NONE) return 0; - return (static_cast(u8x8))->getCols(); + return u8x8->getCols(); } void clear() { if (type==NONE) return; - (static_cast(u8x8))->clear(); + u8x8->clear(); } void setPowerSave(uint8_t save) { if (type==NONE) return; - (static_cast(u8x8))->setPowerSave(save); + u8x8->setPowerSave(save); } void center(String &line, uint8_t width) { @@ -725,9 +722,9 @@ class FourLineDisplayUsermod : public Usermod { bool pinsChanged = false; for (byte i=0; i<5; i++) if (ioPin[i] != newPin[i]) { pinsChanged = true; break; } if (pinsChanged || type!=newType) { - if (type != NONE) delete (static_cast(u8x8)); + if (type != NONE) delete u8x8; 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]; } if (ioPin[0]<0 || ioPin[1]<0) { // data & clock must be > -1 diff --git a/usermods/usermod_v2_rotary_encoder_ui/usermod_v2_rotary_encoder_ui.h b/usermods/usermod_v2_rotary_encoder_ui/usermod_v2_rotary_encoder_ui.h index 5c8dd70e..bf909bdc 100644 --- a/usermods/usermod_v2_rotary_encoder_ui/usermod_v2_rotary_encoder_ui.h +++ b/usermods/usermod_v2_rotary_encoder_ui/usermod_v2_rotary_encoder_ui.h @@ -76,7 +76,7 @@ private: unsigned char Enc_B; unsigned char Enc_A_prev = 0; - bool currentEffectAndPaleeteInitialized = false; + bool currentEffectAndPaletteInitialized = false; uint8_t effectCurrentIndex = 0; uint8_t effectPaletteIndex = 0; @@ -97,9 +97,17 @@ public: */ void setup() { - if (!pinManager.allocatePin(pinA)) { enabled = false; return;} - if (!pinManager.allocatePin(pinB)) { pinManager.deallocatePin(pinA); enabled = false; return; } - if (!pinManager.allocatePin(pinC)) { pinManager.deallocatePin(pinA); pinManager.deallocatePin(pinB); enabled = false; return; } + PinManagerPinType pins[3] = { { pinA, false }, { pinB, false }, { pinC, false } }; + if (!pinManager.allocateMultiplePins(pins, 3, PinOwner::UM_RotaryEncoderUI)) { + // 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(pinB, INPUT_PULLUP); @@ -152,7 +160,7 @@ public: // Initialize effectCurrentIndex and effectPaletteIndex to // current state. We do it here as (at least) effectCurrent // is not yet initialized when setup is called. - if (!currentEffectAndPaleeteInitialized) { + if (!currentEffectAndPaletteInitialized) { findCurrentEffectAndPalette(); } @@ -248,7 +256,7 @@ public: } void findCurrentEffectAndPalette() { - currentEffectAndPaleeteInitialized = true; + currentEffectAndPaletteInitialized = true; for (uint8_t i = 0; i < strip.getModeCount(); i++) { //byte value = modes_alpha_indexes[i]; if (modes_alpha_indexes[i] == effectCurrent) { @@ -455,9 +463,9 @@ public: DEBUG_PRINTLN(F(" config (re)loaded.")); // changing parameters from settings page if (pinA!=newDTpin || pinB!=newCLKpin || pinC!=newSWpin) { - pinManager.deallocatePin(pinA); - pinManager.deallocatePin(pinB); - pinManager.deallocatePin(pinC); + pinManager.deallocatePin(pinA, PinOwner::UM_RotaryEncoderUI); + pinManager.deallocatePin(pinB, PinOwner::UM_RotaryEncoderUI); + pinManager.deallocatePin(pinC, PinOwner::UM_RotaryEncoderUI); pinA = newDTpin; pinB = newCLKpin; pinC = newSWpin; diff --git a/wled00/FX.h b/wled00/FX.h index 57d4cf04..cacd3e8d 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -916,7 +916,9 @@ const char JSON_palette_names[] PROGMEM = R"=====([ "Pastel","Sunset 2","Beech","Vintage","Departure","Landscape","Beach","Sherbet","Hult","Hult 64", "Drywet","Jul","Grintage","Rewhi","Tertiary","Fire","Icefire","Cyane","Light Pink","Autumn", "Magenta","Magred","Yelmag","Yelblu","Orange & Teal","Tiamat","April Night","Orangery","C9","Sakura", -"Aurora","Atlantica","C9 2","C9 New","Temperature","Aurora 2" +"Aurora","Atlantica","C9 2","C9 New","Temperature","Aurora 2","Retro Clown","Candy","Toxy Reaf","Fairy Reaf", +"Semi Blue","Pink Candy","Red Reaf","Aqua Flash","Yelblu Hot","Lite Light","Red Flash","Blink Red","Red Shift","Red Tide", +"Candy2" ])====="; #endif diff --git a/wled00/bus_manager.h b/wled00/bus_manager.h index 2172f667..f607a001 100644 --- a/wled00/bus_manager.h +++ b/wled00/bus_manager.h @@ -125,10 +125,10 @@ class BusDigital : public Bus { public: BusDigital(BusConfig &bc, uint8_t nr) : Bus(bc.type, bc.start) { 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]; if (IS_2PIN(bc.type)) { - if (!pinManager.allocatePin(bc.pins[1])) { + if (!pinManager.allocatePin(bc.pins[1], true, PinOwner::BusDigital)) { cleanup(); return; } _pins[1] = bc.pins[1]; @@ -208,11 +208,11 @@ class BusDigital : public Bus { void cleanup() { PolyBus::cleanup(_busPtr, _iType); - pinManager.deallocatePin(_pins[0]); - pinManager.deallocatePin(_pins[1]); _iType = I_NONE; _valid = false; _busPtr = nullptr; + pinManager.deallocatePin(_pins[1], PinOwner::BusDigital); + pinManager.deallocatePin(_pins[0], PinOwner::BusDigital); } ~BusDigital() { @@ -249,7 +249,7 @@ class BusPwm : public Bus { for (uint8_t i = 0; i < numPins; i++) { uint8_t currentPin = bc.pins[i]; - if (!pinManager.allocatePin(currentPin)) { + if (!pinManager.allocatePin(currentPin, true, PinOwner::BusPwm)) { deallocatePins(); return; } _pins[i] = currentPin; // store only after allocatePin() succeeds @@ -339,7 +339,7 @@ class BusPwm : public Bus { void deallocatePins() { uint8_t numPins = NUM_PWM_PINS(_type); for (uint8_t i = 0; i < numPins; i++) { - pinManager.deallocatePin(_pins[i]); + pinManager.deallocatePin(_pins[i], PinOwner::BusPwm); if (!pinManager.isPinOk(_pins[i])) continue; #ifdef ESP8266 digitalWrite(_pins[i], LOW); //turn off PWM interrupt @@ -469,4 +469,4 @@ class BusManager { uint8_t numBusses = 0; Bus* busses[WLED_MAX_BUSSES]; }; -#endif \ No newline at end of file +#endif diff --git a/wled00/cfg.cpp b/wled00/cfg.cpp index 215c3795..4d4b5995 100644 --- a/wled00/cfg.cpp +++ b/wled00/cfg.cpp @@ -19,6 +19,13 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { //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"]; getStringFromJson(cmDNS, id[F("mdns")], 33); getStringFromJson(serverDescription, id[F("name")], 33); @@ -93,7 +100,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { 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")]; CJSON(ledCount, hw_led[F("total")]); @@ -162,7 +169,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { for (JsonObject btn : hw_btn_ins) { CJSON(buttonType[s], btn["type"]); 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; pinMode(btnPin[s], INPUT_PULLUP); } else { @@ -186,8 +193,10 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { // new install/missing configuration (button 0 has defaults) if (fromFS) { // relies upon only being called once with fromFS == true, which is currently true. - uint8_t s=0; - if (pinManager.allocatePin(btnPin[0],false)) s++; // do not clear button 0 if pin successfully allocated + uint8_t s = 0; + 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 + } for (; s -2) { - if (pinManager.allocatePin(hw_ir_pin,false)) { + if (pinManager.allocatePin(hw_ir_pin, false, PinOwner::IR)) { irPin = hw_ir_pin; } else { irPin = -1; @@ -213,7 +222,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { JsonObject relay = hw[F("relay")]; int hw_relay_pin = 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; pinMode(rlyPin, OUTPUT); } else { @@ -469,6 +478,8 @@ void deserializeConfigFromFS() { return; } + // NOTE: This routine deserializes *and* applies the configuration + // Therefore, must also initialize ethernet from this function deserializeConfig(doc.as(), true); } diff --git a/wled00/const.h b/wled00/const.h index 02ca6e21..99174f25 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -292,7 +292,7 @@ #ifdef ESP8266 #define LEDPIN 2 // GPIO2 (D4) on Wemod D1 mini compatible boards #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 diff --git a/wled00/overlay.cpp b/wled00/overlay.cpp index 8917b2a8..98339686 100644 --- a/wled00/overlay.cpp +++ b/wled00/overlay.cpp @@ -359,7 +359,7 @@ void _drawOverlayCronixie() } #else // WLED_DISABLE_CRONIXIE -byte getSameCodeLength(char code, int index, char const cronixieDisplay[]) {return 0;} +byte getSameCodeLength(char code, int index, char const cronixieDisplay[]) { return 0; } void setCronixie() {} void _overlayCronixie() {} void _drawOverlayCronixie() {} diff --git a/wled00/palettes.h b/wled00/palettes.h index 80b0ea53..c9bdf3f6 100644 --- a/wled00/palettes.h +++ b/wled00/palettes.h @@ -13,7 +13,7 @@ #ifndef PalettesWLED_h #define PalettesWLED_h -#define GRADIENT_PALETTE_COUNT 43 +#define GRADIENT_PALETTE_COUNT 58 const byte ib_jul01_gp[] PROGMEM = { 0, 194, 1, 1, @@ -631,7 +631,7 @@ const byte temperature_gp[] PROGMEM = { 240, 80, 3, 3, 255, 80, 3, 3}; - const byte Aurora2[] PROGMEM = { + const byte Aurora2_gp[] PROGMEM = { 0, 17, 177, 13, //Greenish 64, 121, 242, 5, //Greenish 128, 25, 173, 121, //Turquoise @@ -639,6 +639,213 @@ const byte temperature_gp[] PROGMEM = { 255, 171, 101, 221 //Purple }; + // Gradient palette "bhw1_01_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/bhw/bhw1/tn/bhw1_01.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 12 bytes of program space. + +const byte retro_clown_gp[] PROGMEM = { + 0, 227,101, 3, + 117, 194, 18, 19, + 255, 92, 8,192}; + +// Gradient palette "bhw1_04_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/bhw/bhw1/tn/bhw1_04.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 20 bytes of program space. + +const byte candy_gp[] PROGMEM = { + 0, 229,227, 1, + 15, 227,101, 3, + 142, 40, 1, 80, + 198, 17, 1, 79, + 255, 0, 0, 45}; + +// Gradient palette "bhw1_05_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/bhw/bhw1/tn/bhw1_05.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 8 bytes of program space. + +const byte toxy_reaf_gp[] PROGMEM = { + 0, 1,221, 53, + 255, 73, 3,178}; + +// Gradient palette "bhw1_06_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/bhw/bhw1/tn/bhw1_06.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 16 bytes of program space. + +const byte fairy_reaf_gp[] PROGMEM = { + 0, 184, 1,128, + 160, 1,193,182, + 219, 153,227,190, + 255, 255,255,255}; + +// Gradient palette "bhw1_14_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/bhw/bhw1/tn/bhw1_14.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 36 bytes of program space. + +const byte semi_blue_gp[] PROGMEM = { + 0, 0, 0, 0, + 12, 1, 1, 3, + 53, 8, 1, 22, + 80, 4, 6, 89, + 119, 2, 25,216, + 145, 7, 10, 99, + 186, 15, 2, 31, + 233, 2, 1, 5, + 255, 0, 0, 0}; + +// Gradient palette "bhw1_three_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/bhw/bhw1/tn/bhw1_three.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 32 bytes of program space. + +const byte pink_candy_gp[] PROGMEM = { + 0, 255,255,255, + 45, 7, 12,255, + 112, 227, 1,127, + 112, 227, 1,127, + 140, 255,255,255, + 155, 227, 1,127, + 196, 45, 1, 99, + 255, 255,255,255}; + +// Gradient palette "bhw1_w00t_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/bhw/bhw1/tn/bhw1_w00t.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 16 bytes of program space. + +const byte red_reaf_gp[] PROGMEM = { + 0, 3, 13, 43, + 104, 78,141,240, + 188, 255, 0, 0, + 255, 28, 1, 1}; + + +// Gradient palette "bhw2_23_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/bhw/bhw2/tn/bhw2_23.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Red & Flash in SR +// Size: 28 bytes of program space. + +const byte aqua_flash_gp[] PROGMEM = { + 0, 0, 0, 0, + 66, 57,227,233, + 96, 255,255, 8, + 124, 255,255,255, + 153, 255,255, 8, + 188, 57,227,233, + 255, 0, 0, 0}; + +// Gradient palette "bhw2_xc_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/bhw/bhw2/tn/bhw2_xc.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// YBlue in SR +// Size: 28 bytes of program space. + +const byte yelblu_hot_gp[] PROGMEM = { + 0, 4, 2, 9, + 58, 16, 0, 47, + 122, 24, 0, 16, + 158, 144, 9, 1, + 183, 179, 45, 1, + 219, 220,114, 2, + 255, 234,237, 1}; + + // Gradient palette "bhw2_45_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/bhw/bhw2/tn/bhw2_45.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 24 bytes of program space. + +const byte lite_light_gp[] PROGMEM = { + 0, 0, 0, 0, + 9, 1, 1, 1, + 40, 5, 5, 6, + 66, 5, 5, 6, + 101, 10, 1, 12, + 255, 0, 0, 0}; + +// Gradient palette "bhw2_22_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/bhw/bhw2/tn/bhw2_22.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Pink Plasma in SR +// Size: 20 bytes of program space. + +const byte red_flash_gp[] PROGMEM = { + 0, 0, 0, 0, + 99, 227, 1, 1, + 130, 249,199, 95, + 155, 227, 1, 1, + 255, 0, 0, 0}; + +// Gradient palette "bhw3_40_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/bhw/bhw3/tn/bhw3_40.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 32 bytes of program space. + +const byte blink_red_gp[] PROGMEM = { + 0, 1, 1, 1, + 43, 4, 1, 11, + 76, 10, 1, 3, + 109, 161, 4, 29, + 127, 255, 86,123, + 165, 125, 16,160, + 204, 35, 13,223, + 255, 18, 2, 18}; + +// Gradient palette "bhw3_52_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/bhw/bhw3/tn/bhw3_52.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Yellow2Blue in SR +// Size: 28 bytes of program space. + +const byte red_shift_gp[] PROGMEM = { + 0, 31, 1, 27, + 45, 34, 1, 16, + 99, 137, 5, 9, + 132, 213,128, 10, + 175, 199, 22, 1, + 201, 199, 9, 6, + 255, 1, 0, 1}; + +// Gradient palette "bhw4_097_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/bhw/bhw4/tn/bhw4_097.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Yellow2Red in SR +// Size: 44 bytes of program space. + +const byte red_tide_gp[] PROGMEM = { + 0, 247, 5, 0, + 28, 255, 67, 1, + 43, 234, 88, 11, + 58, 234,176, 51, + 84, 229, 28, 1, + 114, 113, 12, 1, + 140, 255,225, 44, + 168, 113, 12, 1, + 196, 244,209, 88, + 216, 255, 28, 1, + 255, 53, 1, 1}; + +// Gradient palette "bhw4_017_gp", originally from +// http://soliton.vm.bytemark.co.uk/pub/cpt-city/bhw/bhw4/tn/bhw4_017.png.index.html +// converted for FastLED with gammas (2.6, 2.2, 2.5) +// Size: 40 bytes of program space. + +const byte candy2_gp[] PROGMEM = { + 0, 39, 33, 34, + 25, 4, 6, 15, + 48, 49, 29, 22, + 73, 224,173, 1, + 89, 177, 35, 5, + 130, 4, 6, 15, + 163, 255,114, 6, + 186, 224,173, 1, + 211, 39, 33, 34, + 255, 1, 1, 1}; + // Single array of defined cpt-city color palettes. // This will let us programmatically choose one based on // a number, rather than having to activate each explicitly @@ -686,7 +893,22 @@ const byte* const gGradientPalettes[] PROGMEM = { C9_2_gp, //52-39 C9 2 C9_new_gp, //53-40 C9 New temperature_gp, //54-41 Temperature - Aurora2 //55-42 Aurora 2 + Aurora2_gp, //55-42 Aurora 2 + retro_clown_gp, //56-43 Retro Clown + candy_gp, //57-44 Candy + toxy_reaf_gp, //58-45 Toxy Reaf + fairy_reaf_gp, //59-46 Fairy Reaf + semi_blue_gp, //60-47 Semi Blue + pink_candy_gp, //61-48 Pink Candy + red_reaf_gp, //62-49 Red Reaf + aqua_flash_gp, //63-50 Aqua Flash + yelblu_hot_gp, //64-51 Yelblu Hot + lite_light_gp, //65-52 Lite Light + red_flash_gp, //66-53 Red Flash + blink_red_gp, //67-54 Blink Red + red_shift_gp, //68-55 Red Shift + red_tide_gp, //69-56 Red Tide + candy2_gp //70-57 Candy2 }; #endif diff --git a/wled00/pin_manager.cpp b/wled00/pin_manager.cpp index d260ae95..38e6e162 100644 --- a/wled00/pin_manager.cpp +++ b/wled00/pin_manager.cpp @@ -1,35 +1,110 @@ #include "pin_manager.h" #include "wled.h" -void PinManagerClass::deallocatePin(byte gpio) +static void DebugPrintOwnerTag(PinOwner tag) { - if (!isPinOk(gpio, false)) return; + uint32_t q = static_cast(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 bi = gpio - 8*by; 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 (isPinAllocated(gpio)) { - DEBUG_PRINT(F("Attempted duplicate allocation of pin ")); - DEBUG_PRINTLN(gpio); + DEBUG_PRINT(F("PIN ALLOC: Pin ")); + DEBUG_PRINT(gpio); + DEBUG_PRINT(F(" already allocated by ")); + DebugPrintOwnerTag(ownerTag[gpio]); + DEBUG_PRINTLN(F("")); return false; } byte by = gpio >> 3; byte bi = gpio - 8*by; bitWrite(pinAlloc[by], bi, true); + ownerTag[gpio] = tag; 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 ((tag != PinOwner::None) && (ownerTag[gpio] != tag)) return false; byte by = gpio >> 3; byte bi = gpio - 8*by; return bitRead(pinAlloc[by], bi); @@ -46,7 +121,7 @@ bool PinManagerClass::isPinOk(byte gpio, bool output) if (gpio < 34) return true; if (gpio < 40 && !output) return true; //34-39 input only #endif - + return false; } @@ -88,4 +163,4 @@ void PinManagerClass::deallocateLedc(byte pos, byte channels) } #endif -PinManagerClass pinManager = PinManagerClass(); \ No newline at end of file +PinManagerClass pinManager = PinManagerClass(); diff --git a/wled00/pin_manager.h b/wled00/pin_manager.h index 601c4be3..aba1ae9d 100644 --- a/wled00/pin_manager.h +++ b/wled00/pin_manager.h @@ -4,21 +4,92 @@ * Registers pins so there is no attempt for two interfaces to use the same pin */ #include +#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 + 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(PinOwner::None), "PinOwner::None must be zero, so default array initialization works as expected"); class PinManagerClass { private: #ifdef ESP8266 uint8_t pinAlloc[3] = {0x00, 0x00, 0x00}; //24bit, 1 bit per pin, we use first 17bits + PinOwner ownerTag[17] = { PinOwner::None }; #else 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 + PinOwner ownerTag[40] = { PinOwner::None }; #endif public: - void deallocatePin(byte gpio); - bool allocatePin(byte gpio, bool output = true); - bool isPinAllocated(byte gpio); + // De-allocates a single pin + bool deallocatePin(byte gpio, PinOwner tag); + // 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); + #ifdef ARDUINO_ARCH_ESP32 byte allocateLedc(byte channels); void deallocateLedc(byte pos, byte channels); @@ -26,4 +97,4 @@ class PinManagerClass { }; extern PinManagerClass pinManager; -#endif \ No newline at end of file +#endif diff --git a/wled00/set.cpp b/wled00/set.cpp index 99579122..94fcec12 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -54,6 +54,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) #ifdef WLED_USE_ETHERNET ethernetType = request->arg(F("ETH")).toInt(); + WLED::instance().initEthernet(); #endif char k[3]; k[2] = 0; @@ -77,11 +78,17 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) { int t = 0; - if (rlyPin>=0 && pinManager.isPinAllocated(rlyPin)) pinManager.deallocatePin(rlyPin); - if (irPin>=0 && pinManager.isPinAllocated(irPin)) pinManager.deallocatePin(irPin); - for (uint8_t s=0; s=0 && pinManager.isPinAllocated(btnPin[s])) - pinManager.deallocatePin(btnPin[s]); + if (rlyPin>=0 && pinManager.isPinAllocated(rlyPin, PinOwner::Relay)) { + pinManager.deallocatePin(rlyPin, PinOwner::Relay); + } + if (irPin>=0 && pinManager.isPinAllocated(irPin, PinOwner::IR)) { + pinManager.deallocatePin(irPin, PinOwner::IR); + } + for (uint8_t s=0; s=0 && pinManager.isPinAllocated(btnPin[s], PinOwner::Button)) { + pinManager.deallocatePin(btnPin[s], PinOwner::Button); + } + } uint8_t colorOrder, type, skip; uint16_t length, start; @@ -126,7 +133,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) // upate other pins 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; } else { irPin = -1; @@ -134,7 +141,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) irEnabled = request->arg(F("IT")).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; } else { rlyPin = -1; @@ -145,7 +152,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) 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 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; pinMode(btnPin[i], INPUT_PULLUP); buttonType[i] = request->arg(be).toInt(); diff --git a/wled00/wled.cpp b/wled00/wled.cpp index 4a4f9136..f8fd6482 100644 --- a/wled00/wled.cpp +++ b/wled00/wled.cpp @@ -177,6 +177,11 @@ void WiFiEvent(WiFiEvent_t event) break; case SYSTEM_EVENT_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; break; #endif @@ -351,20 +356,21 @@ void WLED::setup() registerUsermods(); #if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM) - if (psramFound()) { - pinManager.allocatePin(16); // GPIO16 reserved for SPI RAM - pinManager.allocatePin(17); // GPIO17 reserved for SPI RAM - } + if (psramFound()) { + // GPIO16/GPIO17 reserved for SPI RAM + managed_pin_type pins[2] = { {16, true}, {17, true} }; + pinManager.allocateMultiplePins(pins, 2, PinOwner::SPI_RAM); + } #endif //DEBUG_PRINT(F("LEDs inited. heap usage ~")); //DEBUG_PRINTLN(heapPreAlloc - ESP.getFreeHeap()); #ifdef WLED_DEBUG - pinManager.allocatePin(1,true); // GPIO1 reserved for debug output + pinManager.allocatePin(1, true, PinOwner::DebugOut); // GPIO1 reserved for debug output #endif #ifdef WLED_USE_DMX //reserve GPIO2 as hardcoded DMX pin - pinManager.allocatePin(2); + pinManager.allocatePin(2, true, PinOwner::DMX); #endif for (uint8_t i=1; i= 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() { #ifdef WLED_ENABLE_WEBSOCKETS ws.onEvent(wsEvent); #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 #ifdef ESP8266 @@ -582,7 +613,7 @@ void WLED::initConnection() #endif 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 { WiFi.config(0U, 0U, 0U); } diff --git a/wled00/wled.h b/wled00/wled.h index d9e96800..91705289 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2108251 +#define VERSION 2108261 //uncomment this if you have a "my_config.h" file you'd like to use //#define WLED_USE_MY_CONFIG @@ -599,13 +599,6 @@ WLED_GLOBAL bool doInitBusses _INIT(false); // Usermod manager 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 #ifdef WLED_DEBUG #ifndef ESP8266 @@ -668,6 +661,7 @@ public: void beginStrip(); void handleConnection(); + bool initEthernet(); // result is informational void initAP(bool resetAP = false); void initConnection(); void initInterfaces(); diff --git a/wled00/wled_ethernet.h b/wled00/wled_ethernet.h index c8001aa1..52bdb2a1 100644 --- a/wled00/wled_ethernet.h +++ b/wled00/wled_ethernet.h @@ -2,7 +2,32 @@ #define WLED_ETHERNET_H #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 { uint8_t eth_address; int eth_power;