From 57e50d0c33139fb5a2cc5fdb204ab554d64db285 Mon Sep 17 00:00:00 2001 From: Daniel Poelzleithner Date: Tue, 18 May 2021 00:26:21 +0000 Subject: [PATCH 1/8] Allow to override the max segments setting when compiling --- wled00/FX.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wled00/FX.h b/wled00/FX.h index 12eb11f6..ead3052c 100644 --- a/wled00/FX.h +++ b/wled00/FX.h @@ -61,7 +61,9 @@ /* How much data bytes all segments combined may allocate */ #define MAX_SEGMENT_DATA 2048 #else +#ifndef MAX_NUM_SEGMENTS #define MAX_NUM_SEGMENTS 16 +#endif #define MAX_NUM_TRANSITIONS 16 #define MAX_SEGMENT_DATA 8192 #endif From 9e8aadb7506ec66e3b6d8542ca5d5e8e1f060284 Mon Sep 17 00:00:00 2001 From: Blaz Kristan Date: Sun, 27 Jun 2021 15:32:33 +0200 Subject: [PATCH 2/8] Usermod settings v2 - added POST field parsing - simpler handling in readFromConfig() --- .../Animated_Staircase/Animated_Staircase.h | 73 ++--- usermods/EXAMPLE_v2/usermod_v2_example.h | 21 +- .../usermod_Fix_unreachable_netservices.h | 1 + .../usermod_PIR_sensor_switch.h | 67 ++-- .../usermod_sn_photoresistor.h | 41 +-- usermods/Temperature/usermod_temperature.h | 112 +++---- usermods/multi_relay/usermod_multi_relay.h | 63 ++-- .../usermod_v2_auto_save.h | 44 ++- .../usermod_v2_four_line_display.h | 294 ++++++++---------- wled00/data/settings_um.htm | 37 ++- wled00/data/style.css | 32 +- wled00/set.cpp | 48 ++- 12 files changed, 400 insertions(+), 433 deletions(-) diff --git a/usermods/Animated_Staircase/Animated_Staircase.h b/usermods/Animated_Staircase/Animated_Staircase.h index e85db95b..029d9de2 100644 --- a/usermods/Animated_Staircase/Animated_Staircase.h +++ b/usermods/Animated_Staircase/Animated_Staircase.h @@ -426,6 +426,8 @@ class Animated_Staircase : public Usermod { /* * Reads the configuration to internal flash memory before setup() is called. + * + * The function should return true if configuration was successfully loaded or false if there was no configuration. */ bool readFromConfig(JsonObject& root) { bool oldUseUSSensorTop = useUSSensorTop; @@ -435,49 +437,41 @@ class Animated_Staircase : public Usermod { int8_t oldBottomAPin = bottomPIRorTriggerPin; int8_t oldBottomBPin = bottomEchoPin; - bool configComplete = true; - - JsonObject staircase = root[FPSTR(_name)]; - if (!staircase.isNull()) { - if (staircase[FPSTR(_enabled)].is()) { - enabled = staircase[FPSTR(_enabled)].as(); - } else { - String str = staircase[FPSTR(_enabled)]; // checkbox -> off or on - enabled = (bool)(str!="off"); // off is guaranteed to be present - } - segment_delay_ms = min(10000,max(10,staircase[FPSTR(_segmentDelay)].as())); // max delay 10s - on_time_ms = min(900,max(10,staircase[FPSTR(_onTime)].as())) * 1000; // min 10s, max 15min - - if (staircase[FPSTR(_useTopUltrasoundSensor)].is()) { - useUSSensorTop = staircase[FPSTR(_useTopUltrasoundSensor)].as(); - } else { - String str = staircase[FPSTR(_useTopUltrasoundSensor)]; // checkbox -> off or on - useUSSensorTop = (bool)(str!="off"); // off is guaranteed to be present - } - - topPIRorTriggerPin = min(39,max(-1,staircase[FPSTR(_topPIRorTrigger_pin)].as())); - topEchoPin = min(39,max(-1,staircase[FPSTR(_topEcho_pin)].as())); - - if (staircase[FPSTR(_useBottomUltrasoundSensor)].is()) { - useUSSensorBottom = staircase[FPSTR(_useBottomUltrasoundSensor)].as(); - } else { - String str = staircase[FPSTR(_useBottomUltrasoundSensor)]; // checkbox -> off or on - useUSSensorBottom = (bool)(str!="off"); // off is guaranteed to be present - } - bottomPIRorTriggerPin = min(39,max(-1,staircase[FPSTR(_bottomPIRorTrigger_pin)].as())); - bottomEchoPin = min(39,max(-1,staircase[FPSTR(_bottomEcho_pin)].as())); - topMaxDist = min(150,max(30,staircase[FPSTR(_topEchoCm)].as())); // max distnace ~1.5m (a lag of 9ms may be expected) - bottomMaxDist = min(150,max(30,staircase[FPSTR(_bottomEchoCm)].as())); // max distance ~1.5m (a lag of 9ms may be expected) - } else { - DEBUG_PRINTLN(F("No config found. (Using defaults.)")); - configComplete = false; + JsonObject top = root[FPSTR(_name)]; + if (top.isNull()) { + DEBUG_PRINT(FPSTR(_name)); + DEBUG_PRINTLN(F(": No config found. (Using defaults.)")); + return false; } + + enabled = top[FPSTR(_enabled)] | enabled; + + segment_delay_ms = top[FPSTR(_segmentDelay)] | segment_delay_ms; + segment_delay_ms = (unsigned long) min((unsigned long)10000,max((unsigned long)10,(unsigned long)segment_delay_ms)); // max delay 10s + + on_time_ms = top[FPSTR(_onTime)] | on_time_ms/1000; + on_time_ms = min(900,max(10,(int)on_time_ms)) * 1000; // min 10s, max 15min + + useUSSensorTop = top[FPSTR(_useTopUltrasoundSensor)] | useUSSensorTop; + topPIRorTriggerPin = top[FPSTR(_topPIRorTrigger_pin)] | topPIRorTriggerPin; + topEchoPin = top[FPSTR(_topEcho_pin)] | topEchoPin; + + useUSSensorBottom = top[FPSTR(_useBottomUltrasoundSensor)] | useUSSensorBottom; + bottomPIRorTriggerPin = top[FPSTR(_bottomPIRorTrigger_pin)] | bottomPIRorTriggerPin; + bottomEchoPin = top[FPSTR(_bottomEcho_pin)] | bottomEchoPin; + + topMaxDist = top[FPSTR(_topEchoCm)] | topMaxDist; + topMaxDist = min(150,max(30,(int)topMaxDist)); // max distnace ~1.5m (a lag of 9ms may be expected) + bottomMaxDist = top[FPSTR(_bottomEchoCm)] | bottomMaxDist; + bottomMaxDist = min(150,max(30,(int)bottomMaxDist)); // max distance ~1.5m (a lag of 9ms may be expected) + + DEBUG_PRINT(FPSTR(_name)); if (!initDone) { // first run: reading from cfg.json - DEBUG_PRINTLN(F("Staircase config loaded.")); + DEBUG_PRINTLN(F(" config loaded.")); } else { // changing paramters from settings page - DEBUG_PRINTLN(F("Staircase config (re)loaded.")); + DEBUG_PRINTLN(F(" config (re)loaded.")); bool changed = false; if ((oldUseUSSensorTop != useUSSensorTop) || (oldUseUSSensorBottom != useUSSensorBottom) || @@ -493,7 +487,8 @@ class Animated_Staircase : public Usermod { } if (changed) setup(); } - return configComplete; + // use "return !top["newestParameter"].isNull();" when updating Usermod with new features + return true; } /* diff --git a/usermods/EXAMPLE_v2/usermod_v2_example.h b/usermods/EXAMPLE_v2/usermod_v2_example.h index ac466935..3e1f8d3a 100644 --- a/usermods/EXAMPLE_v2/usermod_v2_example.h +++ b/usermods/EXAMPLE_v2/usermod_v2_example.h @@ -23,8 +23,12 @@ //class name. Use something descriptive and leave the ": public Usermod" part :) class MyExampleUsermod : public Usermod { private: + // sample usermod default value for variable (you can also use constructor) + int userVar0 = 42; + //Private class members. You can declare variables and functions only accessible to your usermod here unsigned long lastTime = 0; + public: //Functions called by WLED @@ -140,21 +144,14 @@ class MyExampleUsermod : public Usermod { */ bool readFromConfig(JsonObject& root) { - userVar0 = 42; //set your variables to their boot default value (this can also be done when declaring the variable) - + //set defaults for variables when declaring the variable (class definition or constructor) JsonObject top = root["exampleUsermod"]; - if (!top.isNull()) { - bool configComplete = true; + if (!top.isNull()) return false; - //check if value is there - if (top.containsKey("great")) { - //convert value to the correct type - userVar0 = top["great"].as(); - } else configComplete = false; + userVar0 = top["great"] | userVar0; - if (configComplete) return true; - } - return false; + // use "return !top["newestParameter"].isNull();" when updating Usermod with new features + return true; } diff --git a/usermods/Fix_unreachable_netservices_v2/usermod_Fix_unreachable_netservices.h b/usermods/Fix_unreachable_netservices_v2/usermod_Fix_unreachable_netservices.h index 2e9f43c6..3d441e59 100644 --- a/usermods/Fix_unreachable_netservices_v2/usermod_Fix_unreachable_netservices.h +++ b/usermods/Fix_unreachable_netservices_v2/usermod_Fix_unreachable_netservices.h @@ -155,6 +155,7 @@ Delay ())); // check bounds + if (top.isNull()) { + DEBUG_PRINT(FPSTR(_name)); + DEBUG_PRINTLN(F(": No config found. (Using defaults.)")); + return false; } - if (top[FPSTR(_enabled)] != nullptr) { - if (top[FPSTR(_enabled)].is()) { - enabled = top[FPSTR(_enabled)].as(); // reading from cfg.json - } else { - // change from settings page - String str = top[FPSTR(_enabled)]; // checkbox -> off or on - enabled = (bool)(str!="off"); // off is guaranteed to be present - } - } + PIRsensorPin = top["pin"] | PIRsensorPin; - if (top[FPSTR(_switchOffDelay)] != nullptr) { - m_switchOffDelay = (top[FPSTR(_switchOffDelay)].as() * 1000); - } + enabled = top[FPSTR(_enabled)] | enabled; - if (top[FPSTR(_onPreset)] != nullptr) { - m_onPreset = max(0,min(250,top[FPSTR(_onPreset)].as())); - } + m_switchOffDelay = (top[FPSTR(_switchOffDelay)] | m_switchOffDelay/1000) * 1000; - if (top[FPSTR(_offPreset)] != nullptr) { - m_offPreset = max(0,min(250,top[FPSTR(_offPreset)].as())); - } + m_onPreset = top[FPSTR(_onPreset)] | m_onPreset; + m_onPreset = max(0,min(250,(int)m_onPreset)); - if (top[FPSTR(_nightTime)] != nullptr) { - if (top[FPSTR(_nightTime)].is()) { - m_nightTimeOnly = top[FPSTR(_nightTime)].as(); // reading from cfg.json - } else { - // change from settings page - String str = top[FPSTR(_nightTime)]; // checkbox -> off or on - m_nightTimeOnly = (bool)(str!="off"); // off is guaranteed to be present - } - } + m_offPreset = top[FPSTR(_offPreset)] | m_offPreset; + m_offPreset = max(0,min(250,(int)m_offPreset)); - if (top[FPSTR(_mqttOnly)] != nullptr) { - if (top[FPSTR(_mqttOnly)].is()) { - m_mqttOnly = top[FPSTR(_mqttOnly)].as(); // reading from cfg.json - } else { - // change from settings page - String str = top[FPSTR(_mqttOnly)]; // checkbox -> off or on - m_mqttOnly = (bool)(str!="off"); // off is guaranteed to be present - } - } + m_nightTimeOnly = top[FPSTR(_nightTime)] | m_nightTimeOnly; + m_mqttOnly = top[FPSTR(_mqttOnly)] | m_mqttOnly; + DEBUG_PRINT(FPSTR(_name)); if (!initDone) { // reading config prior to setup() - DEBUG_PRINTLN(F("PIR config loaded.")); + DEBUG_PRINTLN(F(" config loaded.")); } else { if (oldPin != PIRsensorPin || oldEnabled != enabled) { // check if pin is OK @@ -396,10 +371,10 @@ public: if (enabled) { sensorPinState = digitalRead(PIRsensorPin); } - DEBUG_PRINTLN(F("PIR config (re)loaded.")); } + DEBUG_PRINTLN(F(" config (re)loaded.")); } - + // use "return !top["newestParameter"].isNull();" when updating Usermod with new features return true; } diff --git a/usermods/SN_Photoresistor/usermod_sn_photoresistor.h b/usermods/SN_Photoresistor/usermod_sn_photoresistor.h index ac86f390..3b785fad 100644 --- a/usermods/SN_Photoresistor/usermod_sn_photoresistor.h +++ b/usermods/SN_Photoresistor/usermod_sn_photoresistor.h @@ -167,37 +167,28 @@ public: } /** - * readFromConfig() is called before setup() to populate properties from values stored in cfg.json - */ + * readFromConfig() is called before setup() to populate properties from values stored in cfg.json + */ bool readFromConfig(JsonObject &root) { // we look for JSON object. JsonObject top = root[FPSTR(_name)]; - - if (!top.isNull()) - { - if (top[FPSTR(_enabled)].is()) - { - disabled = !top[FPSTR(_enabled)].as(); - } - else - { - String str = top[FPSTR(_enabled)]; // checkbox -> off or on - disabled = (bool)(str == "off"); // off is guaranteed to be present - }; - - readingInterval = min(120, max(10, top[FPSTR(_readInterval)].as())) * 1000; // convert to ms - referenceVoltage = top[FPSTR(_referenceVoltage)].as(); - resistorValue = top[FPSTR(_resistorValue)].as(); - adcPrecision = top[FPSTR(_adcPrecision)].as(); - offset = top[FPSTR(_offset)].as(); - DEBUG_PRINTLN(F("Photoresistor config (re)loaded.")); - } - else - { - DEBUG_PRINTLN(F("No config found. (Using defaults.)")); + if (top.isNull()) { + DEBUG_PRINT(FPSTR(_name)); + DEBUG_PRINTLN(F(": No config found. (Using defaults.)")); return false; } + + disabled = !(top[FPSTR(_enabled)] | !disabled); + readingInterval = (top[FPSTR(_readInterval)] | readingInterval/1000) * 1000; // convert to ms + referenceVoltage = top[FPSTR(_referenceVoltage)] | referenceVoltage; + resistorValue = top[FPSTR(_resistorValue)] | resistorValue; + adcPrecision = top[FPSTR(_adcPrecision)] | adcPrecision; + offset = top[FPSTR(_offset)] | offset; + DEBUG_PRINT(FPSTR(_name)); + DEBUG_PRINTLN(F(" config (re)loaded.")); + + // use "return !top["newestParameter"].isNull();" when updating Usermod with new features return true; } }; diff --git a/usermods/Temperature/usermod_temperature.h b/usermods/Temperature/usermod_temperature.h index 8b418dc4..4efb2a44 100644 --- a/usermods/Temperature/usermod_temperature.h +++ b/usermods/Temperature/usermod_temperature.h @@ -17,11 +17,6 @@ #define USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL 60000 #endif -// how many seconds after boot to take first measurement, 20 seconds -#ifndef USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT -#define USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT 20000 -#endif - class UsermodTemperature : public Usermod { private: @@ -32,9 +27,12 @@ class UsermodTemperature : public Usermod { int8_t temperaturePin = TEMPERATURE_PIN; // measurement unit (true==°C, false==°F) 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; // 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 - USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT); + unsigned long lastMeasurement = UINT32_MAX - USERMOD_DALLASTEMPERATURE_MEASUREMENT_INTERVAL; // last time requestTemperatures was called // used to determine when we can read the sensors temperature // we have to wait at least 93.75 ms after requestTemperatures() is called @@ -42,37 +40,32 @@ class UsermodTemperature : public Usermod { float temperature = -100; // default to -100, DS18B20 only goes down to -50C // indicates requestTemperatures has been called but the sensor measurement is not complete bool waitingForConversion = false; - // flag to indicate we have finished the first readTemperature call - // allows this library to report to the user how long until the first - // measurement - bool readTemperatureComplete = false; // flag set at startup if DS18B20 sensor not found, avoids trying to keep getting // temperature if flashed to a board without a sensor attached - bool disabled = false; + bool enabled = true; // strings to reduce flash memory usage (used more than twice) static const char _name[]; static const char _enabled[]; static const char _readInterval[]; + static const char _parasite[]; //Dallas sensor quick (& dirty) reading. Credit to - Author: Peter Scargill, August 17th, 2013 - int16_t readDallas() { + float readDallas() { byte i; byte data[2]; - int16_t result; // raw data from sensor - oneWire->reset(); - oneWire->write(0xCC); // skip ROM - oneWire->write(0xBE); // read (temperature) from EEPROM + int16_t result; // raw data from sensor + if (!oneWire->reset()) return -127.0f; // send reset command and fail fast + oneWire->skip(); // skip ROM + oneWire->write(0xBE); // read (temperature) from EEPROM 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 - result = (data[1]<<8) | data[0]; - result >>= 4; // 9-bit precision accurate to 1°C (/16) - if (data[1]&0x80) result |= 0xF000; // fix negative value - //if (data[0]&0x08) ++result; + result = (data[1]<<4) | (data[0]>>4); // we only need whole part, we will add fraction when returning + if (data[1]&0x80) result |= 0xFF00; // fix negative value oneWire->reset(); - oneWire->write(0xCC); // skip ROM - oneWire->write(0x44,0); // request new temperature reading (without parasite power) - return result; + oneWire->skip(); // skip ROM + oneWire->write(0x44,parasite); // request new temperature reading (without parasite power) + return (float)result + ((data[0]&0x0008) ? 0.5f : 0.0f); } void requestTemperatures() { @@ -86,7 +79,6 @@ class UsermodTemperature : public Usermod { temperature = readDallas(); lastMeasurement = millis(); waitingForConversion = false; - readTemperatureComplete = true; DEBUG_PRINTF("Read temperature %2.1f.\n", temperature); } @@ -118,23 +110,23 @@ class UsermodTemperature : public Usermod { // pin retrieved from cfg.json (readFromConfig()) prior to running setup() if (!pinManager.allocatePin(temperaturePin,false)) { temperaturePin = -1; // allocation failed - disabled = true; + enabled = false; DEBUG_PRINTLN(F("Temperature pin allocation failed.")); } else { - if (!disabled) { + if (enabled) { // config says we are enabled oneWire = new OneWire(temperaturePin); if (!oneWire->reset()) - disabled = true; // resetting 1-Wire bus yielded an error + enabled = false; // resetting 1-Wire bus yielded an error else - while ((disabled=!findSensor()) && retries--) delay(25); // try to find sensor + while ((enabled=findSensor()) && retries--) delay(25); // try to find sensor } } initDone = true; } void loop() { - if (disabled || strip.isUpdating()) return; + if (!enabled || strip.isUpdating()) return; unsigned long now = millis(); @@ -189,7 +181,7 @@ class UsermodTemperature : public Usermod { */ void addToJsonInfo(JsonObject& root) { // dont add temperature to info if we are disabled - if (disabled) return; + if (!enabled) return; JsonObject user = root["u"]; if (user.isNull()) user = root.createNestedObject("u"); @@ -197,14 +189,6 @@ class UsermodTemperature : public Usermod { JsonArray temp = user.createNestedArray(FPSTR(_name)); //temp.add(F("Loaded.")); - if (!readTemperatureComplete) { - // if we haven't read the sensor yet, let the user know - // that we are still waiting for the first measurement - temp.add((USERMOD_DALLASTEMPERATURE_FIRST_MEASUREMENT_AT - millis()) / 1000); - temp.add(F(" sec until read")); - return; - } - if (temperature <= -100) { temp.add(0); temp.add(F(" Sensor Error!")); @@ -239,51 +223,44 @@ class UsermodTemperature : public Usermod { void addToConfig(JsonObject &root) { // we add JSON object: {"Temperature": {"pin": 0, "degC": true}} JsonObject top = root.createNestedObject(FPSTR(_name)); // usermodname - top[FPSTR(_enabled)] = !disabled; + top[FPSTR(_enabled)] = enabled; top["pin"] = temperaturePin; // usermodparam top["degC"] = degC; // usermodparam top[FPSTR(_readInterval)] = readingInterval / 1000; + top[FPSTR(_parasite)] = parasite; DEBUG_PRINTLN(F("Temperature config saved.")); } /** * readFromConfig() is called before setup() to populate properties from values stored in cfg.json + * + * The function should return true if configuration was successfully loaded or false if there was no configuration. */ bool readFromConfig(JsonObject &root) { // we look for JSON object: {"Temperature": {"pin": 0, "degC": true}} - JsonObject top = root[FPSTR(_name)]; int8_t newTemperaturePin = temperaturePin; - bool configComplete = true; - - if (!top.isNull() && top["pin"] != nullptr) { - if (top[FPSTR(_enabled)].is()) { - disabled = !top[FPSTR(_enabled)].as(); - } else { - String str = top[FPSTR(_enabled)]; // checkbox -> off or on - disabled = (bool)(str=="off"); // off is guaranteed to be present - } - newTemperaturePin = min(39,max(-1,top["pin"].as())); - if (top["degC"].is()) { - // reading from cfg.json - degC = top["degC"].as(); - } else { - // new configuration from set.cpp - String str = top["degC"]; // checkbox -> off or on - degC = (bool)(str!="off"); // off is guaranteed to be present - } - readingInterval = min(120,max(10,top[FPSTR(_readInterval)].as())) * 1000; // convert to ms - DEBUG_PRINTLN(F("Temperature config (re)loaded.")); - } else { - DEBUG_PRINTLN(F("No config found. (Using defaults.)")); - configComplete = false; + JsonObject top = root[FPSTR(_name)]; + if (top.isNull()) { + DEBUG_PRINT(FPSTR(_name)); + DEBUG_PRINTLN(F(": No config found. (Using defaults.)")); + return false; } + enabled = top[FPSTR(_enabled)] | enabled; + newTemperaturePin = top["pin"] | newTemperaturePin; + degC = top["degC"] | degC; + readingInterval = top[FPSTR(_readInterval)] | readingInterval/1000; + readingInterval = min(120,max(10,(int)readingInterval)) * 1000; // convert to ms + parasite = top[FPSTR(_parasite)] | parasite; + + DEBUG_PRINT(FPSTR(_name)); if (!initDone) { // first run: reading from cfg.json temperaturePin = newTemperaturePin; + DEBUG_PRINTLN(F(" config loaded.")); } else { - // changing parameters from settings page + // changing paramters from settings page if (newTemperaturePin != temperaturePin) { // deallocate pin and release memory delete oneWire; @@ -292,8 +269,10 @@ class UsermodTemperature : public Usermod { // initialise setup(); } + DEBUG_PRINTLN(F(" config (re)loaded.")); } - return configComplete; + // use "return !top["newestParameter"].isNull();" when updating Usermod with new features + return !top[FPSTR(_parasite)].isNull(); } uint16_t getId() @@ -305,4 +284,5 @@ class UsermodTemperature : public Usermod { // strings to reduce flash memory usage (used more than twice) const char UsermodTemperature::_name[] PROGMEM = "Temperature"; const char UsermodTemperature::_enabled[] PROGMEM = "enabled"; -const char UsermodTemperature::_readInterval[] PROGMEM = "read-interval-s"; \ No newline at end of file +const char UsermodTemperature::_readInterval[] PROGMEM = "read-interval-s"; +const char UsermodTemperature::_parasite[] PROGMEM = "parasite-pwr"; diff --git a/usermods/multi_relay/usermod_multi_relay.h b/usermods/multi_relay/usermod_multi_relay.h index 36e35ec4..22a499b0 100644 --- a/usermods/multi_relay/usermod_multi_relay.h +++ b/usermods/multi_relay/usermod_multi_relay.h @@ -35,7 +35,7 @@ class MultiRelay : public Usermod { // switch timer start time uint32_t _switchTimerStart = 0; // old brightness - uint8_t _oldBrightness = 0; + bool _oldBrightness = 0; // usermod enabled bool enabled = false; // needs to be configured (no default config) @@ -265,7 +265,7 @@ class MultiRelay : public Usermod { _relay[i].active = false; } } - _oldBrightness = bri; + _oldBrightness = (bool)bri; initDone = true; } @@ -288,8 +288,8 @@ class MultiRelay : public Usermod { lastUpdate = millis(); //set relay when LEDs turn on - if (_oldBrightness != bri) { - _oldBrightness = bri; + if (_oldBrightness != (bool)bri) { + _oldBrightness = (bool)bri; _switchTimerStart = millis(); for (uint8_t i=0; i=0) _relay[i].active = true; @@ -347,55 +347,35 @@ class MultiRelay : public Usermod { /** * restore the changeable values * readFromConfig() is called before setup() to populate properties from values stored in cfg.json + * + * The function should return true if configuration was successfully loaded or false if there was no configuration. */ bool readFromConfig(JsonObject &root) { int8_t oldPin[MULTI_RELAY_MAX_RELAYS]; JsonObject top = root[FPSTR(_name)]; - if (top.isNull()) return false; - - if (top[FPSTR(_enabled)] != nullptr) { - if (top[FPSTR(_enabled)].is()) { - enabled = top[FPSTR(_enabled)].as(); // reading from cfg.json - } else { - // change from settings page - String str = top[FPSTR(_enabled)]; // checkbox -> off or on - enabled = (bool)(str!="off"); // off is guaranteed to be present - } + if (top.isNull()) { + DEBUG_PRINT(FPSTR(_name)); + DEBUG_PRINTLN(F(": No config found. (Using defaults.)")); + return false; } + enabled = top[FPSTR(_enabled)] | enabled; + for (uint8_t i=0; i())); - - if (top[parName+FPSTR(_activeHigh)] != nullptr) { - if (top[parName+FPSTR(_activeHigh)].is()) { - _relay[i].mode = top[parName+FPSTR(_activeHigh)].as(); // reading from cfg.json - } else { - // change from settings page - String str = top[parName+FPSTR(_activeHigh)]; // checkbox -> off or on - _relay[i].mode = (bool)(str!="off"); // off is guaranteed to be present - } - } - - if (top[parName+FPSTR(_external)] != nullptr) { - if (top[parName+FPSTR(_external)].is()) { - _relay[i].external = top[parName+FPSTR(_external)].as(); // reading from cfg.json - } else { - // change from settings page - String str = top[parName+FPSTR(_external)]; // checkbox -> off or on - _relay[i].external = (bool)(str!="off"); // off is guaranteed to be present - } - } - - _relay[i].delay = min(600,max(0,abs(top[parName+FPSTR(_delay_str)].as()))); + oldPin[i] = _relay[i].pin; + _relay[i].pin = top[parName+"pin"] | _relay[i].pin; + _relay[i].mode = top[parName+FPSTR(_activeHigh)] | _relay[i].mode; + _relay[i].external = top[parName+FPSTR(_external)] | _relay[i].external; + _relay[i].delay = top[parName+FPSTR(_delay_str)] | _relay[i].delay; + _relay[i].delay = min(600,max(0,abs((int)_relay[i].delay))); // bounds checking max 10min } + DEBUG_PRINT(FPSTR(_name)); if (!initDone) { // reading config prior to setup() - DEBUG_PRINTLN(F("MultiRelay config loaded.")); + DEBUG_PRINTLN(F(" config loaded.")); } else { // deallocate all pins 1st for (uint8_t i=0; i()) { - // reading from cfg.json - enabled = top[FPSTR(_autoSaveEnabled)].as(); - } else { - // reading from POST message - String str = top[FPSTR(_autoSaveEnabled)]; // checkbox -> off or on - enabled = (bool)(str!="off"); // off is guaranteed to be present - } - autoSaveAfterSec = min(3600,max(10,top[FPSTR(_autoSaveAfterSec)].as())); - autoSavePreset = min(250,max(100,top[FPSTR(_autoSavePreset)].as())); - if (top[FPSTR(_autoSaveApplyOnBoot)].is()) { - // reading from cfg.json - applyAutoSaveOnBoot = top[FPSTR(_autoSaveApplyOnBoot)].as(); - } else { - // reading from POST message - String str = top[FPSTR(_autoSaveApplyOnBoot)]; // checkbox -> off or on - applyAutoSaveOnBoot = (bool)(str!="off"); // off is guaranteed to be present - } - DEBUG_PRINTLN(F("Autosave config (re)loaded.")); + + enabled = top[FPSTR(_autoSaveEnabled)] | enabled; + autoSaveAfterSec = top[FPSTR(_autoSaveAfterSec)] | autoSaveAfterSec; + autoSaveAfterSec = (uint16_t) min(3600,max(10,(int)autoSaveAfterSec)); // bounds checking + autoSavePreset = top[FPSTR(_autoSavePreset)] | autoSavePreset; + autoSavePreset = (uint8_t) min(250,max(100,(int)autoSavePreset)); // bounds checking + applyAutoSaveOnBoot = top[FPSTR(_autoSaveApplyOnBoot)] | applyAutoSaveOnBoot; + DEBUG_PRINT(FPSTR(_name)); + DEBUG_PRINTLN(F(" config (re)loaded.")); + + // use "return !top["newestParameter"].isNull();" when updating Usermod with new features return true; } diff --git a/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h b/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h index 9510d9be..1a8384d9 100644 --- a/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h +++ b/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h @@ -54,18 +54,21 @@ #define LINE_BUFFER_SIZE 16+1 typedef enum { - FLD_LINE_4_BRIGHTNESS = 0, - FLD_LINE_4_EFFECT_SPEED, - FLD_LINE_4_EFFECT_INTENSITY, - FLD_LINE_4_MODE, - FLD_LINE_4_PALETTE + FLD_LINE_BRIGHTNESS = 0, + FLD_LINE_EFFECT_SPEED, + FLD_LINE_EFFECT_INTENSITY, + FLD_LINE_MODE, + FLD_LINE_PALETTE, + FLD_LINE_TIME } Line4Type; typedef enum { NONE = 0, SSD1306, // U8X8_SSD1306_128X32_UNIVISION_HW_I2C SH1106, // U8X8_SH1106_128X64_WINSTAR_HW_I2C - SSD1306_64 // U8X8_SSD1306_128X64_NONAME_HW_I2C + SSD1306_64, // U8X8_SSD1306_128X64_NONAME_HW_I2C + SSD1305, // U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C + SSD1305_64 // U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C } DisplayType; class FourLineDisplayUsermod : public Usermod { @@ -87,9 +90,6 @@ class FourLineDisplayUsermod : public Usermod { bool sleepMode = true; // allow screen sleep? bool clockMode = false; // display clock - // needRedraw marks if redraw is required to prevent often redrawing. - bool needRedraw = true; - // Next variables hold the previous known values to determine if redraw is // required. String knownSsid = ""; @@ -106,7 +106,7 @@ class FourLineDisplayUsermod : public Usermod { unsigned long lastUpdate = 0; unsigned long lastRedraw = 0; unsigned long overlayUntil = 0; - Line4Type lineFourType = FLD_LINE_4_BRIGHTNESS; + Line4Type lineType = FLD_LINE_BRIGHTNESS; // Set to 2 or 3 to mark lines 2 or 3. Other values ignored. byte markLineNum = 0; @@ -130,7 +130,7 @@ class FourLineDisplayUsermod : public Usermod { // gets called once at boot. Do all initialization that doesn't depend on // network here void setup() { - if (type==NONE) return; + if (type == NONE) return; if (!pinManager.allocatePin(sclPin)) { sclPin = -1; type = NONE; return;} if (!pinManager.allocatePin(sdaPin)) { pinManager.deallocatePin(sclPin); sclPin = sdaPin = -1; type = NONE; return; } switch (type) { @@ -141,6 +141,7 @@ class FourLineDisplayUsermod : public Usermod { else #endif u8x8 = (U8X8 *) new U8X8_SSD1306_128X32_UNIVISION_HW_I2C(U8X8_PIN_NONE, sclPin, sdaPin); // Pins are Reset, SCL, SDA + lineHeight = 1; break; case SH1106: #ifdef ESP8266 @@ -149,6 +150,7 @@ class FourLineDisplayUsermod : public Usermod { else #endif u8x8 = (U8X8 *) new U8X8_SH1106_128X64_WINSTAR_HW_I2C(U8X8_PIN_NONE, sclPin, sdaPin); // Pins are Reset, SCL, SDA + lineHeight = 2; break; case SSD1306_64: #ifdef ESP8266 @@ -157,6 +159,25 @@ class FourLineDisplayUsermod : public Usermod { else #endif u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_HW_I2C(U8X8_PIN_NONE, sclPin, sdaPin); // Pins are Reset, SCL, SDA + lineHeight = 2; + break; + case SSD1305: + #ifdef ESP8266 + if (!(sclPin==5 && sdaPin==4)) + u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_NONAME_SW_I2C(sclPin, sdaPin); // SCL, SDA, reset + else + #endif + u8x8 = (U8X8 *) new U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, sclPin, sdaPin); // Pins are Reset, SCL, SDA + lineHeight = 1; + break; + case SSD1305_64: + #ifdef ESP8266 + if (!(sclPin==5 && sdaPin==4)) + u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_SW_I2C(sclPin, sdaPin); // SCL, SDA, reset + else + #endif + u8x8 = (U8X8 *) new U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C(U8X8_PIN_NONE, sclPin, sdaPin); // Pins are Reset, SCL, SDA + lineHeight = 2; break; default: u8x8 = nullptr; @@ -179,9 +200,7 @@ class FourLineDisplayUsermod : public Usermod { * Da loop. */ void loop() { - if (millis() - lastUpdate < (clockMode?1000:refreshRate)) { - return; - } + if (millis() - lastUpdate < (clockMode?1000:refreshRate) || strip.isUpdating()) return; lastUpdate = millis(); redraw(false); @@ -233,9 +252,12 @@ class FourLineDisplayUsermod : public Usermod { * or if forceRedraw). */ void redraw(bool forceRedraw) { + static bool showName = false; + unsigned long now = millis(); + if (type==NONE) return; if (overlayUntil > 0) { - if (millis() >= overlayUntil) { + if (now >= overlayUntil) { // Time to display the overlay has elapsed. overlayUntil = 0; forceRedraw = true; @@ -247,74 +269,63 @@ class FourLineDisplayUsermod : public Usermod { } // Check if values which are shown on display changed from the last time. - if (forceRedraw) { - needRedraw = true; - } else if (((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid) { - needRedraw = true; - } else if (knownIp != (apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP())) { - needRedraw = true; - } else if (knownBrightness != bri) { - needRedraw = true; - } else if (knownEffectSpeed != effectSpeed) { - needRedraw = true; - } else if (knownEffectIntensity != effectIntensity) { - needRedraw = true; - } else if (knownMode != strip.getMode()) { - needRedraw = true; - } else if (knownPalette != strip.getSegment(0).palette) { - needRedraw = true; - } - - if (!needRedraw) { + if (forceRedraw || + (((apActive) ? String(apSSID) : WiFi.SSID()) != knownSsid) || + (knownIp != (apActive ? IPAddress(4, 3, 2, 1) : Network.localIP())) || + (knownBrightness != bri) || + (knownEffectSpeed != effectSpeed) || + (knownEffectIntensity != effectIntensity) || + (knownMode != strip.getMode()) || + (knownPalette != strip.getSegment(0).palette)) { + knownHour = 99; // force time update + clear(); + } else if (sleepMode && !displayTurnedOff && ((now - lastRedraw)/1000)%5 == 0) { + // change line every 5s + showName = !showName; + switch (lineType) { + case FLD_LINE_BRIGHTNESS: + lineType = FLD_LINE_EFFECT_SPEED; + break; + case FLD_LINE_MODE: + lineType = FLD_LINE_BRIGHTNESS; + break; + case FLD_LINE_PALETTE: + lineType = clockMode ? FLD_LINE_MODE : FLD_LINE_BRIGHTNESS; + break; + case FLD_LINE_EFFECT_SPEED: + lineType = FLD_LINE_EFFECT_INTENSITY; + break; + case FLD_LINE_EFFECT_INTENSITY: + lineType = FLD_LINE_PALETTE; + break; + default: + lineType = FLD_LINE_MODE; + break; + } + knownHour = 99; // force time update + } else { // Nothing to change. // Turn off display after 3 minutes with no change. if(sleepMode && !displayTurnedOff && (millis() - lastRedraw > screenTimeout)) { // We will still check if there is a change in redraw() // and turn it back on if it changed. - knownHour = 99; // force screen clear + clear(); // force screen clear sleepOrClock(true); } else if (displayTurnedOff && clockMode) { showTime(); - } else if ((millis() - lastRedraw)/1000%3 == 0) { - // change 4th line every 3s - switch (lineFourType) { - case FLD_LINE_4_BRIGHTNESS: - setLineFourType(FLD_LINE_4_EFFECT_SPEED); - break; - case FLD_LINE_4_MODE: - setLineFourType(FLD_LINE_4_BRIGHTNESS); - break; - case FLD_LINE_4_PALETTE: - setLineFourType(clockMode ? FLD_LINE_4_MODE : FLD_LINE_4_BRIGHTNESS); - break; - case FLD_LINE_4_EFFECT_SPEED: - setLineFourType(FLD_LINE_4_EFFECT_INTENSITY); - break; - case FLD_LINE_4_EFFECT_INTENSITY: - setLineFourType(FLD_LINE_4_PALETTE); - break; - default: - break; - } - drawLineFour(); } return; - } else { - knownHour = 99; // force time display - clear(); } - needRedraw = false; - lastRedraw = millis(); + // do not update lastRedraw marker if just switching row contenet + if (((now - lastRedraw)/1000)%5 != 0) lastRedraw = now; - if (displayTurnedOff) { - // Turn the display back on - sleepOrClock(false); - } + // Turn the display back on + if (displayTurnedOff) sleepOrClock(false); // Update last known values. knownSsid = apActive ? WiFi.softAPSSID() : WiFi.SSID(); - knownIp = apActive ? IPAddress(4, 3, 2, 1) : WiFi.localIP(); + knownIp = apActive ? IPAddress(4, 3, 2, 1) : Network.localIP(); knownBrightness = bri; knownMode = strip.getMode(); knownPalette = strip.getSegment(0).palette; @@ -324,7 +335,7 @@ class FourLineDisplayUsermod : public Usermod { // Do the actual drawing // First row with Wifi name - drawGlyph(0, 0, 80, u8x8_font_open_iconic_embedded_1x1); // wifi icon + drawGlyph(0, 0, 80, u8x8_font_open_iconic_embedded_1x1); // home icon String ssidString = knownSsid.substring(0, getCols() > 1 ? getCols() - 2 : 0); drawString(1, 0, ssidString.c_str()); // Print `~` char to indicate that SSID is longer, than our display @@ -333,46 +344,54 @@ class FourLineDisplayUsermod : public Usermod { } // Second row with IP or Psssword - drawGlyph(0, lineHeight, 68, u8x8_font_open_iconic_embedded_1x1); // home icon + drawGlyph(0, lineHeight, 68, u8x8_font_open_iconic_embedded_1x1); // wifi icon // Print password in AP mode and if led is OFF. if (apActive && bri == 0) { drawString(1, lineHeight, apPass); } else { - drawString(1, lineHeight, (knownIp.toString()).c_str()); + // alternate IP address and server name + String secondLine = knownIp.toString(); + if (showName && strcmp(serverDescription, "WLED") != 0) { + secondLine = serverDescription; + } + for (uint8_t i=secondLine.length(); i 0 ? 3 : 0), u8x8_font_open_iconic_weather_2x2); // sun/moon icon //if (markLineNum>1) drawGlyph(2, markLineNum*lineHeight, 66, u8x8_font_open_iconic_arrow_1x1); // arrow icon } - void drawLineFour() { + void drawLine(uint8_t line, Line4Type lineType) { char lineBuffer[LINE_BUFFER_SIZE]; - switch(lineFourType) { - case FLD_LINE_4_BRIGHTNESS: + switch(lineType) { + case FLD_LINE_BRIGHTNESS: sprintf_P(lineBuffer, PSTR("Brightness %3d"), bri); - drawString(2, 3*lineHeight, lineBuffer); + drawString(2, line*lineHeight, lineBuffer); break; - case FLD_LINE_4_EFFECT_SPEED: + case FLD_LINE_EFFECT_SPEED: sprintf_P(lineBuffer, PSTR("FX Speed %3d"), effectSpeed); - drawString(2, 3*lineHeight, lineBuffer); + drawString(2, line*lineHeight, lineBuffer); break; - case FLD_LINE_4_EFFECT_INTENSITY: + case FLD_LINE_EFFECT_INTENSITY: sprintf_P(lineBuffer, PSTR("FX Intens. %3d"), effectIntensity); - drawString(2, 3*lineHeight, lineBuffer); + drawString(2, line*lineHeight, lineBuffer); break; - case FLD_LINE_4_MODE: - showCurrentEffectOrPalette(knownMode, JSON_mode_names, 3); + case FLD_LINE_MODE: + showCurrentEffectOrPalette(knownMode, JSON_mode_names, line); + break; + case FLD_LINE_PALETTE: + showCurrentEffectOrPalette(knownPalette, JSON_palette_names, line); + break; + case FLD_LINE_TIME: + showTime(false); break; - case FLD_LINE_4_PALETTE: default: - showCurrentEffectOrPalette(knownPalette, JSON_palette_names, 3); + // unknown type, do nothing break; } } @@ -447,23 +466,6 @@ class FourLineDisplayUsermod : public Usermod { overlayUntil = millis() + showHowLong; } - /** - * Specify what data should be defined on line 4 - * (the last line). - */ - void setLineFourType(Line4Type newLineFourType) { - if (newLineFourType == FLD_LINE_4_BRIGHTNESS || - newLineFourType == FLD_LINE_4_EFFECT_SPEED || - newLineFourType == FLD_LINE_4_EFFECT_INTENSITY || - newLineFourType == FLD_LINE_4_MODE || - newLineFourType == FLD_LINE_4_PALETTE) { - lineFourType = newLineFourType; - } else { - // Unknown value - lineFourType = FLD_LINE_4_BRIGHTNESS; - } - } - /** * Line 3 or 4 (last two lines) can be marked with an * arrow in the first column. Pass 2 or 3 to this to @@ -487,8 +489,7 @@ class FourLineDisplayUsermod : public Usermod { if (clockMode) showTime(); else setPowerSave(1); displayTurnedOff = true; - } - else { + } else { setPowerSave(0); displayTurnedOff = false; } @@ -504,13 +505,13 @@ class FourLineDisplayUsermod : public Usermod { updateLocalTime(); byte minuteCurrent = minute(localTime); - byte hourCurrent = hour(localTime); + byte hourCurrent = hour(localTime); byte secondCurrent = second(localTime); if (knownMinute == minuteCurrent && knownHour == hourCurrent) { // Time hasn't changed. if (!fullScreen) return; } else { - if (fullScreen) clear(); + //if (fullScreen) clear(); } knownMinute = minuteCurrent; knownHour = hourCurrent; @@ -520,7 +521,7 @@ class FourLineDisplayUsermod : public Usermod { if (fullScreen) draw2x2String(DATE_INDENT, lineHeight==1 ? 0 : lineHeight, lineBuffer); // adjust for 8 line displays else - drawString(2, lineHeight*2, lineBuffer); + drawString(2, lineHeight*3, lineBuffer); byte showHour = hourCurrent; boolean isAM = false; @@ -544,11 +545,12 @@ class FourLineDisplayUsermod : public Usermod { if (fullScreen) { draw2x2String(TIME_INDENT+2, lineHeight*2, lineBuffer); sprintf_P(lineBuffer, PSTR("%02d"), secondCurrent); - if (!useAMPM) drawString(12, lineHeight*2+1, lineBuffer, true); // even with double sized rows print seconds in 1 line + if (useAMPM) drawString(12+(fullScreen?0:2), lineHeight*2, (isAM ? "AM" : "PM"), true); + else drawString(12, lineHeight*2+1, lineBuffer, true); // even with double sized rows print seconds in 1 line } else { - drawString(9+(useAMPM?0:2), lineHeight*2, lineBuffer); + drawString(9+(useAMPM?0:2), lineHeight*3, lineBuffer); + if (useAMPM) drawString(12+(fullScreen?0:2), lineHeight*3, (isAM ? "AM" : "PM"), true); } - if (useAMPM) drawString(12+(fullScreen?0:2), lineHeight*2, (isAM ? "AM" : "PM"), true); } /* @@ -621,55 +623,34 @@ class FourLineDisplayUsermod : public Usermod { int8_t newScl = sclPin; int8_t newSda = sdaPin; - bool configComplete = true; - JsonObject top = root[FPSTR(_name)]; - if (!top.isNull() && top["pin"] != nullptr) { - newScl = top["pin"][0]; - newSda = top["pin"][1]; - newType = top["type"]; - if (top[FPSTR(_flip)].is()) { - flip = top[FPSTR(_flip)].as(); - } else { - String str = top[FPSTR(_flip)]; // checkbox -> off or on - flip = (bool)(str!="off"); // off is guaranteed to be present - needRedraw |= true; - } - contrast = top[FPSTR(_contrast)].as(); - refreshRate = top[FPSTR(_refreshRate)].as() * 1000; - screenTimeout = top[FPSTR(_screenTimeOut)].as() * 1000; - if (top[FPSTR(_sleepMode)].is()) { - sleepMode = top[FPSTR(_sleepMode)].as(); - } else { - String str = top[FPSTR(_sleepMode)]; // checkbox -> off or on - sleepMode = (bool)(str!="off"); // off is guaranteed to be present - needRedraw |= true; - } - if (top[FPSTR(_clockMode)].is()) { - clockMode = top[FPSTR(_clockMode)].as(); - } else { - String str = top[FPSTR(_clockMode)]; // checkbox -> off or on - clockMode = (bool)(str!="off"); // off is guaranteed to be present - needRedraw |= true; - } - DEBUG_PRINTLN(F("4 Line Display config (re)loaded.")); - } else { - DEBUG_PRINTLN(F("No config found. (Using defaults.)")); - configComplete = false; + if (top.isNull()) { + DEBUG_PRINT(FPSTR(_name)); + DEBUG_PRINTLN(F(": No config found. (Using defaults.)")); + return false; } + newScl = top["pin"][0] | newScl; + newSda = top["pin"][1] | newSda; + newType = top["type"] | newType; + flip = top[FPSTR(_flip)] | flip; + contrast = top[FPSTR(_contrast)] | contrast; + refreshRate = (top[FPSTR(_refreshRate)] | refreshRate/1000) * 1000; + screenTimeout = (top[FPSTR(_screenTimeOut)] | screenTimeout/1000) * 1000; + sleepMode = top[FPSTR(_sleepMode)] | sleepMode; + clockMode = top[FPSTR(_clockMode)] | clockMode; + + DEBUG_PRINT(FPSTR(_name)); if (!initDone) { // first run: reading from cfg.json sclPin = newScl; sdaPin = newSda; type = newType; - lineHeight = type==SSD1306 ? 1 : 2; + DEBUG_PRINTLN(F(" config loaded.")); } else { // changing paramters from settings page if (sclPin!=newScl || sdaPin!=newSda || type!=newType) { - if (type==SSD1306) delete (static_cast(u8x8)); - if (type==SH1106) delete (static_cast(u8x8)); - if (type==SSD1306_64) delete (static_cast(u8x8)); + if (type != NONE) delete (static_cast(u8x8)); pinManager.deallocatePin(sclPin); pinManager.deallocatePin(sdaPin); sclPin = newScl; @@ -677,18 +658,17 @@ class FourLineDisplayUsermod : public Usermod { if (newScl<0 || newSda<0) { type = NONE; return true; - } else - type = newType; - lineHeight = type==SSD1306 ? 1 : 2; + } else type = newType; setup(); - needRedraw |= true; + needsRedraw |= true; } setContrast(contrast); setFlipMode(flip); if (needsRedraw && !wakeDisplay()) redraw(true); + DEBUG_PRINTLN(F(" config (re)loaded.")); } - - return configComplete; + // use "return !top["newestParameter"].isNull();" when updating Usermod with new features + return true; } /* diff --git a/wled00/data/settings_um.htm b/wled00/data/settings_um.htm index 58e22e0f..ca5b1d68 100644 --- a/wled00/data/settings_um.htm +++ b/wled00/data/settings_um.htm @@ -29,7 +29,10 @@ if (numM > 0 || locip) ldS(); else gId("um").innerHTML = "No Usermods installed."; } - function check(o,k) { + // https://stackoverflow.com/questions/3885817/how-do-i-check-that-a-number-is-float-or-integer + function isF(n) { return n === +n && n !== (n|0); } + function isI(n) { return n === +n && n === (n|0); } + function check(o,k) { // input object, pin owner key var n = o.name.replace("[]","").substr(-3); if (o.type=="number" && n.substr(0,3)=="pin") { for (var i=0; i`; - urows += `${f}:
`; + if (t=="checkbox") urows += ``; + else if (!a) urows += ``; + urows += `
`; } } function ldS() { diff --git a/wled00/data/style.css b/wled00/data/style.css index 83404355..89366b13 100644 --- a/wled00/data/style.css +++ b/wled00/data/style.css @@ -3,7 +3,7 @@ body { text-align: center; background: #222; color: #fff; - line-height: 200%; + line-height: 200%%; /* %% because of AsyncWebServer */ margin: 0; } hr { @@ -31,8 +31,38 @@ input { font-family: Verdana, sans-serif; border: 0.5ch solid #333; } +input[type="text"] { + font-size: medium; +} input[type="number"] { width: 4em; + font-size: medium; + margin: 2px; +} +input[type="number"].xxl { + width: 100px; +} +input[type="number"].big { + width: 85px; +} +input[type="number"].med { + width: 55px; +} +input[type="number"].small { + width: 40px; +} +select { + margin: 2px; + font-size: medium; +} +input[type="checkbox"] { + /* Double-sized Checkboxes */ + -ms-transform: scale(2); /* IE */ + -moz-transform: scale(2); /* FF */ + -webkit-transform: scale(2); /* Safari and Chrome */ + -o-transform: scale(2); /* Opera */ + transform: scale(2); + margin-right: 10px; } select { background: #333; diff --git a/wled00/set.cpp b/wled00/set.cpp index e2e8e48e..558cb5d2 100644 --- a/wled00/set.cpp +++ b/wled00/set.cpp @@ -431,8 +431,8 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) String value = request->arg(i); // POST request parameters are combined as _ - uint8_t umNameEnd = name.indexOf("_"); - if (!umNameEnd) break; // parameter does not contain "_" -> wrong + int umNameEnd = name.indexOf(":"); + if (umNameEnd<1) break; // parameter does not contain ":" or on 1st place -> wrong JsonObject mod = um[name.substring(0,umNameEnd)]; // get a usermod JSON object if (mod.isNull()) { @@ -442,30 +442,57 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage) DEBUG_PRINT(":"); name = name.substring(umNameEnd+1); // remove mod name from string + // if the resulting name still contains ":" this means nested object + JsonObject subObj; + int umSubObj = name.indexOf(":"); + DEBUG_PRINTF("(%d):",umSubObj); + if (umSubObj>0) { + subObj = mod[name.substring(0,umSubObj)]; + if (subObj.isNull()) + subObj = mod.createNestedObject(name.substring(0,umSubObj)); + name = name.substring(umSubObj+1); // remove nested object name from string + } else { + subObj = mod; + } + DEBUG_PRINT(name); + // check if parameters represent array if (name.endsWith("[]")) { name.replace("[]",""); - if (!mod[name].is()) { - JsonArray ar = mod.createNestedArray(name); - ar.add(value); + if (!subObj[name].is()) { + JsonArray ar = subObj.createNestedArray(name); + ar.add(value.toInt()); j=0; } else { - mod[name].add(value); + subObj[name].add(value.toInt()); j++; } - DEBUG_PRINT(name); DEBUG_PRINT("["); DEBUG_PRINT(j); DEBUG_PRINT("] = "); DEBUG_PRINTLN(value); } else { - mod.remove(name); // checkboxes get two fields (first is always "off", existence of second depends on checkmark and may be "on") - mod[name] = value; - DEBUG_PRINT(name); + // we are using a hidden field with the same name as our parameter (!before the actual parameter!) + // to describe the type of parameter (text,float,int), for boolean patameters the first field contains "off" + // so checkboxes have one or two fields (first is always "false", existence of second depends on checkmark and may be "true") + if (subObj[name].isNull()) { + // the first occurence of the field describes the parameter type (used in next loop) + if (value == "false") subObj[name] = false; // checkboxes may have only one field + else subObj[name] = value; + } else { + String type = subObj[name].as(); // get previously stored value as a type + if (subObj[name].is()) subObj[name] = true; // checkbox/boolean + else if (type == "float") subObj[name] = value.toDouble(); + else if (type == "int") subObj[name] = value.toInt(); + else subObj[name] = value; // text fields + } DEBUG_PRINT(" = "); DEBUG_PRINTLN(value); } } + #ifdef WLED_DEBUG + serializeJson(um,Serial); + #endif usermods.readFromConfig(um); // force change of usermod parameters } @@ -643,7 +670,6 @@ bool handleSet(AsyncWebServerRequest *request, const String& req, bool apply) nightlightActive = false; //always disable nightlight when toggling } } - #endif //set hue From 04b4ef6d85dac4c820281471ce7010a7ccf6dc96 Mon Sep 17 00:00:00 2001 From: Aircoookie Date: Sun, 27 Jun 2021 22:19:03 +0200 Subject: [PATCH 3/8] Regenerate html_settings.h --- usermods/Animated_Staircase/Animated_Staircase.h | 2 +- usermods/Temperature/usermod_temperature.h | 2 +- .../usermod_v2_four_line_display.h | 2 +- wled00/data/style.css | 15 +-------------- wled00/html_settings.h | 4 ++-- 5 files changed, 6 insertions(+), 19 deletions(-) diff --git a/usermods/Animated_Staircase/Animated_Staircase.h b/usermods/Animated_Staircase/Animated_Staircase.h index 029d9de2..b94760e9 100644 --- a/usermods/Animated_Staircase/Animated_Staircase.h +++ b/usermods/Animated_Staircase/Animated_Staircase.h @@ -470,7 +470,7 @@ class Animated_Staircase : public Usermod { // first run: reading from cfg.json DEBUG_PRINTLN(F(" config loaded.")); } else { - // changing paramters from settings page + // changing parameters from settings page DEBUG_PRINTLN(F(" config (re)loaded.")); bool changed = false; if ((oldUseUSSensorTop != useUSSensorTop) || diff --git a/usermods/Temperature/usermod_temperature.h b/usermods/Temperature/usermod_temperature.h index 4efb2a44..94560ed0 100644 --- a/usermods/Temperature/usermod_temperature.h +++ b/usermods/Temperature/usermod_temperature.h @@ -260,7 +260,7 @@ class UsermodTemperature : public Usermod { temperaturePin = newTemperaturePin; DEBUG_PRINTLN(F(" config loaded.")); } else { - // changing paramters from settings page + // changing parameters from settings page if (newTemperaturePin != temperaturePin) { // deallocate pin and release memory delete oneWire; diff --git a/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h b/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h index 1a8384d9..0e21f085 100644 --- a/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h +++ b/usermods/usermod_v2_four_line_display/usermod_v2_four_line_display.h @@ -648,7 +648,7 @@ class FourLineDisplayUsermod : public Usermod { type = newType; DEBUG_PRINTLN(F(" config loaded.")); } else { - // changing paramters from settings page + // changing parameters from settings page if (sclPin!=newScl || sdaPin!=newSda || type!=newType) { if (type != NONE) delete (static_cast(u8x8)); pinManager.deallocatePin(sclPin); diff --git a/wled00/data/style.css b/wled00/data/style.css index 89366b13..484b3ded 100644 --- a/wled00/data/style.css +++ b/wled00/data/style.css @@ -31,12 +31,8 @@ input { font-family: Verdana, sans-serif; border: 0.5ch solid #333; } -input[type="text"] { - font-size: medium; -} input[type="number"] { width: 4em; - font-size: medium; margin: 2px; } input[type="number"].xxl { @@ -51,17 +47,8 @@ input[type="number"].med { input[type="number"].small { width: 40px; } -select { - margin: 2px; - font-size: medium; -} input[type="checkbox"] { - /* Double-sized Checkboxes */ - -ms-transform: scale(2); /* IE */ - -moz-transform: scale(2); /* FF */ - -webkit-transform: scale(2); /* Safari and Chrome */ - -o-transform: scale(2); /* Opera */ - transform: scale(2); + transform: scale(1.5); margin-right: 10px; } select { diff --git a/wled00/html_settings.h b/wled00/html_settings.h index f9bf3301..dc1db438 100644 --- a/wled00/html_settings.h +++ b/wled00/html_settings.h @@ -6,7 +6,7 @@ */ // Autogenerated from wled00/data/style.css, do not edit!! -const char PAGE_settingsCss[] PROGMEM = R"=====()====="; +const char PAGE_settingsCss[] PROGMEM = R"=====()====="; // Autogenerated from wled00/data/settings.htm, do not edit!! @@ -394,7 +394,7 @@ type="submit">Save & Reboot)====="; // Autogenerated from wled00/data/settings_um.htm, do not edit!! const char PAGE_settings_um[] PROGMEM = R"=====(Usermod Settings