Pin manager enhancements (sharing I2C pins).

Effect helpers in UI.
This commit is contained in:
Blaz Kristan 2021-11-19 21:49:23 +01:00
parent 65ac8d4b2b
commit 108fc4a0d8
10 changed files with 262 additions and 108 deletions

View File

@ -3,6 +3,14 @@
#include "src/dependencies/time/DS1307RTC.h" #include "src/dependencies/time/DS1307RTC.h"
#include "wled.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)) //Connect DS1307 to standard I2C pins (ESP32: GPIO 21 (SDA)/GPIO 22 (SCL))
class RTCUsermod : public Usermod { class RTCUsermod : public Usermod {
@ -12,6 +20,8 @@ class RTCUsermod : public Usermod {
public: public:
void setup() { 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(); time_t rtcTime = RTC.get();
if (rtcTime) { if (rtcTime) {
toki.setTime(rtcTime,TOKI_NO_MS_ACCURACY,TOKI_TS_RTC); 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() uint16_t getId()
{ {
return USERMOD_ID_RTC; return USERMOD_ID_RTC;

View File

@ -21,6 +21,14 @@
#include <Wire.h> #include <Wire.h>
#include <VL53L0X.h> #include <VL53L0X.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
#ifndef VL53L0X_MAX_RANGE_MM #ifndef VL53L0X_MAX_RANGE_MM
#define VL53L0X_MAX_RANGE_MM 230 // max height in millimiters to react for motions #define VL53L0X_MAX_RANGE_MM 230 // max height in millimiters to react for motions
#endif #endif
@ -42,6 +50,7 @@ class UsermodVL53L0XGestures : public Usermod {
//Private class members. You can declare variables and functions only accessible to your usermod here //Private class members. You can declare variables and functions only accessible to your usermod here
unsigned long lastTime = 0; unsigned long lastTime = 0;
VL53L0X sensor; VL53L0X sensor;
bool enabled = true;
bool wasMotionBefore = false; bool wasMotionBefore = false;
bool isLongMotion = false; bool isLongMotion = false;
@ -50,6 +59,8 @@ class UsermodVL53L0XGestures : public Usermod {
public: public:
void setup() { 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(); Wire.begin();
sensor.setTimeout(150); sensor.setTimeout(150);
@ -63,6 +74,7 @@ class UsermodVL53L0XGestures : public Usermod {
void loop() { void loop() {
if (!enabled) return;
if (millis() - lastTime > VL53L0X_DELAY_MS) if (millis() - lastTime > VL53L0X_DELAY_MS)
{ {
lastTime = millis(); 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!). * 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. * This could be used in the future for the system to determine whether your usermod is installed.

View File

@ -42,6 +42,14 @@
#include "Wire.h" #include "Wire.h"
#endif #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 === // === INTERRUPT DETECTION ROUTINE ===
// ================================================================ // ================================================================
@ -55,6 +63,7 @@ void IRAM_ATTR dmpDataReady() {
class MPU6050Driver : public Usermod { class MPU6050Driver : public Usermod {
private: private:
MPU6050 mpu; MPU6050 mpu;
bool enabled = true;
// MPU control/status vars // MPU control/status vars
bool dmpReady = false; // set true if DMP init was successful bool dmpReady = false; // set true if DMP init was successful
@ -84,6 +93,8 @@ class MPU6050Driver : public Usermod {
* setup() is called once at boot. WiFi is not yet connected at this point. * setup() is called once at boot. WiFi is not yet connected at this point.
*/ */
void setup() { 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) // join I2C bus (I2Cdev library doesn't do this automatically)
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
Wire.begin(); Wire.begin();
@ -93,16 +104,16 @@ class MPU6050Driver : public Usermod {
#endif #endif
// initialize device // initialize device
Serial.println(F("Initializing I2C devices...")); DEBUG_PRINTLN(F("Initializing I2C devices..."));
mpu.initialize(); mpu.initialize();
pinMode(INTERRUPT_PIN, INPUT); pinMode(INTERRUPT_PIN, INPUT);
// verify connection // verify connection
Serial.println(F("Testing device connections...")); DEBUG_PRINTLN(F("Testing device connections..."));
Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed")); DEBUG_PRINTLN(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
// load and configure the DMP // load and configure the DMP
Serial.println(F("Initializing DMP...")); DEBUG_PRINTLN(F("Initializing DMP..."));
devStatus = mpu.dmpInitialize(); devStatus = mpu.dmpInitialize();
// supply your own gyro offsets here, scaled for min sensitivity // 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) // make sure it worked (returns 0 if so)
if (devStatus == 0) { if (devStatus == 0) {
// turn on the DMP, now that it's ready // turn on the DMP, now that it's ready
Serial.println(F("Enabling DMP...")); DEBUG_PRINTLN(F("Enabling DMP..."));
mpu.setDMPEnabled(true); mpu.setDMPEnabled(true);
// enable Arduino interrupt detection // 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); attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);
mpuIntStatus = mpu.getIntStatus(); mpuIntStatus = mpu.getIntStatus();
// set our DMP Ready flag so the main loop() function knows it's okay to use it // 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; dmpReady = true;
// get expected DMP packet size for later comparison // get expected DMP packet size for later comparison
@ -133,9 +144,9 @@ class MPU6050Driver : public Usermod {
// 1 = initial memory load failed // 1 = initial memory load failed
// 2 = DMP configuration updates failed // 2 = DMP configuration updates failed
// (if it's going to break, usually the code will be 1) // (if it's going to break, usually the code will be 1)
Serial.print(F("DMP Initialization failed (code ")); DEBUG_PRINT(F("DMP Initialization failed (code "));
Serial.print(devStatus); DEBUG_PRINT(devStatus);
Serial.println(F(")")); DEBUG_PRINTLN(F(")"));
} }
} }
@ -144,7 +155,7 @@ class MPU6050Driver : public Usermod {
* Use it to initialize network interfaces * Use it to initialize network interfaces
*/ */
void connected() { void connected() {
//Serial.println("Connected to WiFi!"); //DEBUG_PRINTLN("Connected to WiFi!");
} }
@ -153,7 +164,7 @@ class MPU6050Driver : public Usermod {
*/ */
void loop() { void loop() {
// if programming failed, don't try to do anything // 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 // wait for MPU interrupt or extra packet(s) available
if (!mpuInterrupt && fifoCount < packetSize) return; if (!mpuInterrupt && fifoCount < packetSize) return;
@ -169,7 +180,7 @@ class MPU6050Driver : public Usermod {
if ((mpuIntStatus & 0x10) || fifoCount == 1024) { if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
// reset so we can continue cleanly // reset so we can continue cleanly
mpu.resetFIFO(); mpu.resetFIFO();
Serial.println(F("FIFO overflow!")); DEBUG_PRINTLN(F("FIFO overflow!"));
// otherwise, check for DMP data ready interrupt (this should happen frequently) // otherwise, check for DMP data ready interrupt (this should happen frequently)
} else if (mpuIntStatus & 0x02) { } else if (mpuIntStatus & 0x02) {
@ -259,10 +270,23 @@ class MPU6050Driver : public Usermod {
*/ */
void readFromJsonState(JsonObject& root) 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!). * getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
*/ */

View File

@ -25,6 +25,10 @@
//The SCL and SDA pins are defined here. //The SCL and SDA pins are defined here.
#ifdef ARDUINO_ARCH_ESP32 #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 #ifndef FLD_PIN_SCL
#define FLD_PIN_SCL 22 #define FLD_PIN_SCL 22
#endif #endif
@ -47,6 +51,10 @@
#define FLD_PIN_RESET 26 #define FLD_PIN_RESET 26
#endif #endif
#else #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 #ifndef FLD_PIN_SCL
#define FLD_PIN_SCL 5 #define FLD_PIN_SCL 5
#endif #endif
@ -172,80 +180,64 @@ class FourLineDisplayUsermod : public Usermod {
// network here // network here
void setup() { void setup() {
if (type == NONE || !enabled) return; if (type == NONE || !enabled) return;
bool isHW;
PinOwner po = PinOwner::UM_FourLineDisplay;
if (type == SSD1306_SPI || type == SSD1306_SPI64) { if (type == SSD1306_SPI || type == SSD1306_SPI64) {
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 }}; 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; } if (!pinManager.allocateMultiplePins(pins, 5, po)) { type=NONE; return; }
} else { } else {
isHW = (ioPin[0]==HW_PIN_SCL && ioPin[1]==HW_PIN_SDA);
PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true } }; PinManagerPinType pins[2] = { { ioPin[0], true }, { ioPin[1], true } };
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::UM_FourLineDisplay)) { type=NONE; return; } 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.")); DEBUG_PRINTLN(F("Allocating display."));
switch (type) { switch (type) {
case SSD1306: case SSD1306:
#ifdef ESP8266 if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
if (!(ioPin[0]==5 && ioPin[1]==4)) else u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
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
lineHeight = 1; lineHeight = 1;
break; break;
case SH1106: case SH1106:
#ifdef ESP8266 if (!isHW) u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
if (!(ioPin[0]==5 && ioPin[1]==4)) else u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
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
lineHeight = 2; lineHeight = 2;
break; break;
case SSD1306_64: case SSD1306_64:
#ifdef ESP8266 if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
if (!(ioPin[0]==5 && ioPin[1]==4)) else u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
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
lineHeight = 2; lineHeight = 2;
break; break;
case SSD1305: case SSD1305:
#ifdef ESP8266 if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_NONAME_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
if (!(ioPin[0]==5 && ioPin[1]==4)) else u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
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
lineHeight = 1; lineHeight = 1;
break; break;
case SSD1305_64: case SSD1305_64:
#ifdef ESP8266 if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_SW_I2C(ioPin[0], ioPin[1]); // SCL, SDA, reset
if (!(ioPin[0]==5 && ioPin[1]==4)) else u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, ioPin[0], ioPin[1]); // Pins are Reset, SCL, SDA
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
lineHeight = 2; lineHeight = 2;
break; break;
case SSD1306_SPI: case SSD1306_SPI:
if (!(ioPin[0]==FLD_PIN_CLOCKSPI && ioPin[1]==FLD_PIN_DATASPI)) // if not overridden these sould be HW accellerated if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]);
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
else
u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset
lineHeight = 1; lineHeight = 1;
break; break;
case SSD1306_SPI64: case SSD1306_SPI64:
if (!(ioPin[0]==FLD_PIN_CLOCKSPI && ioPin[1]==FLD_PIN_DATASPI)) // if not overridden these sould be HW accellerated if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]);
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
else
u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset
lineHeight = 2; lineHeight = 2;
break; break;
default: default:
u8x8 = nullptr; u8x8 = nullptr;
} }
if (nullptr == u8x8) { if (nullptr == u8x8) {
DEBUG_PRINTLN(F("Display init failed.")); 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; type = NONE;
return; return;
} }
@ -714,10 +706,10 @@ class FourLineDisplayUsermod : public Usermod {
for (byte i=0; i<5; i++) if (ioPin[i] != newPin[i]) { pinsChanged = true; break; } for (byte i=0; i<5; i++) if (ioPin[i] != newPin[i]) { pinsChanged = true; break; }
if (pinsChanged || type!=newType) { if (pinsChanged || type!=newType) {
if (type != NONE) delete u8x8; if (type != NONE) delete u8x8;
for (byte i=0; i<5; i++) { PinOwner po = PinOwner::UM_FourLineDisplay;
if (ioPin[i]>=0) pinManager.deallocatePin(ioPin[i], 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
ioPin[i] = newPin[i]; 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 if (ioPin[0]<0 || ioPin[1]<0) { // data & clock must be > -1
type = NONE; type = NONE;
return true; return true;

View File

@ -932,7 +932,7 @@ class WS2812FX {
const char JSON_mode_names[] PROGMEM = R"=====([ const char JSON_mode_names[] PROGMEM = R"=====([
"Solid", "Solid",
"Blink@;!;!", "Blink@;!;!",
"Breathe@Speed;,!;!", "Breathe@!,;,!;!",
"Wipe", "Wipe",
"Wipe Random", "Wipe Random",
"Random Colors", "Random Colors",
@ -940,13 +940,12 @@ const char JSON_mode_names[] PROGMEM = R"=====([
"Dynamic", "Dynamic",
"Colorloop", "Colorloop",
"Rainbow", "Rainbow",
"Scan@!,# of dots;,!,?;!", "Scan@!,# of dots;,!,;!",
"Scan Dual@!,# of dots;,!,?;!", "Scan Dual@!,# of dots;,!,;!",
"Fade", "Fade","Theater",
"Theater",
"Theater Rainbow", "Theater Rainbow",
"Running", "Running@!,Wave width;!,!,;!",
"Saw", "Saw@!,Width;Fx,Bg,;!",
"Twinkle", "Twinkle",
"Dissolve", "Dissolve",
"Dissolve Rnd", "Dissolve Rnd",
@ -967,24 +966,24 @@ const char JSON_mode_names[] PROGMEM = R"=====([
"Colorful", "Colorful",
"Traffic Light", "Traffic Light",
"Sweep Random", "Sweep Random",
"Running 2", "Running 2@!,Width;;!",
"Aurora", "Aurora",
"Stream", "Stream",
"Scanner", "Scanner",
"Lighthouse", "Lighthouse",
"Fireworks", "Fireworks",
"Rain", "Rain",
"Tetrix@Speed,Width;Fx,BG,;!", "Tetrix@!,Width;Fx,Bg,;!",
"Fire Flicker", "Fire Flicker",
"Gradient", "Gradient",
"Loading", "Loading",
"Police", "Police@!,Width;;",
"Police All", "Police All@!,Width;;",
"Two Dots", "Two Dots@!,Dot size;,,Bg;!",
"Two Areas", "Two Areas@!,Size;,,Bg;!",
"Running Dual", "Running Dual",
"Halloween", "Halloween",
"Tri Chase", "Tri Chase@!,Size;;",
"Tri Wipe", "Tri Wipe",
"Tri Fade", "Tri Fade",
"Lightning", "Lightning",
@ -994,9 +993,9 @@ const char JSON_mode_names[] PROGMEM = R"=====([
"Stream 2", "Stream 2",
"Oscillate", "Oscillate",
"Pride 2015", "Pride 2015",
"Juggle", "Juggle@!,Trail;Fx,Bg,;!",
"Palette", "Palette@!,;;!",
"Fire 2012", "Fire 2012@Spark rate,Decay;!;!",
"Colorwaves", "Colorwaves",
"Bpm", "Bpm",
"Fill Noise", "Fill Noise",
@ -1013,15 +1012,15 @@ const char JSON_mode_names[] PROGMEM = R"=====([
"Twinklefox", "Twinklefox",
"Twinklecat", "Twinklecat",
"Halloween Eyes", "Halloween Eyes",
"Solid Pattern", "Solid Pattern@Fg size,Bg size;Fg,Bg,;",
"Solid Pattern Tri", "Solid Pattern Tri@,Size;;",
"Spots", "Spots",
"Spots Fade", "Spots Fade",
"Glitter", "Glitter",
"Candle", "Candle@Flicker rate,Flicker intensity;Fx,Bg,;",
"Fireworks Starburst", "Fireworks Starburst",
"Fireworks 1D@Gravity,Firing side;;!", "Fireworks 1D@Gravity,Firing side;;!",
"Bouncing Balls", "Bouncing Balls@Gravity,# of balls;Fx,Bg,;!",
"Sinelon", "Sinelon",
"Sinelon Dual", "Sinelon Dual",
"Sinelon Rainbow", "Sinelon Rainbow",
@ -1032,16 +1031,15 @@ const char JSON_mode_names[] PROGMEM = R"=====([
"Ripple Rainbow", "Ripple Rainbow",
"Heartbeat", "Heartbeat",
"Pacifica", "Pacifica",
"Candle Multi", "Candle Multi@Flicker rate,Flicker intensity;Fx,Bg,;",
"Solid Glitter", "Solid Glitter",
"Sunrise", "Sunrise@Time [min],;!;",
"Phased", "Phased","Twinkleup@Speed,Intensity;,!;!",
"Twinkleup@Speed,Intensity,Min;,!;!",
"Noise Pal", "Noise Pal",
"Sine", "Sine",
"Phased Noise", "Phased Noise",
"Flow", "Flow",
"Chunchun", "Chunchun@!,Gap size;Fg,Bg,;!",
"Dancing Shadows", "Dancing Shadows",
"Washing Machine", "Washing Machine",
"Candy Cane", "Candy Cane",

View File

@ -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 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 serializeState(JsonObject root, bool forPreset = false, bool includeBri = true, bool segmentBounds = true);
void serializeInfo(JsonObject root); 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); uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLen);
void serveJson(AsyncWebServerRequest* request); void serveJson(AsyncWebServerRequest* request);
bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient = 0); bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient = 0);

View File

@ -813,15 +813,50 @@ void serializeNodes(JsonObject root)
} }
} }
// deserializes mode names string into JsonArray void serializeModeData(JsonObject root)
// also removes WLED-SR extensions (@...) from deserialised names {
void deserializeModeNames(JsonArray arr, const char *qstring) { JsonArray fxdata = root.createNestedArray("fxdata");
String lineBuffer; String lineBuffer;
bool insideQuotes = false; bool insideQuotes = false;
char singleJsonSymbol; char singleJsonSymbol;
size_t len = strlen_P(JSON_mode_names);
// Find the mode name in JSON // 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); singleJsonSymbol = pgm_read_byte_near(qstring + i);
if (singleJsonSymbol == '\0') break; if (singleJsonSymbol == '\0') break;
switch (singleJsonSymbol) { switch (singleJsonSymbol) {
@ -854,9 +889,10 @@ uint8_t extractModeName(uint8_t mode, const char *src, char *dest, uint8_t maxLe
bool insideQuotes = false; bool insideQuotes = false;
uint8_t printedChars = 0; uint8_t printedChars = 0;
char singleJsonSymbol; char singleJsonSymbol;
size_t len = strlen_P(src);
// Find the mode name in JSON // 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); singleJsonSymbol = pgm_read_byte_near(src + i);
if (singleJsonSymbol == '\0') break; if (singleJsonSymbol == '\0') break;
switch (singleJsonSymbol) { switch (singleJsonSymbol) {
@ -889,6 +925,7 @@ void serveJson(AsyncWebServerRequest* request)
else if (url.indexOf("si") > 0) subJson = 3; else if (url.indexOf("si") > 0) subJson = 3;
else if (url.indexOf("nodes") > 0) subJson = 4; else if (url.indexOf("nodes") > 0) subJson = 4;
else if (url.indexOf("palx") > 0) subJson = 5; else if (url.indexOf("palx") > 0) subJson = 5;
else if (url.indexOf("fxda") > 0) subJson = 6;
else if (url.indexOf("live") > 0) { else if (url.indexOf("live") > 0) {
serveLiveLeds(request); serveLiveLeds(request);
return; return;
@ -936,6 +973,8 @@ void serveJson(AsyncWebServerRequest* request)
serializeNodes(lDoc); break; serializeNodes(lDoc); break;
case 5: //palettes case 5: //palettes
serializePalettes(lDoc, request); break; serializePalettes(lDoc, request); break;
case 6: // FX helper data
serializeModeData(lDoc); break;
default: //all default: //all
JsonObject state = lDoc.createNestedObject("state"); JsonObject state = lDoc.createNestedObject("state");
serializeState(state); serializeState(state);
@ -945,7 +984,7 @@ void serveJson(AsyncWebServerRequest* request)
{ {
//lDoc[F("effects")] = serialized((const __FlashStringHelper*)JSON_mode_names); //lDoc[F("effects")] = serialized((const __FlashStringHelper*)JSON_mode_names);
JsonArray effects = lDoc.createNestedArray(F("effects")); 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); lDoc[F("palettes")] = serialized((const __FlashStringHelper*)JSON_palette_names);
} }
} }

View File

@ -39,6 +39,48 @@ bool PinManagerClass::deallocatePin(byte gpio, PinOwner tag)
return true; 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 PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, byte arrayElementCount, PinOwner tag)
{ {
bool shouldFail = false; bool shouldFail = false;
@ -58,7 +100,10 @@ bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, by
#endif #endif
shouldFail = true; 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 #ifdef WLED_DEBUG
DEBUG_PRINT(F("PIN ALLOC: FAIL: IO ")); DEBUG_PRINT(F("PIN ALLOC: FAIL: IO "));
DEBUG_PRINT(gpio); DEBUG_PRINT(gpio);
@ -73,6 +118,8 @@ bool PinManagerClass::allocateMultiplePins(const managed_pin_type * mptArray, by
return false; return false;
} }
if (tag==PinOwner::HW_I2C) i2cAllocCount++;
// all pins are available .. track each one // all pins are available .. track each one
for (int i = 0; i < arrayElementCount; i++) { for (int i = 0; i < arrayElementCount; i++) {
byte gpio = mptArray[i].pin; 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) 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)) { if (isPinAllocated(gpio)) {
#ifdef WLED_DEBUG #ifdef WLED_DEBUG
DEBUG_PRINT(F("PIN ALLOC: Pin ")); DEBUG_PRINT(F("PIN ALLOC: Pin "));

View File

@ -33,26 +33,27 @@ enum struct PinOwner : uint8_t {
SPI_RAM = 0x88, // 'SpiR' == SPI RAM SPI_RAM = 0x88, // 'SpiR' == SPI RAM
DebugOut = 0x89, // 'Dbg' == debug output always IO1 DebugOut = 0x89, // 'Dbg' == debug output always IO1
DMX = 0x8A, // 'DMX' == hard-coded to IO2 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 // Use UserMod IDs from const.h here
UM_Unspecified = USERMOD_ID_UNSPECIFIED, // 0x01 UM_Unspecified = USERMOD_ID_UNSPECIFIED, // 0x01
UM_Example = USERMOD_ID_EXAMPLE, // 0x02 // Usermod "usermod_v2_example.h" UM_Example = USERMOD_ID_EXAMPLE, // 0x02 // Usermod "usermod_v2_example.h"
UM_Temperature = USERMOD_ID_TEMPERATURE, // 0x03 // Usermod "usermod_temperature.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 // #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" 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 // #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 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" 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_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_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_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_MultiRelay = USERMOD_ID_MULTI_RELAY, // 0x0D // Usermod "usermod_multi_relay.h"
UM_AnimatedStaircase = USERMOD_ID_ANIMATED_STAIRCASE, // 0x0E // Usermod "Animated_Staircase.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_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 // #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_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_QuinLEDAnPenta = USERMOD_ID_QUINLED_AN_PENTA // 0x17 // Usermod "quinled-an-penta.h"
}; };
static_assert(0u == static_cast<uint8_t>(PinOwner::None), "PinOwner::None must be zero, so default array initialization works as expected"); static_assert(0u == static_cast<uint8_t>(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 uint8_t ledcAlloc[2] = {0x00, 0x00}; //16 LEDC channels
PinOwner ownerTag[40] = { PinOwner::None }; PinOwner ownerTag[40] = { PinOwner::None };
#endif #endif
uint8_t i2cAllocCount = 0; // allow multiple allocation of I2C bus pins but keep track of allocations
public: public:
// De-allocates a single pin // De-allocates a single pin
bool deallocatePin(byte gpio, PinOwner tag); 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. // Allocates a single pin, with an owner tag.
// De-allocation requires the same owner tag (or override) // De-allocation requires the same owner tag (or override)
bool allocatePin(byte gpio, bool output, PinOwner tag); bool allocatePin(byte gpio, bool output, PinOwner tag);
@ -84,7 +88,7 @@ class PinManagerClass {
#endif #endif
inline bool allocatePin(byte gpio, bool output = true) { return allocatePin(gpio, output, PinOwner::None); } inline bool allocatePin(byte gpio, bool output = true) { return allocatePin(gpio, output, PinOwner::None); }
#if !defined(ESP8266) // ESP8266 compiler doesn't understand deprecated attribute #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 #endif
inline void deallocatePin(byte gpio) { deallocatePin(gpio, PinOwner::None); } inline void deallocatePin(byte gpio) { deallocatePin(gpio, PinOwner::None); }

View File

@ -8,7 +8,7 @@
*/ */
// version code in format yymmddb (b = daily build) // 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 //uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG //#define WLED_USE_MY_CONFIG