New SPI display SSD1309 for 4LD.

Fixed global I2C usage (no pin allocation in usermods).
Enabled option dor Multi relay.
This commit is contained in:
Blaz Kristan 2023-06-21 23:31:15 +02:00
parent c04c73bbd7
commit cf48ad06ed
10 changed files with 55 additions and 77 deletions

View File

@ -113,8 +113,7 @@ private:
public: public:
void setup() void setup()
{ {
PinManagerPinType pins[2] = { { i2c_sda, true }, { i2c_scl, true } }; // allocate pins if (i2c_scl<0 || i2c_sda<0) { enabled = false; return; }
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) return;
sensorFound = lightMeter.begin(); sensorFound = lightMeter.begin();
initDone = true; initDone = true;
} }
@ -174,7 +173,9 @@ public:
user = root.createNestedObject(F("u")); user = root.createNestedObject(F("u"));
JsonArray lux_json = user.createNestedArray(F("Luminance")); JsonArray lux_json = user.createNestedArray(F("Luminance"));
if (!sensorFound) { if (!enabled) {
lux_json.add(F("disabled"));
} else if (!sensorFound) {
// if no sensor // if no sensor
lux_json.add(F("BH1750 ")); lux_json.add(F("BH1750 "));
lux_json.add(F("Not Found")); lux_json.add(F("Not Found"));

View File

@ -184,8 +184,7 @@ private:
public: public:
void setup() void setup()
{ {
PinManagerPinType pins[2] = { { i2c_sda, true }, { i2c_scl, true } }; // allocate pins if (i2c_scl<0 || i2c_sda<0) { enabled = false; sensorType = 0; return; }
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { sensorType=0; return; }
if (!bme.begin()) if (!bme.begin())
{ {

View File

@ -12,8 +12,7 @@ class RTCUsermod : public Usermod {
public: public:
void setup() { void setup() {
PinManagerPinType pins[2] = { { i2c_scl, true }, { i2c_sda, true } }; if (i2c_scl<0 || i2c_sda<0) { disabled = true; return; }
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { disabled = true; return; }
RTC.begin(); RTC.begin();
time_t rtcTime = RTC.get(); time_t rtcTime = RTC.get();
if (rtcTime) { if (rtcTime) {
@ -25,8 +24,8 @@ class RTCUsermod : public Usermod {
} }
void loop() { void loop() {
if (strip.isUpdating()) return; if (disabled || strip.isUpdating()) return;
if (!disabled && toki.isTick()) { if (toki.isTick()) {
time_t t = toki.second(); time_t t = toki.second();
if (t != RTC.get()) RTC.set(t); //set RTC to NTP/UI-provided value if (t != RTC.get()) RTC.set(t); //set RTC to NTP/UI-provided value
} }

View File

@ -50,8 +50,7 @@ class UsermodVL53L0XGestures : public Usermod {
public: public:
void setup() { void setup() {
PinManagerPinType pins[2] = { { i2c_scl, true }, { i2c_sda, true } }; if (i2c_scl<0 || i2c_sda<0) { enabled = false; return; }
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { enabled = false; return; }
sensor.setTimeout(150); sensor.setTimeout(150);
if (!sensor.init()) if (!sensor.init())

View File

@ -85,8 +85,7 @@ 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] = { { i2c_scl, true }, { i2c_sda, true } }; if (i2c_scl<0 || i2c_sda<0) { enabled = false; return; }
if (!pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { enabled = false; return; }
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
Wire.setClock(400000U); // 400kHz I2C clock. Comment this line if having compilation difficulties Wire.setClock(400000U); // 400kHz I2C clock. Comment this line if having compilation difficulties
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE

View File

@ -14,6 +14,9 @@
#ifndef MULTI_RELAY_PINS #ifndef MULTI_RELAY_PINS
#define MULTI_RELAY_PINS -1 #define MULTI_RELAY_PINS -1
#define MULTI_RELAY_ENABLED false
#else
#define MULTI_RELAY_ENABLED true
#endif #endif
#define WLED_DEBOUNCE_THRESHOLD 50 //only consider button input of at least 50ms as valid (debouncing) #define WLED_DEBOUNCE_THRESHOLD 50 //only consider button input of at least 50ms as valid (debouncing)
@ -336,7 +339,7 @@ byte MultiRelay::IOexpanderRead(int address) {
MultiRelay::MultiRelay() MultiRelay::MultiRelay()
: _switchTimerStart(0) : _switchTimerStart(0)
, enabled(false) , enabled(MULTI_RELAY_ENABLED)
, initDone(false) , initDone(false)
, usePcf8574(USE_PCF8574) , usePcf8574(USE_PCF8574)
, addrPcf8574(PCF8574_ADDRESS) , addrPcf8574(PCF8574_ADDRESS)
@ -479,7 +482,7 @@ void MultiRelay::publishHomeAssistantAutodiscovery() {
void MultiRelay::setup() { void MultiRelay::setup() {
// pins retrieved from cfg.json (readFromConfig()) prior to running setup() // pins retrieved from cfg.json (readFromConfig()) prior to running setup()
// if we want PCF8574 expander I2C pins need to be valid // if we want PCF8574 expander I2C pins need to be valid
if (i2c_sda == i2c_scl && i2c_sda == -1) usePcf8574 = false; if (i2c_sda<0 || i2c_scl<0) usePcf8574 = false;
uint8_t state = 0; uint8_t state = 0;
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) { for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
@ -765,7 +768,7 @@ bool MultiRelay::readFromConfig(JsonObject &root) {
usePcf8574 = top[FPSTR(_pcf8574)] | usePcf8574; usePcf8574 = top[FPSTR(_pcf8574)] | usePcf8574;
addrPcf8574 = top[FPSTR(_pcfAddress)] | addrPcf8574; addrPcf8574 = top[FPSTR(_pcfAddress)] | addrPcf8574;
// if I2C is not globally initialised just ignore // if I2C is not globally initialised just ignore
if (i2c_sda == i2c_scl && i2c_sda == -1) usePcf8574 = false; if (i2c_sda<0 || i2c_scl<0) usePcf8574 = false;
periodicBroadcastSec = top[FPSTR(_broadcast)] | periodicBroadcastSec; periodicBroadcastSec = top[FPSTR(_broadcast)] | periodicBroadcastSec;
periodicBroadcastSec = min(900,max(0,(int)periodicBroadcastSec)); periodicBroadcastSec = min(900,max(0,(int)periodicBroadcastSec));
HAautodiscovery = top[FPSTR(_HAautodiscovery)] | HAautodiscovery; HAautodiscovery = top[FPSTR(_HAautodiscovery)] | HAautodiscovery;

View File

@ -16,7 +16,6 @@ class ShtUsermod : public Usermod
private: private:
bool enabled = false; // Is usermod enabled or not bool enabled = false; // Is usermod enabled or not
bool firstRunDone = false; // Remembers if the first config load run had been done bool firstRunDone = false; // Remembers if the first config load run had been done
bool pinAllocDone = true; // Remembers if we have allocated pins
bool initDone = false; // Remembers if the mod has been completely initialised bool initDone = false; // Remembers if the mod has been completely initialised
bool haMqttDiscovery = false; // Is MQTT discovery enabled or not bool haMqttDiscovery = false; // Is MQTT discovery enabled or not
bool haMqttDiscoveryDone = false; // Remembers if we already published the HA discovery topics bool haMqttDiscoveryDone = false; // Remembers if we already published the HA discovery topics
@ -94,7 +93,7 @@ void ShtUsermod::initShtTempHumiditySensor()
case USERMOD_SHT_TYPE_SHT85: shtTempHumidSensor = (SHT *) new SHT85(); break; case USERMOD_SHT_TYPE_SHT85: shtTempHumidSensor = (SHT *) new SHT85(); break;
} }
shtTempHumidSensor->begin(shtI2cAddress, i2c_sda, i2c_scl); shtTempHumidSensor->begin(shtI2cAddress); // uses &Wire
if (shtTempHumidSensor->readStatus() == 0xFFFF) { if (shtTempHumidSensor->readStatus() == 0xFFFF) {
DEBUG_PRINTF("[%s] SHT init failed!\n", _name); DEBUG_PRINTF("[%s] SHT init failed!\n", _name);
cleanup(); cleanup();
@ -132,13 +131,6 @@ void ShtUsermod::cleanupShtTempHumiditySensor()
void ShtUsermod::cleanup() void ShtUsermod::cleanup()
{ {
cleanupShtTempHumiditySensor(); cleanupShtTempHumiditySensor();
if (pinAllocDone) {
PinManagerPinType pins[2] = { { i2c_sda, true }, { i2c_scl, true } };
pinManager.deallocateMultiplePins(pins, 2, PinOwner::HW_I2C);
pinAllocDone = false;
}
enabled = false; enabled = false;
} }
@ -237,14 +229,12 @@ void ShtUsermod::appendDeviceToMqttDiscoveryMessage(JsonDocument& root) {
void ShtUsermod::setup() void ShtUsermod::setup()
{ {
if (enabled) { if (enabled) {
PinManagerPinType pins[2] = { { i2c_sda, true }, { i2c_scl, true } }; // GPIOs can be set to -1 , so check they're gt zero
// GPIOs can be set to -1 and allocateMultiplePins() will return true, so check they're gt zero if (i2c_sda < 0 || i2c_scl < 0) {
if (i2c_sda < 0 || i2c_scl < 0 || !pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { DEBUG_PRINTF("[%s] I2C bus not initialised!\n", _name);
DEBUG_PRINTF("[%s] SHT pin allocation failed!\n", _name);
cleanup(); cleanup();
return; return;
} }
pinAllocDone = true;
initShtTempHumiditySensor(); initShtTempHumiditySensor();

View File

@ -88,7 +88,8 @@ typedef enum {
SSD1305, // U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C SSD1305, // U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C
SSD1305_64, // U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C SSD1305_64, // U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C
SSD1306_SPI, // U8X8_SSD1306_128X32_NONAME_HW_SPI SSD1306_SPI, // U8X8_SSD1306_128X32_NONAME_HW_SPI
SSD1306_SPI64 // U8X8_SSD1306_128X64_NONAME_HW_SPI SSD1306_SPI64, // U8X8_SSD1306_128X64_NONAME_HW_SPI
SSD1309_SPI64 // U8X8_SSD1309_128X64_NONAME0_4W_HW_SPI
} DisplayType; } DisplayType;
@ -533,24 +534,18 @@ void FourLineDisplayUsermod::sleepOrClock(bool enabled) {
// gets called once at boot. Do all initialization that doesn't depend on // gets called once at boot. Do all initialization that doesn't depend on
// network here // network here
void FourLineDisplayUsermod::setup() { void FourLineDisplayUsermod::setup() {
if (type == NONE || !enabled) return; bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64 || type == SSD1309_SPI64);
bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64);
// check if pins are -1 and disable usermod as PinManager::allocateMultiplePins() will accept -1 as a valid pin // check if pins are -1 and disable usermod as PinManager::allocateMultiplePins() will accept -1 as a valid pin
if (isSPI) { if (isSPI) {
PinManagerPinType cspins[3] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true } }; if (spi_sclk<0 || spi_mosi<0 || ioPin[0]<0 || ioPin[1]<0 || ioPin[1]<0) {
if (ioPin[0]==-1 || ioPin[1]==-1 || ioPin[1]==-1) { type=NONE; return; }
if (!pinManager.allocateMultiplePins(cspins, 3, PinOwner::UM_FourLineDisplay)) { type=NONE; return; }
PinManagerPinType pins[2] = { { spi_sclk, true }, { spi_mosi, true } };
if (spi_sclk==-1 || spi_mosi==-1 || !pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_SPI)) {
pinManager.deallocateMultiplePins(cspins, 3, PinOwner::UM_FourLineDisplay);
type = NONE; type = NONE;
return; } else {
PinManagerPinType cspins[3] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true } };
if (!pinManager.allocateMultiplePins(cspins, 3, PinOwner::UM_FourLineDisplay)) { type = NONE; }
} }
} else { } else {
PinManagerPinType pins[2] = { {i2c_scl, true }, { i2c_sda, true } }; if (i2c_scl<0 || i2c_sda<0) { type=NONE; }
if (i2c_scl==-1 || i2c_sda==-1 || !pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { type=NONE; return; }
} }
DEBUG_PRINTLN(F("Allocating display.")); DEBUG_PRINTLN(F("Allocating display."));
@ -564,19 +559,15 @@ void FourLineDisplayUsermod::setup() {
// U8X8 uses global SPI variable that is attached to VSPI bus on ESP32 // U8X8 uses global SPI variable that is attached to VSPI bus on ESP32
case SSD1306_SPI: u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_HW_SPI(ioPin[0], ioPin[1], ioPin[2]); break; // Pins are cs, dc, reset case SSD1306_SPI: u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_4W_HW_SPI(ioPin[0], ioPin[1], ioPin[2]); break; // Pins are cs, dc, reset
case SSD1306_SPI64: u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_HW_SPI(ioPin[0], ioPin[1], ioPin[2]); break; // Pins are cs, dc, reset case SSD1306_SPI64: u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_HW_SPI(ioPin[0], ioPin[1], ioPin[2]); break; // Pins are cs, dc, reset
case SSD1309_SPI64: u8x8 = (U8X8 *) new U8X8_SSD1309_128X64_NONAME0_4W_HW_SPI(ioPin[0], ioPin[1], ioPin[2]); break; // Pins are cs, dc, reset
// catchall // catchall
default: u8x8 = (U8X8 *) new U8X8_NULL(); break; default: u8x8 = (U8X8 *) new U8X8_NULL(); enabled = false; break; // catchall to create U8x8 instance
} }
if (nullptr == u8x8) { if (nullptr == u8x8) {
DEBUG_PRINTLN(F("Display init failed.")); DEBUG_PRINTLN(F("Display init failed."));
if (isSPI) { if (isSPI) {
int8_t pins[] = {spi_sclk, spi_mosi};
pinManager.deallocateMultiplePins((const uint8_t*)pins, 2, PinOwner::HW_SPI);
pinManager.deallocateMultiplePins((const uint8_t*)ioPin, 3, PinOwner::UM_FourLineDisplay); pinManager.deallocateMultiplePins((const uint8_t*)ioPin, 3, PinOwner::UM_FourLineDisplay);
} else {
int8_t pins[] = {i2c_scl, i2c_sda};
pinManager.deallocateMultiplePins((const uint8_t*)pins, 2, PinOwner::HW_I2C);
} }
type = NONE; type = NONE;
return; return;
@ -1215,6 +1206,7 @@ void FourLineDisplayUsermod::appendConfigData() {
oappend(SET_F("addOption(dd,'SSD1305 128x64',5);")); oappend(SET_F("addOption(dd,'SSD1305 128x64',5);"));
oappend(SET_F("addOption(dd,'SSD1306 SPI',6);")); oappend(SET_F("addOption(dd,'SSD1306 SPI',6);"));
oappend(SET_F("addOption(dd,'SSD1306 SPI 128x64',7);")); oappend(SET_F("addOption(dd,'SSD1306 SPI 128x64',7);"));
oappend(SET_F("addOption(dd,'SSD1309 SPI 128x64',8);"));
oappend(SET_F("addInfo('4LineDisplay:type',1,'<br><i class=\"warn\">Change may require reboot</i>','');")); oappend(SET_F("addInfo('4LineDisplay:type',1,'<br><i class=\"warn\">Change may require reboot</i>','');"));
oappend(SET_F("addInfo('4LineDisplay:pin[]',0,'','SPI CS');")); oappend(SET_F("addInfo('4LineDisplay:pin[]',0,'','SPI CS');"));
oappend(SET_F("addInfo('4LineDisplay:pin[]',1,'','SPI DC');")); oappend(SET_F("addInfo('4LineDisplay:pin[]',1,'','SPI DC');"));
@ -1306,38 +1298,30 @@ bool FourLineDisplayUsermod::readFromConfig(JsonObject& root) {
bool pinsChanged = false; bool pinsChanged = false;
for (byte i=0; i<3; i++) if (ioPin[i] != oldPin[i]) { pinsChanged = true; break; } for (byte i=0; i<3; i++) if (ioPin[i] != oldPin[i]) { pinsChanged = true; break; }
if (pinsChanged || type!=newType) { if (pinsChanged || type!=newType) {
bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64); bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64 || type == SSD1309_SPI64);
bool newSPI = (newType == SSD1306_SPI || newType == SSD1306_SPI64); bool newSPI = (newType == SSD1306_SPI || newType == SSD1306_SPI64 || newType == SSD1309_SPI64);
if (isSPI) { if (isSPI) {
if (pinsChanged || !newSPI) pinManager.deallocateMultiplePins((const uint8_t*)oldPin, 3, PinOwner::UM_FourLineDisplay); if (pinsChanged || !newSPI) pinManager.deallocateMultiplePins((const uint8_t*)oldPin, 3, PinOwner::UM_FourLineDisplay);
if (!newSPI) { if (!newSPI) {
// was SPI but is no longer SPI // was SPI but is no longer SPI
int8_t oldPins[] = {spi_sclk, spi_mosi}; if (i2c_scl<0 || i2c_sda<0) { newType=NONE; }
pinManager.deallocateMultiplePins((const uint8_t*)oldPins, 2, PinOwner::HW_SPI);
PinManagerPinType pins[2] = { {i2c_scl, true }, { i2c_sda, true } };
if (i2c_scl==-1 || i2c_sda==-1 || !pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_I2C)) { newType=NONE; }
} else { } else {
// still SPI but pins changed // still SPI but pins changed
PinManagerPinType cspins[3] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true } }; PinManagerPinType cspins[3] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true } };
if (ioPin[0]==-1 || ioPin[1]==-1 || ioPin[1]==-1) { newType=NONE; } if (ioPin[0]<0 || ioPin[1]<0 || ioPin[1]<0) { newType=NONE; }
else if (!pinManager.allocateMultiplePins(cspins, 3, PinOwner::UM_FourLineDisplay)) { newType=NONE; } else if (!pinManager.allocateMultiplePins(cspins, 3, PinOwner::UM_FourLineDisplay)) { newType=NONE; }
} }
} else if (newSPI) { } else if (newSPI) {
// was I2C but is now SPI // was I2C but is now SPI
int8_t oldPins[] = {i2c_scl, i2c_sda}; if (spi_sclk<0 || spi_mosi<0) {
pinManager.deallocateMultiplePins((const uint8_t*)oldPins, 2, PinOwner::HW_I2C);
PinManagerPinType pins[3] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true } };
if (ioPin[0]==-1 || ioPin[1]==-1 || ioPin[1]==-1) { newType=NONE; }
else if (!pinManager.allocateMultiplePins(pins, 3, PinOwner::UM_FourLineDisplay)) { newType=NONE; }
else {
PinManagerPinType pins[2] = { { spi_sclk, true }, { spi_mosi, true } };
if (spi_sclk==-1 || spi_mosi==-1 || !pinManager.allocateMultiplePins(pins, 2, PinOwner::HW_SPI)) {
pinManager.deallocateMultiplePins(pins, 3, PinOwner::UM_FourLineDisplay);
newType=NONE; newType=NONE;
} } else {
PinManagerPinType pins[3] = { { ioPin[0], true }, { ioPin[1], true }, { ioPin[2], true } };
if (ioPin[0]<0 || ioPin[1]<0 || ioPin[1]<0) { newType=NONE; }
else if (!pinManager.allocateMultiplePins(pins, 3, PinOwner::UM_FourLineDisplay)) { newType=NONE; }
} }
} else { } else {
// just I2C tye changed // just I2C type changed
} }
type = newType; type = newType;
switch (type) { switch (type) {
@ -1369,8 +1353,12 @@ bool FourLineDisplayUsermod::readFromConfig(JsonObject& root) {
u8x8_Setup(u8x8->getU8x8(), u8x8_d_ssd1306_128x64_noname, u8x8_cad_001, u8x8_byte_arduino_hw_spi, u8x8_gpio_and_delay_arduino); u8x8_Setup(u8x8->getU8x8(), u8x8_d_ssd1306_128x64_noname, u8x8_cad_001, u8x8_byte_arduino_hw_spi, u8x8_gpio_and_delay_arduino);
u8x8_SetPin_4Wire_HW_SPI(u8x8->getU8x8(), ioPin[0], ioPin[1], ioPin[2]); // Pins are cs, dc, reset u8x8_SetPin_4Wire_HW_SPI(u8x8->getU8x8(), ioPin[0], ioPin[1], ioPin[2]); // Pins are cs, dc, reset
break; break;
case SSD1309_SPI64:
u8x8_Setup(u8x8->getU8x8(), u8x8_d_ssd1309_128x64_noname0, u8x8_cad_001, u8x8_byte_arduino_hw_spi, u8x8_gpio_and_delay_arduino);
u8x8_SetPin_4Wire_HW_SPI(u8x8->getU8x8(), ioPin[0], ioPin[1], ioPin[2]); // Pins are cs, dc, reset
default: default:
u8x8_Setup(u8x8->getU8x8(), u8x8_d_null_cb, u8x8_cad_empty, u8x8_byte_empty, u8x8_dummy_cb); u8x8_Setup(u8x8->getU8x8(), u8x8_d_null_cb, u8x8_cad_empty, u8x8_byte_empty, u8x8_dummy_cb);
enabled = false;
break; break;
} }
startDisplay(); startDisplay();

View File

@ -474,7 +474,7 @@ void RotaryEncoderUIUsermod::setup()
DEBUG_PRINTLN(F("Usermod Rotary Encoder init.")); DEBUG_PRINTLN(F("Usermod Rotary Encoder init."));
if (usePcf8574) { if (usePcf8574) {
if ((i2c_sda == i2c_scl && i2c_sda == -1) || pinA<0 || pinB<0 || pinC<0) { if (i2c_sda < 0 || i2c_scl < 0 || pinA < 0 || pinB < 0 || pinC < 0) {
DEBUG_PRINTLN(F("I2C and/or PCF8574 pins unused, disabling.")); DEBUG_PRINTLN(F("I2C and/or PCF8574 pins unused, disabling."));
enabled = false; enabled = false;
return; return;

View File

@ -8,7 +8,7 @@
*/ */
// version code in format yymmddb (b = daily build) // version code in format yymmddb (b = daily build)
#define VERSION 2306180 #define VERSION 2306210
//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