From 108fc4a0d8cc2bf040db0d377482c672b0df6d31 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Fri, 19 Nov 2021 21:49:23 +0100 Subject: [PATCH] Pin manager enhancements (sharing I2C pins). Effect helpers in UI. --- usermods/RTC/usermod_rtc.h | 23 +++++ .../usermod_vl53l0x_gestures.h | 25 ++++++ usermods/mpu6050_imu/usermod_mpu6050_imu.h | 58 +++++++++---- .../usermod_v2_four_line_display.h | 86 +++++++++---------- wled00/FX.h | 50 ++++++----- wled00/fcn_declare.h | 3 +- wled00/json.cpp | 51 +++++++++-- wled00/pin_manager.cpp | 52 ++++++++++- wled00/pin_manager.h | 20 +++-- wled00/wled.h | 2 +- 10 files changed, 262 insertions(+), 108 deletions(-) diff --git a/usermods/RTC/usermod_rtc.h b/usermods/RTC/usermod_rtc.h index 75d91b31..1d75f8df 100644 --- a/usermods/RTC/usermod_rtc.h +++ b/usermods/RTC/usermod_rtc.h @@ -3,6 +3,14 @@ #include "src/dependencies/time/DS1307RTC.h" #include "wled.h" +#ifdef ARDUINO_ARCH_ESP32 + #define HW_PIN_SCL 22 + #define HW_PIN_SDA 21 +#else + #define HW_PIN_SCL 5 + #define HW_PIN_SDA 4 +#endif + //Connect DS1307 to standard I2C pins (ESP32: GPIO 21 (SDA)/GPIO 22 (SCL)) class RTCUsermod : public Usermod { @@ -12,6 +20,8 @@ class RTCUsermod : public Usermod { public: void setup() { + PinManagerPinType pins[2] = { { HW_PIN_SCL, true }, { HW_PIN_SDA, true } }; + if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { disabled = true; return; } time_t rtcTime = RTC.get(); if (rtcTime) { toki.setTime(rtcTime,TOKI_NO_MS_ACCURACY,TOKI_TS_RTC); @@ -28,6 +38,19 @@ class RTCUsermod : public Usermod { } } + /* + * addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object. + * It will be called by WLED when settings are actually saved (for example, LED settings are saved) + * I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings! + */ + void addToConfig(JsonObject& root) + { + JsonObject top = root.createNestedObject("RTC"); + JsonArray pins = top.createNestedArray("pin"); + pins.add(HW_PIN_SCL); + pins.add(HW_PIN_SDA); + } + uint16_t getId() { return USERMOD_ID_RTC; diff --git a/usermods/VL53L0X_gestures/usermod_vl53l0x_gestures.h b/usermods/VL53L0X_gestures/usermod_vl53l0x_gestures.h index 83f26e08..c03c8a1b 100644 --- a/usermods/VL53L0X_gestures/usermod_vl53l0x_gestures.h +++ b/usermods/VL53L0X_gestures/usermod_vl53l0x_gestures.h @@ -21,6 +21,14 @@ #include #include +#ifdef ARDUINO_ARCH_ESP32 + #define HW_PIN_SCL 22 + #define HW_PIN_SDA 21 +#else + #define HW_PIN_SCL 5 + #define HW_PIN_SDA 4 +#endif + #ifndef VL53L0X_MAX_RANGE_MM #define VL53L0X_MAX_RANGE_MM 230 // max height in millimiters to react for motions #endif @@ -42,6 +50,7 @@ class UsermodVL53L0XGestures : public Usermod { //Private class members. You can declare variables and functions only accessible to your usermod here unsigned long lastTime = 0; VL53L0X sensor; + bool enabled = true; bool wasMotionBefore = false; bool isLongMotion = false; @@ -50,6 +59,8 @@ class UsermodVL53L0XGestures : public Usermod { public: void setup() { + PinManagerPinType pins[2] = { { HW_PIN_SCL, true }, { HW_PIN_SDA, true } }; + if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { enabled = false; return; } Wire.begin(); sensor.setTimeout(150); @@ -63,6 +74,7 @@ class UsermodVL53L0XGestures : public Usermod { void loop() { + if (!enabled) return; if (millis() - lastTime > VL53L0X_DELAY_MS) { lastTime = millis(); @@ -110,6 +122,19 @@ class UsermodVL53L0XGestures : public Usermod { } } + /* + * addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object. + * It will be called by WLED when settings are actually saved (for example, LED settings are saved) + * I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings! + */ + void addToConfig(JsonObject& root) + { + JsonObject top = root.createNestedObject("VL53L0x"); + JsonArray pins = top.createNestedArray("pin"); + pins.add(HW_PIN_SCL); + pins.add(HW_PIN_SDA); + } + /* * getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!). * This could be used in the future for the system to determine whether your usermod is installed. diff --git a/usermods/mpu6050_imu/usermod_mpu6050_imu.h b/usermods/mpu6050_imu/usermod_mpu6050_imu.h index 965ab41b..2045a448 100644 --- a/usermods/mpu6050_imu/usermod_mpu6050_imu.h +++ b/usermods/mpu6050_imu/usermod_mpu6050_imu.h @@ -42,6 +42,14 @@ #include "Wire.h" #endif +#ifdef ARDUINO_ARCH_ESP32 + #define HW_PIN_SCL 22 + #define HW_PIN_SDA 21 +#else + #define HW_PIN_SCL 5 + #define HW_PIN_SDA 4 +#endif + // ================================================================ // === INTERRUPT DETECTION ROUTINE === // ================================================================ @@ -55,7 +63,8 @@ void IRAM_ATTR dmpDataReady() { class MPU6050Driver : public Usermod { private: MPU6050 mpu; - + bool enabled = true; + // MPU control/status vars bool dmpReady = false; // set true if DMP init was successful uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU @@ -84,6 +93,8 @@ class MPU6050Driver : public Usermod { * setup() is called once at boot. WiFi is not yet connected at this point. */ void setup() { + PinManagerPinType pins[2] = { { HW_PIN_SCL, true }, { HW_PIN_SDA, true } }; + if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { enabled = false; return; } // join I2C bus (I2Cdev library doesn't do this automatically) #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE Wire.begin(); @@ -93,16 +104,16 @@ class MPU6050Driver : public Usermod { #endif // initialize device - Serial.println(F("Initializing I2C devices...")); + DEBUG_PRINTLN(F("Initializing I2C devices...")); mpu.initialize(); pinMode(INTERRUPT_PIN, INPUT); // verify connection - Serial.println(F("Testing device connections...")); - Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed")); + DEBUG_PRINTLN(F("Testing device connections...")); + DEBUG_PRINTLN(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed")); // load and configure the DMP - Serial.println(F("Initializing DMP...")); + DEBUG_PRINTLN(F("Initializing DMP...")); devStatus = mpu.dmpInitialize(); // supply your own gyro offsets here, scaled for min sensitivity @@ -114,16 +125,16 @@ class MPU6050Driver : public Usermod { // make sure it worked (returns 0 if so) if (devStatus == 0) { // turn on the DMP, now that it's ready - Serial.println(F("Enabling DMP...")); + DEBUG_PRINTLN(F("Enabling DMP...")); mpu.setDMPEnabled(true); // enable Arduino interrupt detection - Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)...")); + DEBUG_PRINTLN(F("Enabling interrupt detection (Arduino external interrupt 0)...")); attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING); mpuIntStatus = mpu.getIntStatus(); // set our DMP Ready flag so the main loop() function knows it's okay to use it - Serial.println(F("DMP ready! Waiting for first interrupt...")); + DEBUG_PRINTLN(F("DMP ready! Waiting for first interrupt...")); dmpReady = true; // get expected DMP packet size for later comparison @@ -133,9 +144,9 @@ class MPU6050Driver : public Usermod { // 1 = initial memory load failed // 2 = DMP configuration updates failed // (if it's going to break, usually the code will be 1) - Serial.print(F("DMP Initialization failed (code ")); - Serial.print(devStatus); - Serial.println(F(")")); + DEBUG_PRINT(F("DMP Initialization failed (code ")); + DEBUG_PRINT(devStatus); + DEBUG_PRINTLN(F(")")); } } @@ -144,7 +155,7 @@ class MPU6050Driver : public Usermod { * Use it to initialize network interfaces */ void connected() { - //Serial.println("Connected to WiFi!"); + //DEBUG_PRINTLN("Connected to WiFi!"); } @@ -153,7 +164,7 @@ class MPU6050Driver : public Usermod { */ void loop() { // if programming failed, don't try to do anything - if (!dmpReady) return; + if (!enabled || !dmpReady) return; // wait for MPU interrupt or extra packet(s) available if (!mpuInterrupt && fifoCount < packetSize) return; @@ -169,7 +180,7 @@ class MPU6050Driver : public Usermod { if ((mpuIntStatus & 0x10) || fifoCount == 1024) { // reset so we can continue cleanly mpu.resetFIFO(); - Serial.println(F("FIFO overflow!")); + DEBUG_PRINTLN(F("FIFO overflow!")); // otherwise, check for DMP data ready interrupt (this should happen frequently) } else if (mpuIntStatus & 0x02) { @@ -259,10 +270,23 @@ class MPU6050Driver : public Usermod { */ void readFromJsonState(JsonObject& root) { - //if (root["bri"] == 255) Serial.println(F("Don't burn down your garage!")); + //if (root["bri"] == 255) DEBUG_PRINTLN(F("Don't burn down your garage!")); } - - + + + /* + * addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object. + * It will be called by WLED when settings are actually saved (for example, LED settings are saved) + * I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings! + */ + void addToConfig(JsonObject& root) + { + JsonObject top = root.createNestedObject("MPU6050_IMU"); + JsonArray pins = top.createNestedArray("pin"); + pins.add(HW_PIN_SCL); + pins.add(HW_PIN_SDA); + } + /* * getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!). */ 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 bb9468cd..e0d283a6 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 @@ -25,6 +25,10 @@ //The SCL and SDA pins are defined here. #ifdef ARDUINO_ARCH_ESP32 + #define HW_PIN_SCL 22 + #define HW_PIN_SDA 21 + #define HW_PIN_CLOCKSPI 18 + #define HW_PIN_DATASPI 23 #ifndef FLD_PIN_SCL #define FLD_PIN_SCL 22 #endif @@ -47,6 +51,10 @@ #define FLD_PIN_RESET 26 #endif #else + #define HW_PIN_SCL 5 + #define HW_PIN_SDA 4 + #define HW_PIN_CLOCKSPI 14 + #define HW_PIN_DATASPI 13 #ifndef FLD_PIN_SCL #define FLD_PIN_SCL 5 #endif @@ -172,80 +180,64 @@ class FourLineDisplayUsermod : public Usermod { // network here void setup() { if (type == NONE || !enabled) return; + + bool isHW; + PinOwner po = PinOwner::UM_FourLineDisplay; if (type == SSD1306_SPI || type == SSD1306_SPI64) { - 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; } + isHW = (ioPin[0]==HW_PIN_CLOCKSPI && ioPin[1]==HW_PIN_DATASPI); + PinManagerPinType pins[5] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true }, { ioPin[3], true }, { ioPin[4], true }}; + if (!pinManager.allocateMultiplePins(pins, 5, po)) { type=NONE; return; } } else { - PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true} }; - if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::UM_FourLineDisplay)) { type=NONE; return; } + isHW = (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA); + PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true } }; + if (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins + if (!pinManager.allocateMultiplePins(pins, 2, po)) { type=NONE; return; } } + DEBUG_PRINTLN(F("Allocating display.")); switch (type) { case SSD1306: - #ifdef ESP8266 - if (!(ioPin[0]==5 && ioPin[1]==4)) - u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset - else - #endif - u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA + if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset + else u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA lineHeight = 1; break; case SH1106: - #ifdef ESP8266 - if (!(ioPin[0]==5 && ioPin[1]==4)) - u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset - else - #endif - u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA + if (!isHW) u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset + else u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA lineHeight = 2; break; case SSD1306_64: - #ifdef ESP8266 - if (!(ioPin[0]==5 && ioPin[1]==4)) - u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset - else - #endif - u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA + if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset + else u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA lineHeight = 2; break; case SSD1305: - #ifdef ESP8266 - if (!(ioPin[0]==5 && ioPin[1]==4)) - u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset - else - #endif - u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA + if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset + else u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA lineHeight = 1; break; case SSD1305_64: - #ifdef ESP8266 - if (!(ioPin[0]==5 && ioPin[1]==4)) - u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset - else - #endif - u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA + if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset + else u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA lineHeight = 2; break; case SSD1306_SPI: - if (!(ioPin[0]==FLD_PIN_CLOCKSPI && ioPin[1]==FLD_PIN_DATASPI)) // if not overridden these sould be HW accellerated - u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]); - else - u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset + if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]); + else u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset lineHeight = 1; break; case SSD1306_SPI64: - if (!(ioPin[0]==FLD_PIN_CLOCKSPI && ioPin[1]==FLD_PIN_DATASPI)) // if not overridden these sould be HW accellerated - u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]); - else - u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset + if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]); + else u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset lineHeight = 2; 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); + pinManager.deallocateMultiplePins((const uint8_t*)ioPin, (type == SSD1306_SPI || type == SSD1306_SPI64) ? 5 : 2, po); type = NONE; return; } @@ -714,10 +706,10 @@ class FourLineDisplayUsermod : public Usermod { for (byte i=0; i<5; i++) if (ioPin[i] != newPin[i]) { pinsChanged = true; break; } if (pinsChanged || type!=newType) { if (type != NONE) delete u8x8; - for (byte i=0; i<5; i++) { - if (ioPin[i]>=0) pinManager.deallocatePin(ioPin[i], PinOwner::UM_FourLineDisplay); - ioPin[i] = newPin[i]; - } + PinOwner po = PinOwner::UM_FourLineDisplay; + if (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA) po = PinOwner::HW_I2C; // allow multiple allocations of HW I2C bus pins + pinManager.deallocateMultiplePins((const uint8_t *)ioPin, (type == SSD1306_SPI || type == SSD1306_SPI64) ? 5 : 2, po); + for (byte i=0; i<5; i++) ioPin[i] = newPin[i]; if (ioPin[0]<0 || ioPin[1]<0) { // data & clock must be > -1 type = NONE; return true; diff --git a/wled00/FX.h b/wled00/FX.h index ab82e0d8..a022e215 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -932,7 +932,7 @@ class WS2812FX { const char JSON_mode_names[] PROGMEM = R"=====([ "Solid", "Blink@;!;!", -"Breathe@Speed;,!;!", +"Breathe@!,;,!;!", "Wipe", "Wipe Random", "Random Colors", @@ -940,13 +940,12 @@ const char JSON_mode_names[] PROGMEM = R"=====([ "Dynamic", "Colorloop", "Rainbow", -"Scan@!,# of dots;,!,?;!", -"Scan Dual@!,# of dots;,!,?;!", -"Fade", -"Theater", +"Scan@!,# of dots;,!,;!", +"Scan Dual@!,# of dots;,!,;!", +"Fade","Theater", "Theater Rainbow", -"Running", -"Saw", +"Running@!,Wave width;!,!,;!", +"Saw@!,Width;Fx,Bg,;!", "Twinkle", "Dissolve", "Dissolve Rnd", @@ -967,24 +966,24 @@ const char JSON_mode_names[] PROGMEM = R"=====([ "Colorful", "Traffic Light", "Sweep Random", -"Running 2", +"Running 2@!,Width;;!", "Aurora", "Stream", "Scanner", "Lighthouse", "Fireworks", "Rain", -"Tetrix@Speed,Width;Fx,BG,;!", +"Tetrix@!,Width;Fx,Bg,;!", "Fire Flicker", "Gradient", "Loading", -"Police", -"Police All", -"Two Dots", -"Two Areas", +"Police@!,Width;;", +"Police All@!,Width;;", +"Two Dots@!,Dot size;,,Bg;!", +"Two Areas@!,Size;,,Bg;!", "Running Dual", "Halloween", -"Tri Chase", +"Tri Chase@!,Size;;", "Tri Wipe", "Tri Fade", "Lightning", @@ -994,9 +993,9 @@ const char JSON_mode_names[] PROGMEM = R"=====([ "Stream 2", "Oscillate", "Pride 2015", -"Juggle", -"Palette", -"Fire 2012", +"Juggle@!,Trail;Fx,Bg,;!", +"Palette@!,;;!", +"Fire 2012@Spark rate,Decay;!;!", "Colorwaves", "Bpm", "Fill Noise", @@ -1013,15 +1012,15 @@ const char JSON_mode_names[] PROGMEM = R"=====([ "Twinklefox", "Twinklecat", "Halloween Eyes", -"Solid Pattern", -"Solid Pattern Tri", +"Solid Pattern@Fg size,Bg size;Fg,Bg,;", +"Solid Pattern Tri@,Size;;", "Spots", "Spots Fade", "Glitter", -"Candle", +"Candle@Flicker rate,Flicker intensity;Fx,Bg,;", "Fireworks Starburst", "Fireworks 1D@Gravity,Firing side;;!", -"Bouncing Balls", +"Bouncing Balls@Gravity,# of balls;Fx,Bg,;!", "Sinelon", "Sinelon Dual", "Sinelon Rainbow", @@ -1032,16 +1031,15 @@ const char JSON_mode_names[] PROGMEM = R"=====([ "Ripple Rainbow", "Heartbeat", "Pacifica", -"Candle Multi", +"Candle Multi@Flicker rate,Flicker intensity;Fx,Bg,;", "Solid Glitter", -"Sunrise", -"Phased", -"Twinkleup@Speed,Intensity,Min;,!;!", +"Sunrise@Time [min],;!;", +"Phased","Twinkleup@Speed,Intensity;,!;!", "Noise Pal", "Sine", "Phased Noise", "Flow", -"Chunchun", +"Chunchun@!,Gap size;Fg,Bg,;!", "Dancing Shadows", "Washing Machine", "Candy Cane", diff --git a/wled00/fcn_declare.h b/wled00/fcn_declare.h index ceaa631f..0a8f5daf 100644 --- a/wled00/fcn_declare.h +++ b/wled00/fcn_declare.h @@ -134,7 +134,8 @@ bool deserializeState(JsonObject root, byte callMode = CALL_MODE_DIRECT_CHANGE, void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool forPreset = false, bool segmentBounds = true); void serializeState(JsonObject root, bool forPreset = false, bool includeBri = true, bool segmentBounds = true); void serializeInfo(JsonObject root); -void serializeSRNames(JsonArray arr, const char *qstring); +void serializeModeNames(JsonArray arr, const char *qstring); +void serializeModeData(JsonObject root); uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLen); void serveJson(AsyncWebServerRequest* request); bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient = 0); diff --git a/wled00/json.cpp b/wled00/json.cpp index a7c4f8b3..b8da480b 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -813,15 +813,50 @@ void serializeNodes(JsonObject root) } } -// deserializes mode names string into JsonArray -// also removes WLED-SR extensions (@...) from deserialised names -void deserializeModeNames(JsonArray arr, const char *qstring) { +void serializeModeData(JsonObject root) +{ + JsonArray fxdata = root.createNestedArray("fxdata"); String lineBuffer; bool insideQuotes = false; char singleJsonSymbol; + size_t len = strlen_P(JSON_mode_names); // Find the mode name in JSON - for (size_t i = 0; i < strlen_P(qstring); i++) { + for (size_t i = 0; i < len; i++) { + singleJsonSymbol = pgm_read_byte_near(JSON_mode_names + i); + if (singleJsonSymbol == '\0') break; + switch (singleJsonSymbol) { + case '"': + insideQuotes = !insideQuotes; + break; + case '[': + break; + case ']': + case ',': + if (lineBuffer.length() > 0) { + uint8_t endPos = lineBuffer.indexOf('@'); + if (endPos>0) fxdata.add(lineBuffer.substring(endPos)); + else fxdata.add(""); + lineBuffer.clear(); + } + break; + default: + if (!insideQuotes) break; + lineBuffer += singleJsonSymbol; + } + } +} + +// deserializes mode names string into JsonArray +// also removes WLED-SR extensions (@...) from deserialised names +void serializeModeNames(JsonArray arr, const char *qstring) { + String lineBuffer; + bool insideQuotes = false; + char singleJsonSymbol; + size_t len = strlen_P(qstring); + + // Find the mode name in JSON + for (size_t i = 0; i < len; i++) { singleJsonSymbol = pgm_read_byte_near(qstring + i); if (singleJsonSymbol == '\0') break; switch (singleJsonSymbol) { @@ -854,9 +889,10 @@ uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLe bool insideQuotes = false; uint8_t printedChars = 0; char singleJsonSymbol; + size_t len = strlen_P(src); // Find the mode name in JSON - for (size_t i = 0; i < strlen_P(src); i++) { + for (size_t i = 0; i < len; i++) { singleJsonSymbol = pgm_read_byte_near(src + i); if (singleJsonSymbol == '\0') break; switch (singleJsonSymbol) { @@ -889,6 +925,7 @@ void serveJson(AsyncWebServerRequest* request) else if (url.indexOf("si") > 0) subJson = 3; else if (url.indexOf("nodes") > 0) subJson = 4; else if (url.indexOf("palx") > 0) subJson = 5; + else if (url.indexOf("fxda") > 0) subJson = 6; else if (url.indexOf("live") > 0) { serveLiveLeds(request); return; @@ -936,6 +973,8 @@ void serveJson(AsyncWebServerRequest* request) serializeNodes(lDoc); break; case 5: //palettes serializePalettes(lDoc, request); break; + case 6: // FX helper data + serializeModeData(lDoc); break; default: //all JsonObject state = lDoc.createNestedObject("state"); serializeState(state); @@ -945,7 +984,7 @@ void serveJson(AsyncWebServerRequest* request) { //lDoc[F("effects")] = serialized((const __FlashStringHelper*)JSON_mode_names); JsonArray effects = lDoc.createNestedArray(F("effects")); - deserializeModeNames(effects, JSON_mode_names); // remove WLED-SR extensions from effect names + serializeModeNames(effects, JSON_mode_names); // remove WLED-SR extensions from effect names lDoc[F("palettes")] = serialized((const __FlashStringHelper*)JSON_palette_names); } } diff --git a/wled00/pin_manager.cpp b/wled00/pin_manager.cpp index 0dd2df56..2b908557 100644 --- a/wled00/pin_manager.cpp +++ b/wled00/pin_manager.cpp @@ -39,6 +39,48 @@ bool PinManagerClass::deallocatePin(byte gpio, PinOwner tag) return true; } +// support function for deallocating multiple pins +bool PinManagerClass::deallocateMultiplePins(const uint8_t *pinArray, byte arrayElementCount, PinOwner tag) +{ + bool shouldFail = false; + DEBUG_PRINTLN(F("MULTIPIN DEALLOC")); + // first verify the pins are OK and allocated by selected owner + for (int i = 0; i < arrayElementCount; i++) { + byte gpio = pinArray[i]; + 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 (isPinAllocated(gpio, tag)) { + // if the current pin is allocated by selected owner it is possible to release it + continue; + } + #ifdef WLED_DEBUG + 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); + #endif + shouldFail = true; + } + if (shouldFail) { + return false; // no pins deallocated + } + if (tag==PinOwner::HW_I2C) { + if (i2cAllocCount && --i2cAllocCount>0) { + // no deallocation done until last owner releases pins + return true; + } + } + for (int i = 0; i < arrayElementCount; i++) { + deallocatePin(pinArray[i], tag); + } + return true; +} + bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, byte arrayElementCount, PinOwner tag) { bool shouldFail = false; @@ -58,7 +100,10 @@ bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, by #endif shouldFail = true; } - if (isPinAllocated(gpio)) { + if (tag==PinOwner::HW_I2C && isPinAllocated(gpio, tag)) { + // allow multiple "allocations" of HW I2C bus pins + continue; + } else if (isPinAllocated(gpio)) { #ifdef WLED_DEBUG DEBUG_PRINT(F("PIN ALLOC: FAIL: IO ")); DEBUG_PRINT(gpio); @@ -73,6 +118,8 @@ bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, by return false; } + if (tag==PinOwner::HW_I2C) i2cAllocCount++; + // all pins are available .. track each one for (int i = 0; i < arrayElementCount; i++) { byte gpio = mptArray[i].pin; @@ -91,7 +138,8 @@ bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, by bool PinManagerClass::allocatePin(byte gpio, bool output, PinOwner tag) { - if (!isPinOk(gpio, output)) return false; + // HW I2C pins have to be allocated using allocateMultiplePins variant since there is always SCL/SDA pair + if (!isPinOk(gpio, output) || tag==PinOwner::HW_I2C) return false; if (isPinAllocated(gpio)) { #ifdef WLED_DEBUG DEBUG_PRINT(F("PIN ALLOC: Pin ")); diff --git a/wled00/pin_manager.h b/wled00/pin_manager.h index 06188b85..5f009425 100644 --- a/wled00/pin_manager.h +++ b/wled00/pin_manager.h @@ -21,7 +21,7 @@ typedef struct PinManagerPinType { * 40 bytes on ESP32 */ enum struct PinOwner : uint8_t { - None = 0, // default == legacy == unspecified owner + None = 0, // default == legacy == unspecified owner // High bit is set for all built-in pin owners Ethernet = 0x81, BusDigital = 0x82, @@ -33,26 +33,27 @@ enum struct PinOwner : uint8_t { SPI_RAM = 0x88, // 'SpiR' == SPI RAM DebugOut = 0x89, // 'Dbg' == debug output always IO1 DMX = 0x8A, // 'DMX' == hard-coded to IO2 + HW_I2C = 0x8B, // 'I2C' == hardware I2C pins (4&5 on ESP8266, 21&22 on ESP32) // Use UserMod IDs from const.h here UM_Unspecified = USERMOD_ID_UNSPECIFIED, // 0x01 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 + // #define USERMOD_ID_IMU // 0x06 // Usermod "usermod_mpu6050_imu.h" -- Uses "standard" HW_I2C pins + UM_FourLineDisplay = USERMOD_ID_FOUR_LINE_DISP, // 0x07 // Usermod "usermod_v2_four_line_display.h -- May use "standard" HW_I2C pins 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 + // #define USERMOD_ID_VL53L0X // 0x0C // Usermod "usermod_vl53l0x_gestures.h" -- Uses "standard" HW_I2C pins 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_RTC // 0x0F // Usermod "usermod_rtc.h" -- Uses "standard" HW_I2C pins // #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 - UM_RGBRotaryEncoder = USERMOD_RGB_ROTARY_ENCODER, // 0x16 // Usermod "rgb-rotary-encoder.h" - UM_QuinLEDAnPenta = USERMOD_ID_QUINLED_AN_PENTA, // 0x17 // Usermod "quinled-an-penta.h" + UM_RGBRotaryEncoder = USERMOD_RGB_ROTARY_ENCODER, // 0x16 // Usermod "rgb-rotary-encoder.h" + UM_QuinLEDAnPenta = USERMOD_ID_QUINLED_AN_PENTA // 0x17 // Usermod "quinled-an-penta.h" }; static_assert(0u == static_cast(PinOwner::None), "PinOwner::None must be zero, so default array initialization works as expected"); @@ -66,10 +67,13 @@ class PinManagerClass { uint8_t ledcAlloc[2] = {0x00, 0x00}; //16 LEDC channels PinOwner ownerTag[40] = { PinOwner::None }; #endif + uint8_t i2cAllocCount = 0; // allow multiple allocation of I2C bus pins but keep track of allocations public: // De-allocates a single pin bool deallocatePin(byte gpio, PinOwner tag); + // De-allocates multiple pins but only if all can be deallocated (PinOwner has to be specified) + bool deallocateMultiplePins(const uint8_t *pinArray, byte arrayElementCount, 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); @@ -84,7 +88,7 @@ class PinManagerClass { #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")]] + [[deprecated("Replaced by two-parameter deallocatePin(gpio, ownerTag), for improved debugging")]] #endif inline void deallocatePin(byte gpio) { deallocatePin(gpio, PinOwner::None); } diff --git a/wled00/wled.h b/wled00/wled.h index 0ece58a1..7b53bf61 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2111171 +#define VERSION 2111191 //uncomment this if you have a "my_config.h" file you'd like to use //#define WLED_USE_MY_CONFIG