diff --git a/usermods/JSON_IR_remote/readme.md b/usermods/JSON_IR_remote/readme.md
index ee18aa941..2cba06ed0 100644
--- a/usermods/JSON_IR_remote/readme.md
+++ b/usermods/JSON_IR_remote/readme.md
@@ -1,33 +1,33 @@
-# JSON IR remote
-
-## Purpose
-
-The JSON IR remote allows users to customize IR remote behavior without writing custom code and compiling.
-It also enables using any remote that is compatible with your IR receiver. Using the JSON IR remote, you can
-map buttons from any remote to any HTTP request API or JSON API command.
-
-## Usage
-
-* Upload the IR config file, named _ir.json_ to your board using the [ip address]/edit url. Pick from one of the included files or create your own.
-* On the config > LED settings page, set the correct IR pin.
-* On the config > Sync Interfaces page, select "JSON Remote" as the Infrared remote.
-
-## Modification
-
-* See if there is a json file with the same number of buttons as your remote. Many remotes will have the same internals and emit the same codes but have different labels.
-* In the ir.json file, each key will be the hex encoded IR code.
-* The "cmd" property will be the HTTP Request API or JSON API to execute when that button is pressed.
-* A limited number of c functions are supported (!incBrightness, !decBrightness, !presetFallback)
-* When using !presetFallback, include properties PL (preset to load), FX (effect to fall back to) and FP (palette to fall back to)
-* If the command is _repeatable_ and does not contain the "~" character, add a "rpt": true property.
-* Other properties are ignored, but having a label property may help when editing.
-
-
-Sample:
-{
- "0xFF629D": {"cmd": "T=2", "rpt": true, "label": "Toggle on/off"}, // HTTP command
- "0xFF9867": {"cmd": "A=~16", "label": "Inc brightness"}, // HTTP command with incrementing
- "0xFF38C7": {"cmd": {"bri": 10}, "label": "Dim to 10"}, // JSON command
- "0xFF22DD": {"cmd": "!presetFallback", "PL": 1, "FX": 16, "FP": 6,
- "label": "Preset 1 or fallback to Saw - Party"}, // c function
-}
+# JSON IR remote
+
+## Purpose
+
+The JSON IR remote allows users to customize IR remote behavior without writing custom code and compiling.
+It also enables using any remote that is compatible with your IR receiver. Using the JSON IR remote, you can
+map buttons from any remote to any HTTP request API or JSON API command.
+
+## Usage
+
+* Upload the IR config file, named _ir.json_ to your board using the [ip address]/edit url. Pick from one of the included files or create your own.
+* On the config > LED settings page, set the correct IR pin.
+* On the config > Sync Interfaces page, select "JSON Remote" as the Infrared remote.
+
+## Modification
+
+* See if there is a json file with the same number of buttons as your remote. Many remotes will have the same internals and emit the same codes but have different labels.
+* In the ir.json file, each key will be the hex encoded IR code.
+* The "cmd" property will be the HTTP Request API or JSON API to execute when that button is pressed.
+* A limited number of c functions are supported (!incBrightness, !decBrightness, !presetFallback)
+* When using !presetFallback, include properties PL (preset to load), FX (effect to fall back to) and FP (palette to fall back to)
+* If the command is _repeatable_ and does not contain the "~" character, add a "rpt": true property.
+* Other properties are ignored, but having a label property may help when editing.
+
+
+Sample:
+{
+ "0xFF629D": {"cmd": "T=2", "rpt": true, "label": "Toggle on/off"}, // HTTP command
+ "0xFF9867": {"cmd": "A=~16", "label": "Inc brightness"}, // HTTP command with incrementing
+ "0xFF38C7": {"cmd": {"bri": 10}, "label": "Dim to 10"}, // JSON command
+ "0xFF22DD": {"cmd": "!presetFallback", "PL": 1, "FX": 16, "FP": 6,
+ "label": "Preset 1 or fallback to Saw - Party"}, // c function
+}
diff --git a/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h b/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h
index 2978b4914..6682dde3d 100644
--- a/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h
+++ b/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h
@@ -1,409 +1,409 @@
-#pragma once
-
-#include "wled.h"
-
-#ifndef PIR_SENSOR_PIN
- // compatible with QuinLED-Dig-Uno
- #ifdef ARDUINO_ARCH_ESP32
- #define PIR_SENSOR_PIN 23 // Q4
- #else //ESP8266 boards
- #define PIR_SENSOR_PIN 13 // Q4 (D7 on D1 mini)
- #endif
-#endif
-
-/*
- * This usermod handles PIR sensor states.
- * The strip will be switched on and the off timer will be resetted when the sensor goes HIGH.
- * When the sensor state goes LOW, the off timer is started and when it expires, the strip is switched off.
- *
- *
- * Usermods allow you to add own functionality to WLED more easily
- * See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
- *
- * v2 usermods are class inheritance based and can (but don't have to) implement more functions, each of them is shown in this example.
- * Multiple v2 usermods can be added to one compilation easily.
- *
- * Creating a usermod:
- * This file serves as an example. If you want to create a usermod, it is recommended to use usermod_v2_empty.h from the usermods folder as a template.
- * Please remember to rename the class and file to a descriptive name.
- * You may also use multiple .h and .cpp files.
- *
- * Using a usermod:
- * 1. Copy the usermod into the sketch folder (same folder as wled00.ino)
- * 2. Register the usermod by adding #include "usermod_filename.h" in the top and registerUsermod(new MyUsermodClass()) in the bottom of usermods_list.cpp
- */
-
-class PIRsensorSwitch : public Usermod
-{
-public:
- /**
- * constructor
- */
- PIRsensorSwitch() {}
- /**
- * desctructor
- */
- ~PIRsensorSwitch() {}
-
- /**
- * Enable/Disable the PIR sensor
- */
- void EnablePIRsensor(bool en) { enabled = en; }
- /**
- * Get PIR sensor enabled/disabled state
- */
- bool PIRsensorEnabled() { return enabled; }
-
-private:
- // PIR sensor pin
- int8_t PIRsensorPin = PIR_SENSOR_PIN;
- // notification mode for colorUpdated()
- const byte NotifyUpdateMode = CALL_MODE_NO_NOTIFY; // CALL_MODE_DIRECT_CHANGE
- // delay before switch off after the sensor state goes LOW
- uint32_t m_switchOffDelay = 600000; // 10min
- // off timer start time
- uint32_t m_offTimerStart = 0;
- // current PIR sensor pin state
- byte sensorPinState = LOW;
- // PIR sensor enabled
- bool enabled = true;
- // status of initialisation
- bool initDone = false;
- // on and off presets
- uint8_t m_onPreset = 0;
- uint8_t m_offPreset = 0;
- // flag to indicate that PIR sensor should activate WLED during nighttime only
- bool m_nightTimeOnly = false;
- // flag to send MQTT message only (assuming it is enabled)
- bool m_mqttOnly = false;
- // flag to enable triggering only if WLED is initially off (LEDs are not on, preventing running effect being overwritten by PIR)
- bool m_offOnly = false;
- bool PIRtriggered = false;
-
- unsigned long lastLoop = 0;
-
- // strings to reduce flash memory usage (used more than twice)
- static const char _name[];
- static const char _switchOffDelay[];
- static const char _enabled[];
- static const char _onPreset[];
- static const char _offPreset[];
- static const char _nightTime[];
- static const char _mqttOnly[];
- static const char _offOnly[];
-
- /**
- * check if it is daytime
- * if sunrise/sunset is not defined (no NTP or lat/lon) default to nighttime
- */
- bool isDayTime() {
- bool isDayTime = false;
- updateLocalTime();
- uint8_t hr = hour(localTime);
- uint8_t mi = minute(localTime);
-
- if (sunrise && sunset) {
- if (hour(sunrise)
hr) {
- isDayTime = true;
- } else {
- if (hour(sunrise)==hr && minute(sunrise)mi) {
- isDayTime = true;
- }
- }
- }
- return isDayTime;
- }
-
- /**
- * switch strip on/off
- */
- void switchStrip(bool switchOn)
- {
- if (m_offOnly && bri && (switchOn || (!PIRtriggered && !switchOn))) return;
- PIRtriggered = switchOn;
- if (switchOn && m_onPreset) {
- applyPreset(m_onPreset);
- } else if (!switchOn && m_offPreset) {
- applyPreset(m_offPreset);
- } else if (switchOn && bri == 0) {
- bri = briLast;
- colorUpdated(NotifyUpdateMode);
- } else if (!switchOn && bri != 0) {
- briLast = bri;
- bri = 0;
- colorUpdated(NotifyUpdateMode);
- }
- }
-
- void publishMqtt(const char* state)
- {
- //Check if MQTT Connected, otherwise it will crash the 8266
- if (WLED_MQTT_CONNECTED){
- char subuf[64];
- strcpy(subuf, mqttDeviceTopic);
- strcat_P(subuf, PSTR("/motion"));
- mqtt->publish(subuf, 0, false, state);
- }
- }
-
- /**
- * Read and update PIR sensor state.
- * Initilize/reset switch off timer
- */
- bool updatePIRsensorState()
- {
- bool pinState = digitalRead(PIRsensorPin);
- if (pinState != sensorPinState) {
- sensorPinState = pinState; // change previous state
-
- if (sensorPinState == HIGH) {
- m_offTimerStart = 0;
- if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(true);
- publishMqtt("on");
- } else /*if (bri != 0)*/ {
- // start switch off timer
- m_offTimerStart = millis();
- }
- return true;
- }
- return false;
- }
-
- /**
- * switch off the strip if the delay has elapsed
- */
- bool handleOffTimer()
- {
- if (m_offTimerStart > 0 && millis() - m_offTimerStart > m_switchOffDelay)
- {
- if (enabled == true)
- {
- if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(false);
- publishMqtt("off");
- }
- m_offTimerStart = 0;
- return true;
- }
- return false;
- }
-
-public:
- //Functions called by WLED
-
- /**
- * setup() is called once at boot. WiFi is not yet connected at this point.
- * You can use it to initialize variables, sensors or similar.
- */
- void setup()
- {
- if (enabled) {
- // pin retrieved from cfg.json (readFromConfig()) prior to running setup()
- if (PIRsensorPin >= 0 && pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) {
- // PIR Sensor mode INPUT_PULLUP
- pinMode(PIRsensorPin, INPUT_PULLUP);
- sensorPinState = digitalRead(PIRsensorPin);
- } else {
- if (PIRsensorPin >= 0) {
- DEBUG_PRINTLN(F("PIRSensorSwitch pin allocation failed."));
- }
- PIRsensorPin = -1; // allocation failed
- enabled = false;
- }
- }
- initDone = true;
- }
-
- /**
- * connected() is called every time the WiFi is (re)connected
- * Use it to initialize network interfaces
- */
- void connected()
- {
- }
-
- /**
- * loop() is called continuously. Here you can check for events, read sensors, etc.
- */
- void loop()
- {
- // only check sensors 4x/s
- if (!enabled || millis() - lastLoop < 250 || strip.isUpdating()) return;
- lastLoop = millis();
-
- if (!updatePIRsensorState()) {
- handleOffTimer();
- }
- }
-
- /**
- * addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
- *
- * Add PIR sensor state and switch off timer duration to jsoninfo
- */
- void addToJsonInfo(JsonObject &root)
- {
- JsonObject user = root["u"];
- if (user.isNull()) user = root.createNestedObject("u");
-
- if (enabled)
- {
- // off timer
- String uiDomString = F("PIR ");
- JsonArray infoArr = user.createNestedArray(uiDomString); // timer value
- if (m_offTimerStart > 0)
- {
- uiDomString = "";
- unsigned int offSeconds = (m_switchOffDelay - (millis() - m_offTimerStart)) / 1000;
- if (offSeconds >= 3600)
- {
- uiDomString += (offSeconds / 3600);
- uiDomString += F("h ");
- offSeconds %= 3600;
- }
- if (offSeconds >= 60)
- {
- uiDomString += (offSeconds / 60);
- offSeconds %= 60;
- }
- else if (uiDomString.length() > 0)
- {
- uiDomString += 0;
- }
- if (uiDomString.length() > 0)
- {
- uiDomString += F("min ");
- }
- uiDomString += (offSeconds);
- infoArr.add(uiDomString + F("s"));
- } else {
- infoArr.add(sensorPinState ? F("sensor on") : F("inactive"));
- }
- } else {
- String uiDomString = F("PIR sensor");
- JsonArray infoArr = user.createNestedArray(uiDomString);
- infoArr.add(F("disabled"));
- }
- }
-
- /**
- * 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
- */
-/*
- void addToJsonState(JsonObject &root)
- {
- }
-*/
-
- /**
- * 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
- */
-/*
- void readFromJsonState(JsonObject &root)
- {
- }
-*/
-
- /**
- * provide the changeable values
- */
- void addToConfig(JsonObject &root)
- {
- JsonObject top = root.createNestedObject(FPSTR(_name));
- top[FPSTR(_enabled)] = enabled;
- top[FPSTR(_switchOffDelay)] = m_switchOffDelay / 1000;
- top["pin"] = PIRsensorPin;
- top[FPSTR(_onPreset)] = m_onPreset;
- top[FPSTR(_offPreset)] = m_offPreset;
- top[FPSTR(_nightTime)] = m_nightTimeOnly;
- top[FPSTR(_mqttOnly)] = m_mqttOnly;
- top[FPSTR(_offOnly)] = m_offOnly;
- DEBUG_PRINTLN(F("PIR config saved."));
- }
-
- /**
- * 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)
- {
- bool oldEnabled = enabled;
- int8_t oldPin = PIRsensorPin;
-
- JsonObject top = root[FPSTR(_name)];
- if (top.isNull()) {
- DEBUG_PRINT(FPSTR(_name));
- DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
- return false;
- }
-
- PIRsensorPin = top["pin"] | PIRsensorPin;
-
- enabled = top[FPSTR(_enabled)] | enabled;
-
- m_switchOffDelay = (top[FPSTR(_switchOffDelay)] | m_switchOffDelay/1000) * 1000;
-
- m_onPreset = top[FPSTR(_onPreset)] | m_onPreset;
- m_onPreset = max(0,min(250,(int)m_onPreset));
-
- m_offPreset = top[FPSTR(_offPreset)] | m_offPreset;
- m_offPreset = max(0,min(250,(int)m_offPreset));
-
- m_nightTimeOnly = top[FPSTR(_nightTime)] | m_nightTimeOnly;
- m_mqttOnly = top[FPSTR(_mqttOnly)] | m_mqttOnly;
- m_offOnly = top[FPSTR(_offOnly)] | m_offOnly;
-
- DEBUG_PRINT(FPSTR(_name));
- if (!initDone) {
- // reading config prior to setup()
- DEBUG_PRINTLN(F(" config loaded."));
- } else {
- if (oldPin != PIRsensorPin || oldEnabled != enabled) {
- // check if pin is OK
- if (oldPin != PIRsensorPin && oldPin >= 0) {
- // if we are changing pin in settings page
- // deallocate old pin
- pinManager.deallocatePin(oldPin, PinOwner::UM_PIR);
- if (pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) {
- pinMode(PIRsensorPin, INPUT_PULLUP);
- } else {
- // allocation failed
- PIRsensorPin = -1;
- enabled = false;
- }
- }
- if (enabled) {
- sensorPinState = digitalRead(PIRsensorPin);
- }
- }
- DEBUG_PRINTLN(F(" config (re)loaded."));
- }
- // use "return !top["newestParameter"].isNull();" when updating Usermod with new features
- return !top[FPSTR(_offOnly)].isNull();
- }
-
- /**
- * 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.
- */
- uint16_t getId()
- {
- return USERMOD_ID_PIRSWITCH;
- }
-};
-
-// strings to reduce flash memory usage (used more than twice)
-const char PIRsensorSwitch::_name[] PROGMEM = "PIRsensorSwitch";
-const char PIRsensorSwitch::_enabled[] PROGMEM = "PIRenabled";
-const char PIRsensorSwitch::_switchOffDelay[] PROGMEM = "PIRoffSec";
-const char PIRsensorSwitch::_onPreset[] PROGMEM = "on-preset";
-const char PIRsensorSwitch::_offPreset[] PROGMEM = "off-preset";
-const char PIRsensorSwitch::_nightTime[] PROGMEM = "nighttime-only";
-const char PIRsensorSwitch::_mqttOnly[] PROGMEM = "mqtt-only";
-const char PIRsensorSwitch::_offOnly[] PROGMEM = "off-only";
+#pragma once
+
+#include "wled.h"
+
+#ifndef PIR_SENSOR_PIN
+ // compatible with QuinLED-Dig-Uno
+ #ifdef ARDUINO_ARCH_ESP32
+ #define PIR_SENSOR_PIN 23 // Q4
+ #else //ESP8266 boards
+ #define PIR_SENSOR_PIN 13 // Q4 (D7 on D1 mini)
+ #endif
+#endif
+
+/*
+ * This usermod handles PIR sensor states.
+ * The strip will be switched on and the off timer will be resetted when the sensor goes HIGH.
+ * When the sensor state goes LOW, the off timer is started and when it expires, the strip is switched off.
+ *
+ *
+ * Usermods allow you to add own functionality to WLED more easily
+ * See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
+ *
+ * v2 usermods are class inheritance based and can (but don't have to) implement more functions, each of them is shown in this example.
+ * Multiple v2 usermods can be added to one compilation easily.
+ *
+ * Creating a usermod:
+ * This file serves as an example. If you want to create a usermod, it is recommended to use usermod_v2_empty.h from the usermods folder as a template.
+ * Please remember to rename the class and file to a descriptive name.
+ * You may also use multiple .h and .cpp files.
+ *
+ * Using a usermod:
+ * 1. Copy the usermod into the sketch folder (same folder as wled00.ino)
+ * 2. Register the usermod by adding #include "usermod_filename.h" in the top and registerUsermod(new MyUsermodClass()) in the bottom of usermods_list.cpp
+ */
+
+class PIRsensorSwitch : public Usermod
+{
+public:
+ /**
+ * constructor
+ */
+ PIRsensorSwitch() {}
+ /**
+ * desctructor
+ */
+ ~PIRsensorSwitch() {}
+
+ /**
+ * Enable/Disable the PIR sensor
+ */
+ void EnablePIRsensor(bool en) { enabled = en; }
+ /**
+ * Get PIR sensor enabled/disabled state
+ */
+ bool PIRsensorEnabled() { return enabled; }
+
+private:
+ // PIR sensor pin
+ int8_t PIRsensorPin = PIR_SENSOR_PIN;
+ // notification mode for colorUpdated()
+ const byte NotifyUpdateMode = CALL_MODE_NO_NOTIFY; // CALL_MODE_DIRECT_CHANGE
+ // delay before switch off after the sensor state goes LOW
+ uint32_t m_switchOffDelay = 600000; // 10min
+ // off timer start time
+ uint32_t m_offTimerStart = 0;
+ // current PIR sensor pin state
+ byte sensorPinState = LOW;
+ // PIR sensor enabled
+ bool enabled = true;
+ // status of initialisation
+ bool initDone = false;
+ // on and off presets
+ uint8_t m_onPreset = 0;
+ uint8_t m_offPreset = 0;
+ // flag to indicate that PIR sensor should activate WLED during nighttime only
+ bool m_nightTimeOnly = false;
+ // flag to send MQTT message only (assuming it is enabled)
+ bool m_mqttOnly = false;
+ // flag to enable triggering only if WLED is initially off (LEDs are not on, preventing running effect being overwritten by PIR)
+ bool m_offOnly = false;
+ bool PIRtriggered = false;
+
+ unsigned long lastLoop = 0;
+
+ // strings to reduce flash memory usage (used more than twice)
+ static const char _name[];
+ static const char _switchOffDelay[];
+ static const char _enabled[];
+ static const char _onPreset[];
+ static const char _offPreset[];
+ static const char _nightTime[];
+ static const char _mqttOnly[];
+ static const char _offOnly[];
+
+ /**
+ * check if it is daytime
+ * if sunrise/sunset is not defined (no NTP or lat/lon) default to nighttime
+ */
+ bool isDayTime() {
+ bool isDayTime = false;
+ updateLocalTime();
+ uint8_t hr = hour(localTime);
+ uint8_t mi = minute(localTime);
+
+ if (sunrise && sunset) {
+ if (hour(sunrise)
hr) {
+ isDayTime = true;
+ } else {
+ if (hour(sunrise)==hr && minute(sunrise)mi) {
+ isDayTime = true;
+ }
+ }
+ }
+ return isDayTime;
+ }
+
+ /**
+ * switch strip on/off
+ */
+ void switchStrip(bool switchOn)
+ {
+ if (m_offOnly && bri && (switchOn || (!PIRtriggered && !switchOn))) return;
+ PIRtriggered = switchOn;
+ if (switchOn && m_onPreset) {
+ applyPreset(m_onPreset);
+ } else if (!switchOn && m_offPreset) {
+ applyPreset(m_offPreset);
+ } else if (switchOn && bri == 0) {
+ bri = briLast;
+ colorUpdated(NotifyUpdateMode);
+ } else if (!switchOn && bri != 0) {
+ briLast = bri;
+ bri = 0;
+ colorUpdated(NotifyUpdateMode);
+ }
+ }
+
+ void publishMqtt(const char* state)
+ {
+ //Check if MQTT Connected, otherwise it will crash the 8266
+ if (WLED_MQTT_CONNECTED){
+ char subuf[64];
+ strcpy(subuf, mqttDeviceTopic);
+ strcat_P(subuf, PSTR("/motion"));
+ mqtt->publish(subuf, 0, false, state);
+ }
+ }
+
+ /**
+ * Read and update PIR sensor state.
+ * Initilize/reset switch off timer
+ */
+ bool updatePIRsensorState()
+ {
+ bool pinState = digitalRead(PIRsensorPin);
+ if (pinState != sensorPinState) {
+ sensorPinState = pinState; // change previous state
+
+ if (sensorPinState == HIGH) {
+ m_offTimerStart = 0;
+ if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(true);
+ publishMqtt("on");
+ } else /*if (bri != 0)*/ {
+ // start switch off timer
+ m_offTimerStart = millis();
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * switch off the strip if the delay has elapsed
+ */
+ bool handleOffTimer()
+ {
+ if (m_offTimerStart > 0 && millis() - m_offTimerStart > m_switchOffDelay)
+ {
+ if (enabled == true)
+ {
+ if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(false);
+ publishMqtt("off");
+ }
+ m_offTimerStart = 0;
+ return true;
+ }
+ return false;
+ }
+
+public:
+ //Functions called by WLED
+
+ /**
+ * setup() is called once at boot. WiFi is not yet connected at this point.
+ * You can use it to initialize variables, sensors or similar.
+ */
+ void setup()
+ {
+ if (enabled) {
+ // pin retrieved from cfg.json (readFromConfig()) prior to running setup()
+ if (PIRsensorPin >= 0 && pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) {
+ // PIR Sensor mode INPUT_PULLUP
+ pinMode(PIRsensorPin, INPUT_PULLUP);
+ sensorPinState = digitalRead(PIRsensorPin);
+ } else {
+ if (PIRsensorPin >= 0) {
+ DEBUG_PRINTLN(F("PIRSensorSwitch pin allocation failed."));
+ }
+ PIRsensorPin = -1; // allocation failed
+ enabled = false;
+ }
+ }
+ initDone = true;
+ }
+
+ /**
+ * connected() is called every time the WiFi is (re)connected
+ * Use it to initialize network interfaces
+ */
+ void connected()
+ {
+ }
+
+ /**
+ * loop() is called continuously. Here you can check for events, read sensors, etc.
+ */
+ void loop()
+ {
+ // only check sensors 4x/s
+ if (!enabled || millis() - lastLoop < 250 || strip.isUpdating()) return;
+ lastLoop = millis();
+
+ if (!updatePIRsensorState()) {
+ handleOffTimer();
+ }
+ }
+
+ /**
+ * addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API.
+ *
+ * Add PIR sensor state and switch off timer duration to jsoninfo
+ */
+ void addToJsonInfo(JsonObject &root)
+ {
+ JsonObject user = root["u"];
+ if (user.isNull()) user = root.createNestedObject("u");
+
+ if (enabled)
+ {
+ // off timer
+ String uiDomString = F("PIR ");
+ JsonArray infoArr = user.createNestedArray(uiDomString); // timer value
+ if (m_offTimerStart > 0)
+ {
+ uiDomString = "";
+ unsigned int offSeconds = (m_switchOffDelay - (millis() - m_offTimerStart)) / 1000;
+ if (offSeconds >= 3600)
+ {
+ uiDomString += (offSeconds / 3600);
+ uiDomString += F("h ");
+ offSeconds %= 3600;
+ }
+ if (offSeconds >= 60)
+ {
+ uiDomString += (offSeconds / 60);
+ offSeconds %= 60;
+ }
+ else if (uiDomString.length() > 0)
+ {
+ uiDomString += 0;
+ }
+ if (uiDomString.length() > 0)
+ {
+ uiDomString += F("min ");
+ }
+ uiDomString += (offSeconds);
+ infoArr.add(uiDomString + F("s"));
+ } else {
+ infoArr.add(sensorPinState ? F("sensor on") : F("inactive"));
+ }
+ } else {
+ String uiDomString = F("PIR sensor");
+ JsonArray infoArr = user.createNestedArray(uiDomString);
+ infoArr.add(F("disabled"));
+ }
+ }
+
+ /**
+ * 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
+ */
+/*
+ void addToJsonState(JsonObject &root)
+ {
+ }
+*/
+
+ /**
+ * 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
+ */
+/*
+ void readFromJsonState(JsonObject &root)
+ {
+ }
+*/
+
+ /**
+ * provide the changeable values
+ */
+ void addToConfig(JsonObject &root)
+ {
+ JsonObject top = root.createNestedObject(FPSTR(_name));
+ top[FPSTR(_enabled)] = enabled;
+ top[FPSTR(_switchOffDelay)] = m_switchOffDelay / 1000;
+ top["pin"] = PIRsensorPin;
+ top[FPSTR(_onPreset)] = m_onPreset;
+ top[FPSTR(_offPreset)] = m_offPreset;
+ top[FPSTR(_nightTime)] = m_nightTimeOnly;
+ top[FPSTR(_mqttOnly)] = m_mqttOnly;
+ top[FPSTR(_offOnly)] = m_offOnly;
+ DEBUG_PRINTLN(F("PIR config saved."));
+ }
+
+ /**
+ * 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)
+ {
+ bool oldEnabled = enabled;
+ int8_t oldPin = PIRsensorPin;
+
+ JsonObject top = root[FPSTR(_name)];
+ if (top.isNull()) {
+ DEBUG_PRINT(FPSTR(_name));
+ DEBUG_PRINTLN(F(": No config found. (Using defaults.)"));
+ return false;
+ }
+
+ PIRsensorPin = top["pin"] | PIRsensorPin;
+
+ enabled = top[FPSTR(_enabled)] | enabled;
+
+ m_switchOffDelay = (top[FPSTR(_switchOffDelay)] | m_switchOffDelay/1000) * 1000;
+
+ m_onPreset = top[FPSTR(_onPreset)] | m_onPreset;
+ m_onPreset = max(0,min(250,(int)m_onPreset));
+
+ m_offPreset = top[FPSTR(_offPreset)] | m_offPreset;
+ m_offPreset = max(0,min(250,(int)m_offPreset));
+
+ m_nightTimeOnly = top[FPSTR(_nightTime)] | m_nightTimeOnly;
+ m_mqttOnly = top[FPSTR(_mqttOnly)] | m_mqttOnly;
+ m_offOnly = top[FPSTR(_offOnly)] | m_offOnly;
+
+ DEBUG_PRINT(FPSTR(_name));
+ if (!initDone) {
+ // reading config prior to setup()
+ DEBUG_PRINTLN(F(" config loaded."));
+ } else {
+ if (oldPin != PIRsensorPin || oldEnabled != enabled) {
+ // check if pin is OK
+ if (oldPin != PIRsensorPin && oldPin >= 0) {
+ // if we are changing pin in settings page
+ // deallocate old pin
+ pinManager.deallocatePin(oldPin, PinOwner::UM_PIR);
+ if (pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) {
+ pinMode(PIRsensorPin, INPUT_PULLUP);
+ } else {
+ // allocation failed
+ PIRsensorPin = -1;
+ enabled = false;
+ }
+ }
+ if (enabled) {
+ sensorPinState = digitalRead(PIRsensorPin);
+ }
+ }
+ DEBUG_PRINTLN(F(" config (re)loaded."));
+ }
+ // use "return !top["newestParameter"].isNull();" when updating Usermod with new features
+ return !top[FPSTR(_offOnly)].isNull();
+ }
+
+ /**
+ * 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.
+ */
+ uint16_t getId()
+ {
+ return USERMOD_ID_PIRSWITCH;
+ }
+};
+
+// strings to reduce flash memory usage (used more than twice)
+const char PIRsensorSwitch::_name[] PROGMEM = "PIRsensorSwitch";
+const char PIRsensorSwitch::_enabled[] PROGMEM = "PIRenabled";
+const char PIRsensorSwitch::_switchOffDelay[] PROGMEM = "PIRoffSec";
+const char PIRsensorSwitch::_onPreset[] PROGMEM = "on-preset";
+const char PIRsensorSwitch::_offPreset[] PROGMEM = "off-preset";
+const char PIRsensorSwitch::_nightTime[] PROGMEM = "nighttime-only";
+const char PIRsensorSwitch::_mqttOnly[] PROGMEM = "mqtt-only";
+const char PIRsensorSwitch::_offOnly[] PROGMEM = "off-only";
diff --git a/usermods/Temperature/usermod_temperature.h b/usermods/Temperature/usermod_temperature.h
index c42290474..7c209f475 100644
--- a/usermods/Temperature/usermod_temperature.h
+++ b/usermods/Temperature/usermod_temperature.h
@@ -81,7 +81,9 @@ class UsermodTemperature : public Usermod {
temperature = readDallas();
lastMeasurement = millis();
waitingForConversion = false;
- DEBUG_PRINTF("Read temperature %2.1f.\n", temperature);
+ //DEBUG_PRINTF("Read temperature %2.1f.\n", temperature); // does not work properly on 8266
+ DEBUG_PRINT(F("Read temperature "));
+ DEBUG_PRINTLN(temperature);
}
bool findSensor() {
diff --git a/usermods/multi_relay/usermod_multi_relay.h b/usermods/multi_relay/usermod_multi_relay.h
index cd3e01c78..c3d55d66d 100644
--- a/usermods/multi_relay/usermod_multi_relay.h
+++ b/usermods/multi_relay/usermod_multi_relay.h
@@ -317,15 +317,15 @@ class MultiRelay : public Usermod {
* 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
*/
- 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).
* Values in the state object may be modified by connected clients
*/
- void readFromJsonState(JsonObject &root) {
- }
+ //void readFromJsonState(JsonObject &root) {
+ //}
/**
* provide the changeable values
@@ -335,11 +335,12 @@ class MultiRelay : public Usermod {
top[FPSTR(_enabled)] = enabled;
for (uint8_t i=0; isetBusClock(ioFrequency); // can be used for SPI too
u8x8->begin();
setFlipMode(flip);
setContrast(contrast); //Contrast setup will help to preserve OLED lifetime. In case OLED need to be brighter increase number up to 255
@@ -683,6 +686,7 @@ class FourLineDisplayUsermod : public Usermod {
top[FPSTR(_screenTimeOut)] = screenTimeout/1000;
top[FPSTR(_sleepMode)] = (bool) sleepMode;
top[FPSTR(_clockMode)] = (bool) clockMode;
+ top[FPSTR(_busClkFrequency)] = ioFrequency/1000;
DEBUG_PRINTLN(F("4 Line Display config saved."));
}
@@ -714,6 +718,7 @@ class FourLineDisplayUsermod : public Usermod {
screenTimeout = (top[FPSTR(_screenTimeOut)] | screenTimeout/1000) * 1000;
sleepMode = top[FPSTR(_sleepMode)] | sleepMode;
clockMode = top[FPSTR(_clockMode)] | clockMode;
+ ioFrequency = min(3400, max(100, (int)(top[FPSTR(_busClkFrequency)] | ioFrequency/1000))) * 1000; // limit frequency
DEBUG_PRINT(FPSTR(_name));
if (!initDone) {
@@ -739,12 +744,13 @@ class FourLineDisplayUsermod : public Usermod {
setup();
needsRedraw |= true;
}
+ if (!(type == SSD1306_SPI || type == SSD1306_SPI64)) u8x8->setBusClock(ioFrequency); // can be used for SPI too
setContrast(contrast);
setFlipMode(flip);
if (needsRedraw && !wakeDisplay()) redraw(true);
}
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
- return !(top["pin"][2]).isNull();
+ return !(top[_busClkFrequency]).isNull();
}
/*
@@ -757,10 +763,11 @@ class FourLineDisplayUsermod : public Usermod {
};
// strings to reduce flash memory usage (used more than twice)
-const char FourLineDisplayUsermod::_name[] PROGMEM = "4LineDisplay";
-const char FourLineDisplayUsermod::_contrast[] PROGMEM = "contrast";
-const char FourLineDisplayUsermod::_refreshRate[] PROGMEM = "refreshRateSec";
-const char FourLineDisplayUsermod::_screenTimeOut[] PROGMEM = "screenTimeOutSec";
-const char FourLineDisplayUsermod::_flip[] PROGMEM = "flip";
-const char FourLineDisplayUsermod::_sleepMode[] PROGMEM = "sleepMode";
-const char FourLineDisplayUsermod::_clockMode[] PROGMEM = "clockMode";
+const char FourLineDisplayUsermod::_name[] PROGMEM = "4LineDisplay";
+const char FourLineDisplayUsermod::_contrast[] PROGMEM = "contrast";
+const char FourLineDisplayUsermod::_refreshRate[] PROGMEM = "refreshRateSec";
+const char FourLineDisplayUsermod::_screenTimeOut[] PROGMEM = "screenTimeOutSec";
+const char FourLineDisplayUsermod::_flip[] PROGMEM = "flip";
+const char FourLineDisplayUsermod::_sleepMode[] PROGMEM = "sleepMode";
+const char FourLineDisplayUsermod::_clockMode[] PROGMEM = "clockMode";
+const char FourLineDisplayUsermod::_busClkFrequency[] PROGMEM = "i2c-freq-kHz";
diff --git a/wled00/wled_ethernet.h b/wled00/wled_ethernet.h
index 4332ef566..6c4df32ae 100644
--- a/wled00/wled_ethernet.h
+++ b/wled00/wled_ethernet.h
@@ -4,18 +4,6 @@
#ifdef WLED_USE_ETHERNET
#include "pin_manager.h"
-// The following six pins are neither configurable nor
-// can they be re-assigned through IOMUX / GPIO matrix.
-// See https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-ethernet-kit-v1.1.html#ip101gri-phy-interface
-const managed_pin_type esp32_nonconfigurable_ethernet_pins[6] = {
- { 21, true }, // RMII EMAC TX EN == When high, clocks the data on TXD0 and TXD1 to transmitter
- { 19, true }, // RMII EMAC TXD0 == First bit of transmitted data
- { 22, true }, // RMII EMAC TXD1 == Second bit of transmitted data
- { 25, false }, // RMII EMAC RXD0 == First bit of received data
- { 26, false }, // RMII EMAC RXD1 == Second bit of received data
- { 27, true }, // RMII EMAC CRS_DV == Carrier Sense and RX Data Valid
-};
-
// For ESP32, the remaining five pins are at least somewhat configurable.
// eth_address is in range [0..31], indicates which PHY (MAC?) address should be allocated to the interface
// eth_power is an output GPIO pin used to enable/disable the ethernet port (and/or external oscillator)
@@ -37,15 +25,16 @@ typedef struct EthernetSettings {
eth_clock_mode_t eth_clk_mode;
} ethernet_settings;
-ethernet_settings ethernetBoards[] = {
+const ethernet_settings ethernetBoards[] = {
// None
{
},
// WT32-EHT01
- // (*) NOTE: silkscreen on board revision v1.2 may be wrong:
- // silkscreen on v1.2 says IO35, but appears to be IO5
- // silkscreen on v1.2 says RXD, and appears to be IO35
+ // Please note, from my testing only these pins work for LED outputs:
+ // IO2, IO4, IO12, IO14, IO15
+ // These pins do not appear to work from my testing:
+ // IO35, IO36, IO39
{
1, // eth_address,
16, // eth_power,
@@ -97,14 +86,27 @@ ethernet_settings ethernetBoards[] = {
// ESP3DEUXQuattro
{
- 1, // eth_address,
- -1, // eth_power,
- 23, // eth_mdc,
- 18, // eth_mdio,
- ETH_PHY_LAN8720, // eth_type,
- ETH_CLOCK_GPIO17_OUT // eth_clk_mode
+ 1, // eth_address,
+ -1, // eth_power,
+ 23, // eth_mdc,
+ 18, // eth_mdio,
+ ETH_PHY_LAN8720, // eth_type,
+ ETH_CLOCK_GPIO17_OUT // eth_clk_mode
}
};
#endif
+#define WLED_ETH_RSVD_PINS_COUNT 6
+// The following six pins are neither configurable nor
+// can they be re-assigned through IOMUX / GPIO matrix.
+// See https://docs.espressif.com/projects/esp-idf/en/latest/esp32/hw-reference/esp32/get-started-ethernet-kit-v1.1.html#ip101gri-phy-interface
+const managed_pin_type esp32_nonconfigurable_ethernet_pins[WLED_ETH_RSVD_PINS_COUNT] = {
+ { 21, true }, // RMII EMAC TX EN == When high, clocks the data on TXD0 and TXD1 to transmitter
+ { 19, true }, // RMII EMAC TXD0 == First bit of transmitted data
+ { 22, true }, // RMII EMAC TXD1 == Second bit of transmitted data
+ { 25, false }, // RMII EMAC RXD0 == First bit of received data
+ { 26, false }, // RMII EMAC RXD1 == Second bit of received data
+ { 27, true }, // RMII EMAC CRS_DV == Carrier Sense and RX Data Valid
+};
+
#endif
\ No newline at end of file
diff --git a/wled00/xml.cpp b/wled00/xml.cpp
index 6ef33412c..6997c98fe 100644
--- a/wled00/xml.cpp
+++ b/wled00/xml.cpp
@@ -1,4 +1,5 @@
#include "wled.h"
+#include "wled_ethernet.h"
/*
* Sending XML status files to client
@@ -186,6 +187,52 @@ void sappends(char stype, const char* key, char* val)
}
}
+void extractPin(JsonObject &obj, const char *key) {
+ if (obj[key].is()) {
+ JsonArray pins = obj[key].as();
+ for (JsonVariant pv : pins) {
+ if (pv.as() > -1) { oappend(","); oappendi(pv.as()); }
+ }
+ } else {
+ if (obj[key].as() > -1) { oappend(","); oappendi(obj[key].as()); }
+ }
+}
+
+// oappens used pins by scanning JsonObject (1 level deep)
+void fillUMPins(JsonObject &mods)
+{
+ for (JsonPair kv : mods) {
+ // kv.key() is usermod name or subobject key
+ // kv.value() is object itself
+ JsonObject obj = kv.value();
+ if (!obj.isNull()) {
+ // element is an JsonObject
+ if (!obj["pin"].isNull()) {
+ extractPin(obj, "pin");
+ } else {
+ // scan keys (just one level deep as is possible with usermods)
+ for (JsonPair so : obj) {
+ const char *key = so.key().c_str();
+ if (strstr(key, "pin")) {
+ // we found a key containing "pin" substring
+ if (strlen(strstr(key, "pin")) == 3) {
+ // and it is at the end, we found another pin
+ extractPin(obj, key);
+ continue;
+ }
+ }
+ if (!obj[so.key()].is()) continue;
+ JsonObject subObj = obj[so.key()];
+ if (!subObj["pin"].isNull()) {
+ // get pins from subobject
+ extractPin(subObj, "pin");
+ }
+ }
+ }
+ }
+ }
+}
+
//get values for settings form in javascript
void getSettingsJS(byte subPage, char* dest)
@@ -198,7 +245,8 @@ void getSettingsJS(byte subPage, char* dest)
if (subPage <1 || subPage >8) return;
- if (subPage == 1) {
+ if (subPage == 1)
+ {
sappends('s',SET_F("CS"),clientSSID);
byte l = strlen(clientPass);
@@ -264,50 +312,53 @@ void getSettingsJS(byte subPage, char* dest)
}
}
- if (subPage == 2) {
+ if (subPage == 2)
+ {
char nS[8];
// add reserved and usermod pins as d.um_p array
+ oappend(SET_F("d.um_p=[6,7,8,9,10,11"));
+
DynamicJsonDocument doc(JSON_BUFFER_SIZE/2);
JsonObject mods = doc.createNestedObject(F("um"));
usermods.addToConfig(mods);
- oappend(SET_F("d.um_p=["));
- if (!mods.isNull()) {
- uint8_t i=0;
- for (JsonPair kv : mods) {
- if (!kv.value().isNull()) {
- // element is an JsonObject
- JsonObject obj = kv.value();
- if (obj["pin"] != nullptr) {
- if (obj["pin"].is()) {
- JsonArray pins = obj["pin"].as();
- for (JsonVariant pv : pins) {
- if (i++) oappend(SET_F(","));
- oappendi(pv.as());
- }
- } else {
- if (i++) oappend(SET_F(","));
- oappendi(obj["pin"].as());
- }
- }
- }
+ if (!mods.isNull()) fillUMPins(mods);
+
+ #ifdef WLED_ENABLE_DMX
+ oappend(SET_F(",2")); // DMX hardcoded pin
+ #endif
+
+ //Note: Using pin 3 (RX) disables Adalight / Serial JSON
+
+ #ifdef WLED_DEBUG
+ oappend(SET_F(",1")); // debug output (TX) pin
+ #endif
+
+ #if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
+ if (psramFound()) oappend(SET_F(",16,17")); // GPIO16 & GPIO17 reserved for SPI RAM
+ #endif
+
+ #ifdef WLED_USE_ETHERNET
+ if (ethernetType != WLED_ETH_NONE && ethernetType < WLED_NUM_ETH_TYPES) {
+ for (uint8_t p=0; p=0) { oappend(","); oappend(itoa(ethernetBoards[ethernetType].eth_power,nS,10)); }
+ if (ethernetBoards[ethernetType].eth_mdc>=0) { oappend(","); oappend(itoa(ethernetBoards[ethernetType].eth_mdc,nS,10)); }
+ if (ethernetBoards[ethernetType].eth_mdio>=0) { oappend(","); oappend(itoa(ethernetBoards[ethernetType].eth_mdio,nS,10)); }
+ switch (ethernetBoards[ethernetType].eth_clk_mode) {
+ case ETH_CLOCK_GPIO0_IN:
+ case ETH_CLOCK_GPIO0_OUT:
+ oappend(SET_F(",0"));
+ break;
+ case ETH_CLOCK_GPIO16_OUT:
+ oappend(SET_F(",16"));
+ break;
+ case ETH_CLOCK_GPIO17_OUT:
+ oappend(SET_F(",17"));
+ break;
}
- if (i) oappend(SET_F(","));
- oappend(SET_F("6,7,8,9,10,11")); // flash memory pins
- #ifdef WLED_ENABLE_DMX
- oappend(SET_F(",2")); // DMX hardcoded pin
- #endif
- //Adalight / Serial in requires pin 3 to be unused. However, Serial input can not be prevented by WLED
- #ifdef WLED_DEBUG
- oappend(SET_F(",1")); // debug output (TX) pin
- #endif
- #if defined(ARDUINO_ARCH_ESP32) && defined(WLED_USE_PSRAM)
- if (psramFound()) oappend(SET_F(",16,17")); // GPIO16 & GPIO17 reserved for SPI RAM
- #endif
- //TODO: add reservations for Ethernet shield pins
- #ifdef WLED_USE_ETHERNET
- #endif
}
+ #endif
+
oappend(SET_F("];"));
// set limits
@@ -338,9 +389,9 @@ void getSettingsJS(byte subPage, char* dest)
uint8_t nPins = bus->getPins(pins);
for (uint8_t i = 0; i < nPins; i++) {
lp[1] = 48+i;
- if (pinManager.isPinOk(pins[i])) sappend('v', lp, pins[i]);
+ if (pinManager.isPinOk(pins[i])) sappend('v',lp,pins[i]);
}
- sappend('v', lc, bus->getLength());
+ sappend('v',lc,bus->getLength());
sappend('v',lt,bus->getType());
sappend('v',co,bus->getColorOrder());
sappend('v',ls,bus->getStart());