From 69a826f8968e25d9bda391102a36ef6c9dd24b3d Mon Sep 17 00:00:00 2001 From: gegu Date: Sun, 14 Jun 2020 12:54:35 +0200 Subject: [PATCH] Usermods: PIR sensor switch, v2 unreachable net services --- .../Fix_unreachable_netservices_v2/readme.md | 54 ++++ .../usermod_Fix_unreachable_netservices.h | 138 ++++++++++ usermods/PIR_sensor_switch/readme.md | 75 ++++++ .../usermod_PIR_sensor_switch.h | 249 ++++++++++++++++++ wled00/const.h | 2 + 5 files changed, 518 insertions(+) create mode 100644 usermods/Fix_unreachable_netservices_v2/readme.md create mode 100644 usermods/Fix_unreachable_netservices_v2/usermod_Fix_unreachable_netservices.h create mode 100644 usermods/PIR_sensor_switch/readme.md create mode 100644 usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h diff --git a/usermods/Fix_unreachable_netservices_v2/readme.md b/usermods/Fix_unreachable_netservices_v2/readme.md new file mode 100644 index 00000000..f7d2aed6 --- /dev/null +++ b/usermods/Fix_unreachable_netservices_v2/readme.md @@ -0,0 +1,54 @@ +# Fix unreachable net services V2 + +This usermod-v2 modification performs a ping request to the local IP address every 60 seconds. By this procedure the net services of WLED remains accessible in some problematic WLAN environments. + +The modification works with static or DHCP IP address configuration. + +**Webinterface**: The number of pings and reconnects is displayed on the info page in the web interface. + +_Story:_ + +Unfortunately, with all ESP projects where a web server or other network services are running, I have the problem that after some time the web server is no longer accessible. Now I found out that the connection is at least reestablished when a ping request is executed by the device. + +With this modification, in the worst case, the network functions are not available for 60 seconds until the next ping request. + +## Installation + +1. Copy the file `usermod_Fix_unreachable_netservices.h` to the `wled00` directory. +2. Register the usermod by adding `#include "usermod_Fix_unreachable_netservices.h"` in the top and `registerUsermod(new FixUnreachableNetServices());` in the bottom of `usermods_list.cpp`. + +Example **usermods_list.cpp**: + +```cpp +#include "wled.h" +/* + * Register your v2 usermods here! + * (for v1 usermods using just usermod.cpp, you can ignore this file) + */ + +/* + * Add/uncomment your usermod filename here (and once more below) + * || || || + * \/ \/ \/ + */ +//#include "usermod_v2_example.h" +//#include "usermod_temperature.h" +//#include "usermod_v2_empty.h" +#include "usermod_Fix_unreachable_netservices.h" + +void registerUsermods() +{ + /* + * Add your usermod class name here + * || || || + * \/ \/ \/ + */ + //usermods.add(new MyExampleUsermod()); + //usermods.add(new UsermodTemperature()); + //usermods.add(new UsermodRenameMe()); + usermods.add(new FixUnreachableNetServices()); + +} +``` + +Hopefully I can help someone with that - @gegu diff --git a/usermods/Fix_unreachable_netservices_v2/usermod_Fix_unreachable_netservices.h b/usermods/Fix_unreachable_netservices_v2/usermod_Fix_unreachable_netservices.h new file mode 100644 index 00000000..8ffc821e --- /dev/null +++ b/usermods/Fix_unreachable_netservices_v2/usermod_Fix_unreachable_netservices.h @@ -0,0 +1,138 @@ +#pragma once + +#include "wled.h" +#include + +/* + * This usermod performs a ping request to the local IP address every 60 seconds. + * By this procedure the net services of WLED remains accessible in some problematic WLAN environments. + * + * 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 FixUnreachableNetServices : public Usermod { + private: + //Private class members. You can declare variables and functions only accessible to your usermod here + unsigned long m_lastTime = 0; + + // desclare required variables + const unsigned int PingDelayMs = 60000; + unsigned long m_connectedWiFi = 0; + ping_option m_pingOpt; + unsigned int m_pingCount = 0; + + 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() { + //Serial.println("Hello from my usermod!"); + } + + + /* + * connected() is called every time the WiFi is (re)connected + * Use it to initialize network interfaces + */ + void connected() { + //Serial.println("Connected to WiFi!"); + + ++m_connectedWiFi; + + // initialize ping_options structure + memset(&m_pingOpt, 0, sizeof(struct ping_option)); + m_pingOpt.count = 1; + m_pingOpt.ip = WiFi.localIP(); + + } + + + /* + * loop() is called continuously. Here you can check for events, read sensors, etc. + * + * Tips: + * 1. You can use "if (WLED_CONNECTED)" to check for a successful network connection. + * Additionally, "if (WLED_MQTT_CONNECTED)" is available to check for a connection to an MQTT broker. + * + * 2. Try to avoid using the delay() function. NEVER use delays longer than 10 milliseconds. + * Instead, use a timer check as shown here. + */ + void loop() { + if (m_connectedWiFi > 0 && millis()-m_lastTime > PingDelayMs) + { + ping_start(&m_pingOpt); + m_lastTime = millis(); + ++m_pingCount; + } + } + + + /* + * addToJsonInfo() can be used to add custom entries to the /json/info part of the JSON API. + * Creating an "u" object allows you to add custom key/value pairs to the Info section of the WLED web UI. + * Below it is shown how this could be used for e.g. a light sensor + */ + void addToJsonInfo(JsonObject& root) + { + //this code adds "u":{"⚡ Ping fix pings": m_pingCount} to the info object + JsonObject user = root["u"]; + if (user.isNull()) user = root.createNestedObject("u"); + + JsonArray infoArr = user.createNestedArray("⚡ Ping fix pings"); //name + infoArr.add(m_pingCount); //value + + //this code adds "u":{"⚡ Reconnects": m_connectedWiFi - 1} to the info object + infoArr = user.createNestedArray("⚡ Reconnects"); //name + infoArr.add(m_connectedWiFi - 1); //value + } + + + /* + * 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) + { + //root["user0"] = userVar0; + } + + + /* + * 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) + { + //userVar0 = root["user0"] | userVar0; //if "user0" key exists in JSON, update, else keep old value + //if (root["bri"] == 255) Serial.println(F("Don't burn down your garage!")); + } + + + /* + * 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_FIXNETSERVICES; + } + + //More methods can be added in the future, this example will then be extended. + //Your usermod will remain compatible as it does not need to implement all methods from the Usermod base class! +}; diff --git a/usermods/PIR_sensor_switch/readme.md b/usermods/PIR_sensor_switch/readme.md new file mode 100644 index 00000000..447f541c --- /dev/null +++ b/usermods/PIR_sensor_switch/readme.md @@ -0,0 +1,75 @@ +# PIR sensor switch + +This usermod-v2 modification allows the connection of a PIR sensor to switch on the LED strip when motion is detected. The switch-off occurs ten minutes after no more motion is detected. + +_Story:_ + +I use the PIR Sensor to automatically turn on the WLED analog clock in my home office room when I am there. +The LED strip is switched [using a relay](https://github.com/Aircoookie/WLED/wiki/Control-a-relay-with-WLED) to keep the power consumption low when it is switched off. + +## Webinterface + +The info page in the web interface shows the items below + +- the state of the sensor. By clicking on the state the sensor can be deactivated/activated. +**I recommend to deactivate the sensor before installing an OTA update**. +- the remaining time of the off timer. + +## JSON API + +The usermod supports the following state changes: + +| JSON key | Value range | Description | +|------------|-------------|---------------------------------| +| PIRenabled | bool | Deactivdate/activate the sensor | +| PIRoffSec | 60 to 43200 | Off timer seconds | + +## Sensor connection + +My setup uses an HC-SR501 sensor, a HC-SR505 should also work. + +The usermod uses GPIO13 (D1 mini pin D7) for the sensor signal. +[This example page](http://www.esp8266learning.com/wemos-mini-pir-sensor-example.php) describes how to connect the sensor. + +Use the potentiometers on the sensor to set the time-delay to the minimum and the sensitivity to about half, or slightly above. + +## Usermod installation + +1. Copy the file `usermod_PIR_sensor_switch.h` to the `wled00` directory. +2. Register the usermod by adding `#include "usermod_PIR_sensor_switch.h"` in the top and `registerUsermod(new PIRsensorSwitch());` in the bottom of `usermods_list.cpp`. + +Example **usermods_list.cpp**: + +```cpp +#include "wled.h" +/* + * Register your v2 usermods here! + * (for v1 usermods using just usermod.cpp, you can ignore this file) + */ + +/* + * Add/uncomment your usermod filename here (and once more below) + * || || || + * \/ \/ \/ + */ +//#include "usermod_v2_example.h" +//#include "usermod_temperature.h" +//#include "usermod_v2_empty.h" +#include "usermod_PIR_sensor_switch.h" + +void registerUsermods() +{ + /* + * Add your usermod class name here + * || || || + * \/ \/ \/ + */ + //usermods.add(new MyExampleUsermod()); + //usermods.add(new UsermodTemperature()); + //usermods.add(new UsermodRenameMe()); + usermods.add(new PIRsensorSwitch()); + +} +``` + +Have fun - @gegu diff --git a/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h b/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h new file mode 100644 index 00000000..aa76147c --- /dev/null +++ b/usermods/PIR_sensor_switch/usermod_PIR_sensor_switch.h @@ -0,0 +1,249 @@ +#pragma once + +#include "wled.h" + +/* + * 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 { + private: + // PIR sensor pin + const uint8_t PIRsensorPin = 13; // D7 on D1 mini + // notification mode for colorUpdated() + const byte NotifyUpdateMode = NOTIFIER_CALL_MODE_NO_NOTIFY; // NOTIFIER_CALL_MODE_DIRECT_CHANGE + // delay before switch off after the sensor state goes LOW + uint32_t m_switchOffDelay = 120000; + // off timer start time + uint32_t m_offTimerStart = 0; + // current PIR sensor pin state + byte m_PIRsensorPinState = LOW; + // PIR sensor enabled - ISR attached + bool m_PIRenabled = true; + + /* + * return or change if new PIR sensor state is available + */ + static volatile bool newPIRsensorState(bool changeState = false, bool newState = false) { + static volatile bool s_PIRsensorState = false; + if (changeState) { + s_PIRsensorState = newState; + } + return s_PIRsensorState; + } + + /* + * PIR sensor state has changed + */ + static void IRAM_ATTR ISR_PIRstateChange() { + newPIRsensorState(true, true); + } + + /* + * switch strip on/off + */ + void switchStrip(bool switchOn) { + if (switchOn && bri == 0) { + bri = briLast; + colorUpdated(NotifyUpdateMode); + } + else if (!switchOn && bri != 0) { + briLast = bri; + bri = 0; + colorUpdated(NotifyUpdateMode); + } + } + + /* + * Read and update PIR sensor state. + * Initilize/reset switch off timer + */ + bool updatePIRsensorState() { + if (newPIRsensorState()) { + m_PIRsensorPinState = digitalRead(PIRsensorPin); + + if (m_PIRsensorPinState == HIGH) { + m_offTimerStart = 0; + switchStrip(true); + } + else if (bri != 0) { + // start switch off timer + m_offTimerStart = millis(); + } + newPIRsensorState(true, false); + return true; + } + return false; + } + + /* + * switch off the strip if the delay has elapsed + */ + bool handleOffTimer() { + if (m_offTimerStart > 0 && millis() - m_offTimerStart > m_switchOffDelay) { + switchStrip(false); + 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() { + // PIR Sensor mode INPUT_PULLUP + pinMode(PIRsensorPin, INPUT_PULLUP); + // assign interrupt function and set CHANGE mode + attachInterrupt(digitalPinToInterrupt(PIRsensorPin), ISR_PIRstateChange, CHANGE); + } + + + /* + * 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() { + 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) + { + //this code adds "u":{"⏲ PIR sensor state":uiDomString} to the info object + // the value contains a button to toggle the sensor enabled/disabled + JsonObject user = root["u"]; + if (user.isNull()) user = root.createNestedObject("u"); + + JsonArray infoArr = user.createNestedArray("⏲ PIR sensor state"); //name + String uiDomString = ""; + infoArr.add(uiDomString); //value + + //this code adds "u":{"⏲ switch off timer":uiDomString} to the info object + infoArr = user.createNestedArray("⏲ switch off timer"); //name + + // off timer + if (m_offTimerStart > 0) { + uiDomString = ""; + unsigned int offSeconds = (m_switchOffDelay - (millis() - m_offTimerStart)) / 1000; + if (offSeconds >= 3600) { + uiDomString += (offSeconds / 3600); + uiDomString += " hours "; + offSeconds %= 3600; + } + if (offSeconds >= 60) { + uiDomString += (offSeconds / 60); + offSeconds %= 60; + } else if (uiDomString.length() > 0){ + uiDomString += 0; + } + if (uiDomString.length() > 0){ + uiDomString += " min "; + } + uiDomString += (offSeconds); + infoArr.add(uiDomString + " sec"); + } else { + infoArr.add("inactive"); + } + } + + + /* + * 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 + * Add "PIRenabled" to json state. This can be used to disable/enable the sensor. + * Add "PIRoffSec" to json state. This can be used to adjust milliseconds . + */ + void addToJsonState(JsonObject& root) + { + root["PIRenabled"] = m_PIRenabled; + root["PIRoffSec"] = (m_switchOffDelay / 1000); + } + + + /* + * 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 + * Read "PIRenabled" from json state and switch enable/disable the PIR sensor. + * Read "PIRoffSec" from json state and adjust milliseconds . + */ + void readFromJsonState(JsonObject& root) + { + if (root["PIRoffSec"] != nullptr) { + m_switchOffDelay = (1000 * max(60UL, min(43200UL, root["PIRoffSec"].as()))); + } + + if (root["PIRenabled"] != nullptr) { + if (root["PIRenabled"] && !m_PIRenabled) { + attachInterrupt(digitalPinToInterrupt(PIRsensorPin), ISR_PIRstateChange, CHANGE); + newPIRsensorState(true, true); + } + else if(m_PIRenabled) { + detachInterrupt(PIRsensorPin); + } + m_PIRenabled = root["PIRenabled"]; + } + } + + + /* + * 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; + } + + //More methods can be added in the future, this example will then be extended. + //Your usermod will remain compatible as it does not need to implement all methods from the Usermod base class! +}; diff --git a/wled00/const.h b/wled00/const.h index ba905f0d..34e42f8a 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -18,6 +18,8 @@ #define USERMOD_ID_UNSPECIFIED 1 //Default value for a general user mod that does not specify a custom ID #define USERMOD_ID_EXAMPLE 2 //Usermod "usermod_v2_example.h" #define USERMOD_ID_TEMPERATURE 3 //Usermod "usermod_temperature.h" +#define USERMOD_ID_FIXNETSERVICES 4 //Usermod "usermod_Fix_unreachable_netservices.h" +#define USERMOD_ID_PIRSWITCH 5 //Usermod "usermod_PIR_sensor_switch.h" //Access point behavior #define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot