From 8453cd82e99a65f8f8a737e9527dff946644d4e2 Mon Sep 17 00:00:00 2001 From: cschwinne Date: Mon, 27 Sep 2021 22:51:40 +0200 Subject: [PATCH 1/4] Fixed DMXmap --- wled00/data/dmxmap.htm | 6 +----- wled00/html_other.h | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/wled00/data/dmxmap.htm b/wled00/data/dmxmap.htm index 23e056cb..25953b0e 100644 --- a/wled00/data/dmxmap.htm +++ b/wled00/data/dmxmap.htm @@ -16,11 +16,7 @@ } DMXMap = ""; for (i=0;i<512;i++) { - isstart = ""; - if ((i+1) % 10 == 0) { - isstart="S" - } - DMXMap += "
" + String(i+1) + "
" + dmxlabels[dmxchans[i]] + "
"; + DMXMap += "
" + String(i+1) + "
" + dmxlabels[dmxchans[i]] + "
"; } document.getElementById("map").innerHTML = DMXMap; } diff --git a/wled00/html_other.h b/wled00/html_other.h index e0bf4217..ef9f70b4 100644 --- a/wled00/html_other.h +++ b/wled00/html_other.h @@ -24,7 +24,7 @@ function B(){window.history.back()}function RS(){window.location="/settings"}fun const char PAGE_dmxmap[] PROGMEM = R"=====( DMX Map
...
)====="; From 3a874bc8c7d6e1d520c4829a77b61dc902489bf2 Mon Sep 17 00:00:00 2001 From: Scott Bailey Date: Tue, 28 Sep 2021 09:56:00 -0700 Subject: [PATCH 2/4] Add colorUpdated call --- wled00/ir.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/wled00/ir.cpp b/wled00/ir.cpp index ca99e4d6..2a04458b 100644 --- a/wled00/ir.cpp +++ b/wled00/ir.cpp @@ -618,7 +618,8 @@ void decodeIRJson(uint32_t code) cmdStr = "win&" + cmdStr; } handleSet(nullptr, cmdStr, false); - } + } + colorUpdated(CALL_MODE_BUTTON); } else if (!jsonCmdObj.isNull()) { // command is JSON object //allow applyPreset() to reuse JSON buffer, or it would alloc. a second buffer and run out of mem. From dc9dedf220da510a3742bbe63334fba5e1d40392 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Kristan?= Date: Thu, 30 Sep 2021 16:30:44 +0200 Subject: [PATCH 3/4] Fixed pin reservations. (#2214) * Fixed pin reservations. Added ethernet pin reservations. Minor tweaks in usermods. * Optional ADA compile (not default, free GPIO3 use) * Move ethernet definitions Remove pin 3 used check Co-authored-by: cschwinne --- usermods/JSON_IR_remote/readme.md | 66 +- .../usermod_PIR_sensor_switch.h | 818 +++++++++--------- usermods/Temperature/usermod_temperature.h | 4 +- usermods/multi_relay/usermod_multi_relay.h | 30 +- .../usermod_v2_four_line_display.h | 23 +- wled00/wled_ethernet.h | 46 +- wled00/xml.cpp | 129 ++- 7 files changed, 593 insertions(+), 523 deletions(-) diff --git a/usermods/JSON_IR_remote/readme.md b/usermods/JSON_IR_remote/readme.md index ee18aa94..2cba06ed 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 2978b491..6682dde3 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 c4229047..7c209f47 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 cd3e01c7..c3d55d66 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 4332ef56..6c4df32a 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 6ef33412..6997c98f 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()); From 17c20276a9641c31ff7e578e1d54766b79d0942d Mon Sep 17 00:00:00 2001 From: cschwinne Date: Fri, 1 Oct 2021 20:26:23 +0200 Subject: [PATCH 4/4] Make sbuff local This should save 4 bytes per ESPAsyncE131 instance --- wled00/src/dependencies/e131/ESPAsyncE131.cpp | 2 +- wled00/src/dependencies/e131/ESPAsyncE131.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/wled00/src/dependencies/e131/ESPAsyncE131.cpp b/wled00/src/dependencies/e131/ESPAsyncE131.cpp index 8ba97106..b4065b06 100644 --- a/wled00/src/dependencies/e131/ESPAsyncE131.cpp +++ b/wled00/src/dependencies/e131/ESPAsyncE131.cpp @@ -101,7 +101,7 @@ void ESPAsyncE131::parsePacket(AsyncUDPPacket _packet) { bool error = false; uint8_t protocol = P_E131; - sbuff = reinterpret_cast(_packet.data()); + e131_packet_t *sbuff = reinterpret_cast(_packet.data()); //E1.31 packet identifier ("ACS-E1.17") if (memcmp(sbuff->acn_id, ESPAsyncE131::ACN_ID, sizeof(sbuff->acn_id))) diff --git a/wled00/src/dependencies/e131/ESPAsyncE131.h b/wled00/src/dependencies/e131/ESPAsyncE131.h index e9ce5c12..4cf522d8 100644 --- a/wled00/src/dependencies/e131/ESPAsyncE131.h +++ b/wled00/src/dependencies/e131/ESPAsyncE131.h @@ -163,7 +163,6 @@ class ESPAsyncE131 { static const uint32_t VECTOR_FRAME = 2; static const uint8_t VECTOR_DMP = 2; - e131_packet_t *sbuff; // Pointer to scratch packet buffer AsyncUDP udp; // AsyncUDP // Internal Initializers