Merge pull request #992 from gegu/master

Usermods: PIR sensor switch, v2 unreachable net services
This commit is contained in:
Aircoookie 2020-06-14 21:11:54 +02:00 committed by GitHub
commit 9ab4b3f3f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 518 additions and 0 deletions

View File

@ -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

View File

@ -0,0 +1,138 @@
#pragma once
#include "wled.h"
#include <ping.h>
/*
* 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":{"&#x26A1; Ping fix pings": m_pingCount} to the info object
JsonObject user = root["u"];
if (user.isNull()) user = root.createNestedObject("u");
JsonArray infoArr = user.createNestedArray("&#x26A1; Ping fix pings"); //name
infoArr.add(m_pingCount); //value
//this code adds "u":{"&#x26A1; Reconnects": m_connectedWiFi - 1} to the info object
infoArr = user.createNestedArray("&#x26A1; 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!
};

View File

@ -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

View File

@ -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 = 600000;
// 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":{"&#x23F2; 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("&#x23F2; PIR sensor state"); //name
String uiDomString = "<button class=\"btn infobtn\" onclick=\"requestJson({PIRenabled:";
String sensorStateInfo;
// PIR sensor state
if (m_PIRenabled) {
uiDomString += "false";
sensorStateInfo = (m_PIRsensorPinState != LOW ? "active" : "inactive"); //value
} else {
uiDomString += "true";
sensorStateInfo = "Disabled !";
}
uiDomString += "});return false;\">";
uiDomString += sensorStateInfo;
uiDomString += "</button>";
infoArr.add(uiDomString); //value
//this code adds "u":{"&#x23F2; switch off timer":uiDomString} to the info object
infoArr = user.createNestedArray("&#x23F2; 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 <m_switchOffDelay> 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 <m_switchOffDelay> milliseconds .
*/
void readFromJsonState(JsonObject& root)
{
if (root["PIRoffSec"] != nullptr) {
m_switchOffDelay = (1000 * max(60UL, min(43200UL, root["PIRoffSec"].as<unsigned long>())));
}
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!
};

View File

@ -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