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 "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;

View File

@ -21,6 +21,14 @@
#include <Wire.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
#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.

View File

@ -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!).
*/

View File

@ -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;

View File

@ -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",

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 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);

View File

@ -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);
}
}

View File

@ -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 "));

View File

@ -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<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
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); }

View File

@ -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