- preventing strip blinking due to usermod running
- temeperature reading with 0.5°C precision
This commit is contained in:
Blaz Kristan 2021-05-26 16:11:48 +02:00
parent 1ba70706c2
commit 6b5c2be701
5 changed files with 977 additions and 967 deletions

View File

@ -222,9 +222,8 @@ public:
void loop() void loop()
{ {
// only check sensors 10x/s // only check sensors 10x/s
unsigned long now = millis(); if (millis() - lastLoop < 100 || strip.isUpdating()) return;
if (now - lastLoop < 100) return; lastLoop = millis();
lastLoop = now;
if (!updatePIRsensorState()) { if (!updatePIRsensorState()) {
handleOffTimer(); handleOffTimer();

View File

@ -27,6 +27,9 @@ class UsermodTemperature : public Usermod {
int8_t temperaturePin = TEMPERATURE_PIN; int8_t temperaturePin = TEMPERATURE_PIN;
// measurement unit (true==°C, false==°F) // measurement unit (true==°C, false==°F)
bool degC = true; bool degC = true;
// using parasite power on the sensor
bool parasite = false;
// how often do we read from sensor?
unsigned long readingInterval = USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL; unsigned long readingInterval = USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL;
// set last reading as "40 sec before boot", so first reading is taken after 20 sec // set last reading as "40 sec before boot", so first reading is taken after 20 sec
unsigned long lastMeasurement = UINT32_MAX - USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL; unsigned long lastMeasurement = UINT32_MAX - USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL;
@ -45,25 +48,24 @@ class UsermodTemperature : public Usermod {
static const char _name[]; static const char _name[];
static const char _enabled[]; static const char _enabled[];
static const char _readInterval[]; static const char _readInterval[];
static const char _parasite[];
//Dallas sensor quick (& dirty) reading. Credit to - Author: Peter Scargill, August 17th, 2013 //Dallas sensor quick (& dirty) reading. Credit to - Author: Peter Scargill, August 17th, 2013
int16_t readDallas() { float readDallas() {
byte i; byte i;
byte data[2]; byte data[2];
int16_t result; // raw data from sensor int16_t result; // raw data from sensor
oneWire->reset(); if (!oneWire->reset()) return -127.0f; // send reset command and fail fast
oneWire->write(0xCC); // skip ROM oneWire->skip(); // skip ROM
oneWire->write(0xBE); // read (temperature) from EEPROM oneWire->write(0xBE); // read (temperature) from EEPROM
for (i=0; i < 2; i++) data[i] = oneWire->read(); // first 2 bytes contain temperature for (i=0; i < 2; i++) data[i] = oneWire->read(); // first 2 bytes contain temperature
for (i=2; i < 8; i++) oneWire->read(); // read unused bytes for (i=2; i < 8; i++) oneWire->read(); // read unused bytes
result = (data[1]<<8) | data[0]; result = (data[1]<<4) | (data[0]>>4); // we only need whole part, we will add fraction when returning
result >>= 4; // 9-bit precision accurate to 1°C (/16) if (data[1]&0x80) result |= 0xFF00; // fix negative value
if (data[1]&0x80) result |= 0x8000; // fix negative value
//if (data[0]&0x08) ++result;
oneWire->reset(); oneWire->reset();
oneWire->write(0xCC); // skip ROM oneWire->skip(); // skip ROM
oneWire->write(0x44,0); // request new temperature reading (without parasite power) oneWire->write(0x44,parasite); // request new temperature reading (without parasite power)
return result; return (float)result + ((data[0]&0x0008) ? 0.5f : 0.0f);
} }
void requestTemperatures() { void requestTemperatures() {
@ -225,6 +227,7 @@ class UsermodTemperature : public Usermod {
top["pin"] = temperaturePin; // usermodparam top["pin"] = temperaturePin; // usermodparam
top["degC"] = degC; // usermodparam top["degC"] = degC; // usermodparam
top[FPSTR(_readInterval)] = readingInterval / 1000; top[FPSTR(_readInterval)] = readingInterval / 1000;
top[FPSTR(_parasite)] = parasite;
DEBUG_PRINTLN(F("Temperature config saved.")); DEBUG_PRINTLN(F("Temperature config saved."));
} }
@ -253,7 +256,14 @@ class UsermodTemperature : public Usermod {
degC = (bool)(str!="off"); // off is guaranteed to be present degC = (bool)(str!="off"); // off is guaranteed to be present
} }
readingInterval = min(120,max(10,top[FPSTR(_readInterval)].as<int>())) * 1000; // convert to ms readingInterval = min(120,max(10,top[FPSTR(_readInterval)].as<int>())) * 1000; // convert to ms
DEBUG_PRINTLN(F("Temperature config (re)loaded.")); if (top[FPSTR(_parasite)].is<bool>()) {
// reading from cfg.json
parasite = top[FPSTR(_parasite)].as<bool>();
} else {
// new configuration from set.cpp
String str = top[FPSTR(_parasite)]; // checkbox -> off or on
parasite = (bool)(str!="off"); // off is guaranteed to be present
}
} else { } else {
DEBUG_PRINTLN(F("No config found. (Using defaults.)")); DEBUG_PRINTLN(F("No config found. (Using defaults.)"));
} }
@ -261,7 +271,9 @@ class UsermodTemperature : public Usermod {
if (!initDone) { if (!initDone) {
// first run: reading from cfg.json // first run: reading from cfg.json
temperaturePin = newTemperaturePin; temperaturePin = newTemperaturePin;
DEBUG_PRINTLN(F("Temperature config loaded."));
} else { } else {
DEBUG_PRINTLN(F("Temperature config re-loaded."));
// changing paramters from settings page // changing paramters from settings page
if (newTemperaturePin != temperaturePin) { if (newTemperaturePin != temperaturePin) {
// deallocate pin and release memory // deallocate pin and release memory
@ -283,4 +295,5 @@ class UsermodTemperature : public Usermod {
// strings to reduce flash memory usage (used more than twice) // strings to reduce flash memory usage (used more than twice)
const char UsermodTemperature::_name[] PROGMEM = "Temperature"; const char UsermodTemperature::_name[] PROGMEM = "Temperature";
const char UsermodTemperature::_enabled[] PROGMEM = "enabled"; const char UsermodTemperature::_enabled[] PROGMEM = "enabled";
const char UsermodTemperature::_readInterval[] PROGMEM = "read-interval-s"; const char UsermodTemperature::_readInterval[] PROGMEM = "read-interval-s";
const char UsermodTemperature::_parasite[] PROGMEM = "parasite-pwr";

View File

@ -1,244 +1,244 @@
#pragma once #pragma once
#include "wled.h" #include "wled.h"
// v2 Usermod to automatically save settings // v2 Usermod to automatically save settings
// to configurable preset after a change to any of // to configurable preset after a change to any of
// //
// * brightness // * brightness
// * effect speed // * effect speed
// * effect intensity // * effect intensity
// * mode (effect) // * mode (effect)
// * palette // * palette
// //
// but it will wait for configurable number of seconds, a "settle" // but it will wait for configurable number of seconds, a "settle"
// period in case there are other changes (any change will // period in case there are other changes (any change will
// extend the "settle" window). // extend the "settle" window).
// //
// It can be configured to load auto saved preset at startup, // It can be configured to load auto saved preset at startup,
// during the first `loop()`. // during the first `loop()`.
// //
// AutoSaveUsermod is standalone, but if FourLineDisplayUsermod // AutoSaveUsermod is standalone, but if FourLineDisplayUsermod
// is installed, it will notify the user of the saved changes. // is installed, it will notify the user of the saved changes.
// format: "~ MM-DD HH:MM:SS ~" // format: "~ MM-DD HH:MM:SS ~"
#define PRESET_NAME_BUFFER_SIZE 25 #define PRESET_NAME_BUFFER_SIZE 25
class AutoSaveUsermod : public Usermod { class AutoSaveUsermod : public Usermod {
private: private:
bool firstLoop = true; bool firstLoop = true;
bool initDone = false; bool initDone = false;
bool enabled = true; bool enabled = true;
// configurable parameters // configurable parameters
unsigned long autoSaveAfterSec = 15; // 15s by default unsigned long autoSaveAfterSec = 15; // 15s by default
uint8_t autoSavePreset = 250; // last possible preset uint8_t autoSavePreset = 250; // last possible preset
bool applyAutoSaveOnBoot = false; // do we load auto-saved preset on boot? bool applyAutoSaveOnBoot = false; // do we load auto-saved preset on boot?
// If we've detected the need to auto save, this will be non zero. // If we've detected the need to auto save, this will be non zero.
unsigned long autoSaveAfter = 0; unsigned long autoSaveAfter = 0;
uint8_t knownBrightness = 0; uint8_t knownBrightness = 0;
uint8_t knownEffectSpeed = 0; uint8_t knownEffectSpeed = 0;
uint8_t knownEffectIntensity = 0; uint8_t knownEffectIntensity = 0;
uint8_t knownMode = 0; uint8_t knownMode = 0;
uint8_t knownPalette = 0; uint8_t knownPalette = 0;
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
FourLineDisplayUsermod* display; FourLineDisplayUsermod* display;
#endif #endif
// strings to reduce flash memory usage (used more than twice) // strings to reduce flash memory usage (used more than twice)
static const char _name[]; static const char _name[];
static const char _autoSaveEnabled[]; static const char _autoSaveEnabled[];
static const char _autoSaveAfterSec[]; static const char _autoSaveAfterSec[];
static const char _autoSavePreset[]; static const char _autoSavePreset[];
static const char _autoSaveApplyOnBoot[]; static const char _autoSaveApplyOnBoot[];
void inline saveSettings() { void inline saveSettings() {
char presetNameBuffer[PRESET_NAME_BUFFER_SIZE]; char presetNameBuffer[PRESET_NAME_BUFFER_SIZE];
updateLocalTime(); updateLocalTime();
sprintf_P(presetNameBuffer, sprintf_P(presetNameBuffer,
PSTR("~ %02d-%02d %02d:%02d:%02d ~"), PSTR("~ %02d-%02d %02d:%02d:%02d ~"),
month(localTime), day(localTime), month(localTime), day(localTime),
hour(localTime), minute(localTime), second(localTime)); hour(localTime), minute(localTime), second(localTime));
savePreset(autoSavePreset, true, presetNameBuffer); savePreset(autoSavePreset, true, presetNameBuffer);
} }
void inline displayOverlay() { void inline displayOverlay() {
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
if (display != nullptr) { if (display != nullptr) {
display->wakeDisplay(); display->wakeDisplay();
display->overlay("Settings", "Auto Saved", 1500); display->overlay("Settings", "Auto Saved", 1500);
} }
#endif #endif
} }
public: public:
// 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 setup() { void setup() {
#ifdef USERMOD_FOUR_LINE_DISPLAY #ifdef USERMOD_FOUR_LINE_DISPLAY
// This Usermod has enhanced funcionality if // This Usermod has enhanced funcionality if
// FourLineDisplayUsermod is available. // FourLineDisplayUsermod is available.
display = (FourLineDisplayUsermod*) usermods.lookup(USERMOD_ID_FOUR_LINE_DISP); display = (FourLineDisplayUsermod*) usermods.lookup(USERMOD_ID_FOUR_LINE_DISP);
#endif #endif
initDone = true; initDone = true;
} }
// gets called every time WiFi is (re-)connected. Initialize own network // gets called every time WiFi is (re-)connected. Initialize own network
// interfaces here // interfaces here
void connected() {} void connected() {}
/* /*
* Da loop. * Da loop.
*/ */
void loop() { void loop() {
if (!autoSaveAfterSec || !enabled) return; // setting 0 as autosave seconds disables autosave if (!autoSaveAfterSec || !enabled || strip.isUpdating()) return; // setting 0 as autosave seconds disables autosave
unsigned long now = millis(); unsigned long now = millis();
uint8_t currentMode = strip.getMode(); uint8_t currentMode = strip.getMode();
uint8_t currentPalette = strip.getSegment(0).palette; uint8_t currentPalette = strip.getSegment(0).palette;
if (firstLoop) { if (firstLoop) {
firstLoop = false; firstLoop = false;
if (applyAutoSaveOnBoot) applyPreset(autoSavePreset); if (applyAutoSaveOnBoot) applyPreset(autoSavePreset);
knownBrightness = bri; knownBrightness = bri;
knownEffectSpeed = effectSpeed; knownEffectSpeed = effectSpeed;
knownEffectIntensity = effectIntensity; knownEffectIntensity = effectIntensity;
knownMode = currentMode; knownMode = currentMode;
knownPalette = currentPalette; knownPalette = currentPalette;
return; return;
} }
unsigned long wouldAutoSaveAfter = now + autoSaveAfterSec*1000; unsigned long wouldAutoSaveAfter = now + autoSaveAfterSec*1000;
if (knownBrightness != bri) { if (knownBrightness != bri) {
knownBrightness = bri; knownBrightness = bri;
autoSaveAfter = wouldAutoSaveAfter; autoSaveAfter = wouldAutoSaveAfter;
} else if (knownEffectSpeed != effectSpeed) { } else if (knownEffectSpeed != effectSpeed) {
knownEffectSpeed = effectSpeed; knownEffectSpeed = effectSpeed;
autoSaveAfter = wouldAutoSaveAfter; autoSaveAfter = wouldAutoSaveAfter;
} else if (knownEffectIntensity != effectIntensity) { } else if (knownEffectIntensity != effectIntensity) {
knownEffectIntensity = effectIntensity; knownEffectIntensity = effectIntensity;
autoSaveAfter = wouldAutoSaveAfter; autoSaveAfter = wouldAutoSaveAfter;
} else if (knownMode != currentMode) { } else if (knownMode != currentMode) {
knownMode = currentMode; knownMode = currentMode;
autoSaveAfter = wouldAutoSaveAfter; autoSaveAfter = wouldAutoSaveAfter;
} else if (knownPalette != currentPalette) { } else if (knownPalette != currentPalette) {
knownPalette = currentPalette; knownPalette = currentPalette;
autoSaveAfter = wouldAutoSaveAfter; autoSaveAfter = wouldAutoSaveAfter;
} }
if (autoSaveAfter && now > autoSaveAfter) { if (autoSaveAfter && now > autoSaveAfter) {
autoSaveAfter = 0; autoSaveAfter = 0;
// Time to auto save. You may have some flickry? // Time to auto save. You may have some flickry?
saveSettings(); saveSettings();
displayOverlay(); displayOverlay();
} }
} }
/* /*
* addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API. * addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
* Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI. * Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI.
* Below it is shown how this could be used for e.g. a light sensor * Below it is shown how this could be used for e.g. a light sensor
*/ */
//void addToJsonInfo(JsonObject& root) { //void addToJsonInfo(JsonObject& root) {
//JsonObject user = root["u"]; //JsonObject user = root["u"];
//if (user.isNull()) user = root.createNestedObject("u"); //if (user.isNull()) user = root.createNestedObject("u");
//JsonArray data = user.createNestedArray(F("Autosave")); //JsonArray data = user.createNestedArray(F("Autosave"));
//data.add(F("Loaded.")); //data.add(F("Loaded."));
//} //}
/* /*
* addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object). * addToJsonState() can be used to add custom entries to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients * Values in the state object may be modified by connected clients
*/ */
//void addToJsonState(JsonObject& root) { //void addToJsonState(JsonObject& root) {
//} //}
/* /*
* readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object). * readFromJsonState() can be used to receive data clients send to the /json/state part of the JSON API (state object).
* Values in the state object may be modified by connected clients * Values in the state object may be modified by connected clients
*/ */
//void readFromJsonState(JsonObject& root) { //void readFromJsonState(JsonObject& root) {
// if (!initDone) return; // prevent crash on boot applyPreset() // if (!initDone) return; // prevent crash on boot applyPreset()
//} //}
/* /*
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object. * 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) * It will be called by WLED when settings are actually saved (for example, LED settings are saved)
* If you want to force saving the current state, use serializeConfig() in your loop(). * If you want to force saving the current state, use serializeConfig() in your loop().
* *
* CAUTION: serializeConfig() will initiate a filesystem write operation. * CAUTION: serializeConfig() will initiate a filesystem write operation.
* It might cause the LEDs to stutter and will cause flash wear if called too often. * It might cause the LEDs to stutter and will cause flash wear if called too often.
* Use it sparingly and always in the loop, never in network callbacks! * Use it sparingly and always in the loop, never in network callbacks!
* *
* addToConfig() will also not yet add your setting to one of the settings pages automatically. * addToConfig() will also not yet add your setting to one of the settings pages automatically.
* To make that work you still have to add the setting to the HTML, xml.cpp and set.cpp manually. * To make that work you still have to add the setting to the HTML, xml.cpp and set.cpp manually.
* *
* I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings! * I highly recommend checking out the basics of ArduinoJson serialization and deserialization in order to use custom settings!
*/ */
void addToConfig(JsonObject& root) { void addToConfig(JsonObject& root) {
// we add JSON object: {"Autosave": {"autoSaveAfterSec": 10, "autoSavePreset": 99}} // we add JSON object: {"Autosave": {"autoSaveAfterSec": 10, "autoSavePreset": 99}}
JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname
top[FPSTR(_autoSaveEnabled)] = enabled; top[FPSTR(_autoSaveEnabled)] = enabled;
top[FPSTR(_autoSaveAfterSec)] = autoSaveAfterSec; // usermodparam top[FPSTR(_autoSaveAfterSec)] = autoSaveAfterSec; // usermodparam
top[FPSTR(_autoSavePreset)] = autoSavePreset; // usermodparam top[FPSTR(_autoSavePreset)] = autoSavePreset; // usermodparam
top[FPSTR(_autoSaveApplyOnBoot)] = applyAutoSaveOnBoot; top[FPSTR(_autoSaveApplyOnBoot)] = applyAutoSaveOnBoot;
DEBUG_PRINTLN(F("Autosave config saved.")); DEBUG_PRINTLN(F("Autosave config saved."));
} }
/* /*
* readFromConfig() can be used to read back the custom settings you added with addToConfig(). * readFromConfig() can be used to read back the custom settings you added with addToConfig().
* This is called by WLED when settings are loaded (currently this only happens once immediately after boot) * This is called by WLED when settings are loaded (currently this only happens once immediately after boot)
* *
* readFromConfig() is called BEFORE setup(). This means you can use your persistent values in setup() (e.g. pin assignments, buffer sizes), * readFromConfig() is called BEFORE setup(). This means you can use your persistent values in setup() (e.g. pin assignments, buffer sizes),
* but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup. * but also that if you want to write persistent values to a dynamic buffer, you'd need to allocate it here instead of in setup.
* If you don't know what that is, don't fret. It most likely doesn't affect your use case :) * If you don't know what that is, don't fret. It most likely doesn't affect your use case :)
*/ */
void readFromConfig(JsonObject& root) { void readFromConfig(JsonObject& root) {
// we look for JSON object: {"Autosave": {"autoSaveAfterSec": 10, "autoSavePreset": 99}} // we look for JSON object: {"Autosave": {"autoSaveAfterSec": 10, "autoSavePreset": 99}}
JsonObject top = root[FPSTR(_name)]; JsonObject top = root[FPSTR(_name)];
if (top.isNull()) { if (top.isNull()) {
DEBUG_PRINTLN(F("No config found. (Using defaults.)")); DEBUG_PRINTLN(F("No config found. (Using defaults.)"));
return; return;
} }
if (top[FPSTR(_autoSaveEnabled)].is<bool>()) { if (top[FPSTR(_autoSaveEnabled)].is<bool>()) {
// reading from cfg.json // reading from cfg.json
enabled = top[FPSTR(_autoSaveEnabled)].as<bool>(); enabled = top[FPSTR(_autoSaveEnabled)].as<bool>();
} else { } else {
// reading from POST message // reading from POST message
String str = top[FPSTR(_autoSaveEnabled)]; // checkbox -> off or on String str = top[FPSTR(_autoSaveEnabled)]; // checkbox -> off or on
enabled = (bool)(str!="off"); // off is guaranteed to be present enabled = (bool)(str!="off"); // off is guaranteed to be present
} }
autoSaveAfterSec = min(3600,max(10,top[FPSTR(_autoSaveAfterSec)].as<int>())); autoSaveAfterSec = min(3600,max(10,top[FPSTR(_autoSaveAfterSec)].as<int>()));
autoSavePreset = min(250,max(100,top[FPSTR(_autoSavePreset)].as<int>())); autoSavePreset = min(250,max(100,top[FPSTR(_autoSavePreset)].as<int>()));
if (top[FPSTR(_autoSaveApplyOnBoot)].is<bool>()) { if (top[FPSTR(_autoSaveApplyOnBoot)].is<bool>()) {
// reading from cfg.json // reading from cfg.json
applyAutoSaveOnBoot = top[FPSTR(_autoSaveApplyOnBoot)].as<bool>(); applyAutoSaveOnBoot = top[FPSTR(_autoSaveApplyOnBoot)].as<bool>();
} else { } else {
// reading from POST message // reading from POST message
String str = top[FPSTR(_autoSaveApplyOnBoot)]; // checkbox -> off or on String str = top[FPSTR(_autoSaveApplyOnBoot)]; // checkbox -> off or on
applyAutoSaveOnBoot = (bool)(str!="off"); // off is guaranteed to be present applyAutoSaveOnBoot = (bool)(str!="off"); // off is guaranteed to be present
} }
DEBUG_PRINTLN(F("Autosave config (re)loaded.")); DEBUG_PRINTLN(F("Autosave config (re)loaded."));
} }
/* /*
* 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.
*/ */
uint16_t getId() { uint16_t getId() {
return USERMOD_ID_AUTO_SAVE; return USERMOD_ID_AUTO_SAVE;
} }
}; };
// strings to reduce flash memory usage (used more than twice) // strings to reduce flash memory usage (used more than twice)
const char AutoSaveUsermod::_name[] PROGMEM = "Autosave"; const char AutoSaveUsermod::_name[] PROGMEM = "Autosave";
const char AutoSaveUsermod::_autoSaveEnabled[] PROGMEM = "enabled"; const char AutoSaveUsermod::_autoSaveEnabled[] PROGMEM = "enabled";
const char AutoSaveUsermod::_autoSaveAfterSec[] PROGMEM = "autoSaveAfterSec"; const char AutoSaveUsermod::_autoSaveAfterSec[] PROGMEM = "autoSaveAfterSec";
const char AutoSaveUsermod::_autoSavePreset[] PROGMEM = "autoSavePreset"; const char AutoSaveUsermod::_autoSavePreset[] PROGMEM = "autoSavePreset";
const char AutoSaveUsermod::_autoSaveApplyOnBoot[] PROGMEM = "autoSaveApplyOnBoot"; const char AutoSaveUsermod::_autoSaveApplyOnBoot[] PROGMEM = "autoSaveApplyOnBoot";

View File

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