Merge branch 'master' into preset-number

This commit is contained in:
Aircoookie 2020-12-13 23:40:34 +01:00 committed by GitHub
commit 220757be06
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 706 additions and 403 deletions

View File

@ -1,6 +1,28 @@
## WLED changelog
### WLED version 0.11.0
### Development versions after 0.11.0 release
#### Build 2012130
- Fixed RGBW mode not saved between reboots (fixes #1457)
- Added brightness scaling in palette function for default (PR #1484)
-
#### Build 2012101
- Fixed preset cycle default duration rounded down to nearest 10sec interval (#1458)
- Enabled E1.31/DDP/Art-Net in AP mode
#### Build 2012100
- Fixed multi-segment preset cycle
- Fixed EEPROM (pre-0.11 settings) not cleared on factory reset
- Fixed an issue with intermittent crashes on FX change (PR #1465)
- Added function to know if strip is updating (PR #1466)
- Fixed using colorwheel sliding the UI (PR #1459)
- Fixed analog clock settings not saving (PR #1448)
- Added Temperature palette (PR #1430)
- Added Candy cane FX (PR #1445)
#### Build 2012020
@ -11,6 +33,8 @@
- Fixed compilation for analog (PWM) LEDs
### WLED version 0.11.0
#### Build 2011290
- Release of WLED 0.11.0 "Mirai"

5
images/Readme.md Normal file
View File

@ -0,0 +1,5 @@
### Additional Logos
Additional awesome logos for WLED can be found here [Aircoookie/Akemi](https://github.com/Aircoookie/Akemi).
<img src="https://github.com/Aircoookie/Akemi/blob/master/akemi/001_cheerful.png">

8
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "wled",
"version": "0.10.2",
"version": "0.11.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -958,9 +958,9 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"ini": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
"integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
},
"inliner": {
"version": "1.13.1",

View File

@ -1,17 +1,32 @@
# Fix unreachable net services V2
**Attention: This usermod compiles only for ESP8266**
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.
## Webinterface
The number of pings and reconnects is displayed on the info page in the web interface.
The ping delay can be changed. Changes persist after a reboot.
## JSON API
The usermod supports the following state changes:
| JSON key | Value range | Description |
|-------------|------------------|---------------------------------|
| PingDelayMs | 5000 to 18000000 | Deactivdate/activate the sensor |
Changes also persist after a reboot.
## Installation
1. Copy the file `usermod_Fix_unreachable_netservices.h` to the `wled00` directory.

View File

@ -1,6 +1,14 @@
#pragma once
#include "wled.h"
#if defined(ESP32)
#warning "Usermod FixUnreachableNetServices works only with ESP8266 builds"
class FixUnreachableNetServices : public Usermod
{
};
#endif
#if defined(ESP8266)
#include <ping.h>
/*
@ -23,116 +31,138 @@
* 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;
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;
// declare required variables
unsigned long m_pingDelayMs = 60000;
unsigned long m_connectedWiFi = 0;
ping_option m_pingOpt;
unsigned int m_pingCount = 0;
bool m_updateConfig = false;
public:
//Functions called by WLED
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!");
}
/**
* 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!");
/*
* 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;
++m_connectedWiFi;
// initialize ping_options structure
memset(&m_pingOpt, 0, sizeof(struct ping_option));
m_pingOpt.count = 1;
m_pingOpt.ip = WiFi.localIP();
}
// 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)
/**
* loop
*/
void loop()
{
if (m_connectedWiFi > 0 && millis() - m_lastTime > m_pingDelayMs)
{
//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
ping_start(&m_pingOpt);
m_lastTime = millis();
++m_pingCount;
}
/*
* 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)
if (m_updateConfig)
{
//root["user0"] = userVar0;
serializeConfig();
m_updateConfig = false;
}
}
/**
* 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");
/*
* 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)
String uiDomString = "&#x26A1; Ping fix pings<span style=\"display:block;padding-left:25px;\">\
Delay <input type=\"number\" min=\"5\" max=\"300\" value=\"";
uiDomString += (unsigned long)(m_pingDelayMs / 1000);
uiDomString += "\" onchange=\"requestJson({PingDelay:parseInt(this.value)});\">sec</span>";
JsonArray infoArr = user.createNestedArray(uiDomString); //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["PingDelay"] = (m_pingDelayMs/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
*/
void readFromJsonState(JsonObject &root)
{
if (root["PingDelay"] != nullptr)
{
//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!"));
m_pingDelayMs = (1000 * max(1UL, min(300UL, root["PingDelay"].as<unsigned long>())));
m_updateConfig = true;
}
}
/**
* provide the changeable values
*/
void addToConfig(JsonObject &root)
{
JsonObject top = root.createNestedObject("FixUnreachableNetServices");
top["PingDelayMs"] = m_pingDelayMs;
}
/*
* 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;
}
/**
* restore the changeable values
*/
void readFromConfig(JsonObject &root)
{
JsonObject top = root["FixUnreachableNetServices"];
m_pingDelayMs = top["PingDelayMs"] | m_pingDelayMs;
m_pingDelayMs = max(5000UL, min(18000000UL, m_pingDelayMs));
}
//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!
/**
* 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;
}
};
#endif

View File

@ -1,17 +0,0 @@
# Fix unreachable Webserver
This modification performs a ping request to the local IP address every 60 seconds. By this procedure the web server remains accessible in some problematic WLAN environments.
The modification works with static or DHCP IP address configuration
_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
Copy and replace the file `usermod.cpp` in wled00 directory.

View File

@ -1,43 +0,0 @@
#include "wled.h"
/*
* This file allows you to add own functionality to WLED more easily
* See: https://github.com/Aircoookie/WLED/wiki/Add-own-functionality
* EEPROM bytes 2750+ are reserved for your custom use case. (if you extend #define EEPSIZE in const.h)
* bytes 2400+ are currently ununsed, but might be used for future wled features
*/
#include <ping.h>
const int PingDelayMs = 60000;
long lastCheckTime = 0;
bool connectedWiFi = false;
ping_option pingOpt;
//Use userVar0 and userVar1 (API calls &U0=,&U1=, uint16_t)
//gets called once at boot. Do all initialization that doesn't depend on network here
void userSetup()
{
}
//gets called every time WiFi is (re-)connected. Initialize own network interfaces here
void userConnected()
{
connectedWiFi = true;
// initialize ping_options structure
memset(&pingOpt, 0, sizeof(struct ping_option));
pingOpt.count = 1;
pingOpt.ip = WiFi.localIP();
}
//loop. You can use "if (WLED_CONNECTED)" to check for successful connection
void userLoop()
{
if (connectedWiFi && millis()-lastCheckTime > PingDelayMs)
{
ping_start(&pingOpt);
lastCheckTime = millis();
}
}

View File

@ -11,19 +11,21 @@ The LED strip is switched [using a relay](https://github.com/Aircoookie/WLED/wik
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 state of the sensor. By clicking on the state the sensor can be deactivated/activated. Changes persist after a reboot.
**I recommend to deactivate the sensor before an OTA update and activate it again afterwards**.
- the remaining time of the off timer.
## JSON API
The usermod supports the following state changes:
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 |
Changes also persist after a reboot.
## Sensor connection
My setup uses an HC-SR501 sensor, a HC-SR505 should also work.
@ -55,7 +57,7 @@ Example **usermods_list.cpp**:
//#include "usermod_v2_example.h"
//#include "usermod_temperature.h"
//#include "usermod_v2_empty.h"
#include "usermod_PIR_sensor_switch.h"
#include "usermod_PIR_sensor_switch.h"
void registerUsermods()
{
@ -72,26 +74,36 @@ void registerUsermods()
}
```
## Usermod installation (advanced mode)
## API to enable/disable the PIR sensor from outside. For example from another usermod.
In this mode IR sensor will disable PIR when light ON by remote controller and enable PIR when light OFF.
The class provides the static method `PIRsensorSwitch* PIRsensorSwitch::GetInstance()` to get a pointer to the usermod object.
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`.
3. Add to the line 237, on `wled.h` in the `wled00` directory:
To query or change the PIR sensor state the methods `bool PIRsensorEnabled()` and `void EnablePIRsensor(bool enable)` are available.
`WLED_GLOBAL bool m_PIRenabled _INIT(true); // enable PIR sensor`
### There are two options to get access to the usermod instance:
4. On `ir.cpp` in the `wled00` directory, add to the IR controller's mapping (beyond line 200):
1. Include `usermod_PIR_sensor_switch.h` **before** you include the other usermod in `usermods_list.cpp'
- To the off button:
`m_PIRenabled = true;`
or
- To the on button:
`m_PIRenabled = false;`
2. Use `#include "usermod_PIR_sensor_switch.h"` at the top of the `usermod.h` where you need it.
5. Edit line 40, on `usermod_PIR_sensor_switch.h` in the `wled00` directory:
**Example usermod.h :**
```cpp
#include "wled.h"
`\\bool m_PIRenabled = true;`
#include "usermod_PIR_sensor_switch.h"
class MyUsermod : public Usermod {
//...
void togglePIRSensor() {
if (PIRsensorSwitch::GetInstance() != nullptr) {
PIRsensorSwitch::GetInstance()->EnablePIRsensor(!PIRsensorSwitch::GetInstance()->PIRsensorEnabled());
}
}
//...
};
```
Have fun - @gegu

View File

@ -24,228 +24,343 @@
* 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;
class PIRsensorSwitch : public Usermod
{
public:
/**
* constructor
*/
PIRsensorSwitch()
{
// set static instance pointer
PIRsensorSwitchInstance(this);
}
/**
* desctructor
*/
~PIRsensorSwitch()
{
PIRsensorSwitchInstance(nullptr, 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;
/**
* return the instance pointer of the class
*/
static PIRsensorSwitch *GetInstance() { return PIRsensorSwitchInstance(); }
/**
* Enable/Disable the PIR sensor
*/
void EnablePIRsensor(bool enable) { m_PIRenabled = enable; }
/**
* Get PIR sensor enabled/disabled state
*/
bool PIRsensorEnabled() { return m_PIRenabled; }
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;
// state if serializeConfig() should be called
bool m_updateConfig = false;
/**
* return or change if new PIR sensor state is available
*/
static volatile bool newPIRsensorState(bool changeState = false, bool newState = false);
/**
* PIR sensor state has changed
*/
static void IRAM_ATTR ISR_PIRstateChange();
/**
* Set/get instance pointer
*/
static PIRsensorSwitch *PIRsensorSwitchInstance(PIRsensorSwitch *pInstance = nullptr, bool bRemoveInstance = false);
/**
* switch strip on/off
*/
void switchStrip(bool switchOn)
{
if (switchOn && bri == 0)
{
bri = briLast;
colorUpdated(NotifyUpdateMode);
}
/*
* PIR sensor state has changed
*/
static void IRAM_ATTR ISR_PIRstateChange() {
newPIRsensorState(true, true);
else if (!switchOn && bri != 0)
{
briLast = bri;
bri = 0;
colorUpdated(NotifyUpdateMode);
}
}
/*
* 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);
/*
* 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) {
if (m_PIRenabled == true){
switchStrip(false);
}
if (m_PIRsensorPinState == HIGH)
{
m_offTimerStart = 0;
return true;
switchStrip(true);
}
return false;
else if (bri != 0)
{
// start switch off timer
m_offTimerStart = millis();
}
newPIRsensorState(true, false);
return true;
}
return false;
}
public:
//Functions called by WLED
/**
* switch off the strip if the delay has elapsed
*/
bool handleOffTimer()
{
if (m_offTimerStart > 0 && millis() - m_offTimerStart > m_switchOffDelay)
{
if (m_PIRenabled == true)
{
switchStrip(false);
}
m_offTimerStart = 0;
return true;
}
return false;
}
/*
* 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);
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);
if (m_PIRenabled)
{
// 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()
{
}
/*
* 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)
/**
* loop() is called continuously. Here you can check for events, read sensors, etc.
*/
void loop()
{
if (!updatePIRsensorState())
{
//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");
handleOffTimer();
if (m_updateConfig)
{
serializeConfig();
m_updateConfig = false;
}
}
}
/**
* 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");
/*
* 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)
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)
{
root["PIRenabled"] = m_PIRenabled;
root["PIRoffSec"] = (m_switchOffDelay / 1000);
uiDomString += "false";
sensorStateInfo = (m_PIRsensorPinState != LOW ? "active" : "inactive"); //value
}
/*
* 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)
else
{
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"];
}
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
uiDomString = "&#x23F2; switch off timer<span style=\"display:block;padding-left:25px;\">\
after <input type=\"number\" min=\"1\" max=\"720\" value=\"";
uiDomString += (m_switchOffDelay / 60000);
uiDomString += "\" onchange=\"requestJson({PIRoffSec:parseInt(this.value)*60});\">min</span>";
infoArr = user.createNestedArray(uiDomString); //name
/*
* 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()
// off timer
if (m_offTimerStart > 0)
{
return USERMOD_ID_PIRSWITCH;
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>())));
m_updateConfig = true;
}
//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!
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"];
m_updateConfig = true;
}
}
/**
* provide the changeable values
*/
void addToConfig(JsonObject &root)
{
JsonObject top = root.createNestedObject("PIRsensorSwitch");
top["PIRenabled"] = m_PIRenabled;
top["PIRoffSec"] = m_switchOffDelay;
}
/**
* restore the changeable values
*/
void readFromConfig(JsonObject &root)
{
JsonObject top = root["PIRsensorSwitch"];
m_PIRenabled = (top["PIRenabled"] != nullptr ? top["PIRenabled"] : true);
m_switchOffDelay = top["PIRoffSec"] | m_switchOffDelay;
}
/**
* 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;
}
};
//////////////////////////////////////////////////////
// PIRsensorSwitch static method implementations
volatile bool PIRsensorSwitch::newPIRsensorState(bool changeState, bool newState)
{
static volatile bool s_PIRsensorState = false;
if (changeState)
{
s_PIRsensorState = newState;
}
return s_PIRsensorState;
}
void IRAM_ATTR PIRsensorSwitch::ISR_PIRstateChange()
{
newPIRsensorState(true, true);
}
PIRsensorSwitch *PIRsensorSwitch::PIRsensorSwitchInstance(PIRsensorSwitch *pInstance, bool bRemoveInstance)
{
static PIRsensorSwitch *s_pPIRsensorSwitch = nullptr;
if (pInstance != nullptr || bRemoveInstance)
{
s_pPIRsensorSwitch = pInstance;
}
return s_pPIRsensorSwitch;
}

View File

@ -149,7 +149,6 @@ public:
void Show()
{
byte b;
switch (_type)
{
case NeoPixelType_Grb:
@ -191,6 +190,51 @@ public:
}
}
bool CanShow()
{
bool canShow = true;
switch (_type)
{
case NeoPixelType_Grb:
{
for (uint8_t idx = 0; idx < numStrips; idx++)
{
switch (idx)
{
case 0: canShow &= pGrb0->CanShow(); break;
case 1: canShow &= pGrb1->CanShow(); break;
case 2: canShow &= pGrb2->CanShow(); break;
case 3: canShow &= pGrb3->CanShow(); break;
case 4: canShow &= pGrb4->CanShow(); break;
case 5: canShow &= pGrb5->CanShow(); break;
case 6: canShow &= pGrb6->CanShow(); break;
case 7: canShow &= pGrb7->CanShow(); break;
}
}
break;
}
case NeoPixelType_Grbw:
{
for (uint8_t idx = 0; idx < numStrips; idx++)
{
switch (idx)
{
case 0: canShow &= pGrbw0->CanShow(); break;
case 1: canShow &= pGrbw1->CanShow(); break;
case 2: canShow &= pGrbw2->CanShow(); break;
case 3: canShow &= pGrbw3->CanShow(); break;
case 4: canShow &= pGrbw4->CanShow(); break;
case 5: canShow &= pGrbw5->CanShow(); break;
case 6: canShow &= pGrbw6->CanShow(); break;
case 7: canShow &= pGrbw7->CanShow(); break;
}
}
break;
}
}
return canShow;
}
void SetPixelColorRaw(uint16_t indexPixel, RgbwColor c)
{
// figure out which strip this pixel index is on

View File

@ -116,7 +116,7 @@
#define IS_REVERSE ((SEGMENT.options & REVERSE ) == REVERSE )
#define IS_SELECTED ((SEGMENT.options & SELECTED ) == SELECTED )
#define MODE_COUNT 114
#define MODE_COUNT 115
#define FX_MODE_STATIC 0
#define FX_MODE_BLINK 1
@ -317,9 +317,31 @@ class WS2812FX {
WS2812FX::_usedSegmentData -= _dataLen;
_dataLen = 0;
}
void reset(){next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0; deallocateData();}
/**
* If reset of this segment was request, clears runtime
* settings of this segment.
* Must not be called while an effect mode function is running
* because it could access the data buffer and this method
* may free that data buffer.
*/
void resetIfRequired() {
if (_requiresReset) {
next_time = 0; step = 0; call = 0; aux0 = 0; aux1 = 0;
deallocateData();
_requiresReset = false;
}
}
/**
* Flags that before the next effect is calculated,
* the internal segment state should be reset.
* Call resetIfRequired before calling the next effect function.
*/
void reset() { _requiresReset = true; }
private:
uint16_t _dataLen = 0;
bool _requiresReset = false;
} segment_runtime;
WS2812FX() {
@ -480,7 +502,9 @@ class WS2812FX {
gammaCorrectCol = true,
applyToAllSelected = true,
segmentsAreIdentical(Segment* a, Segment* b),
setEffectConfig(uint8_t m, uint8_t s, uint8_t i, uint8_t p);
setEffectConfig(uint8_t m, uint8_t s, uint8_t i, uint8_t p),
// return true if the strip is being sent pixel updates
isUpdating(void);
uint8_t
mainSegment = 0,

View File

@ -44,6 +44,10 @@ const uint16_t customMappingTable[] = {
const uint16_t customMappingSize = sizeof(customMappingTable)/sizeof(uint16_t); //30 in example
#endif
#ifndef PWM_INDEX
#define PWM_INDEX 0
#endif
void WS2812FX::init(bool supportWhite, uint16_t countPixels, bool skipFirst)
{
if (supportWhite == _useRgbw && countPixels == _length && _skipFirstMode == skipFirst) return;
@ -76,6 +80,11 @@ void WS2812FX::service() {
for(uint8_t i=0; i < MAX_NUM_SEGMENTS; i++)
{
_segment_index = i;
// reset the segment runtime data if needed, called before isActive to ensure deleted
// segment's buffers are cleared
SEGENV.resetIfRequired();
if (SEGMENT.isActive())
{
if(nowUp > SEGENV.next_time || _triggered || (doShow && SEGMENT.mode == 0)) //last is temporary
@ -218,7 +227,10 @@ void WS2812FX::setPixelColor(uint16_t i, byte r, byte g, byte b, byte w)
//you can set it to 0 if the ESP is powered by USB and the LEDs by external
void WS2812FX::show(void) {
if (_callback) _callback();
// avoid race condition, caputre _callback value
show_callback callback = _callback;
if (callback) callback();
//power limit calculation
//each LED can draw up 195075 "power units" (approx. 53mA)
@ -291,10 +303,24 @@ void WS2812FX::show(void) {
bus->SetBrightness(_brightness);
}
// some buses send asynchronously and this method will return before
// all of the data has been sent.
// See https://github.com/Makuna/NeoPixelBus/wiki/ESP32-NeoMethods#neoesp32rmt-methods
bus->Show();
_lastShow = millis();
}
/**
* Returns a true value if any of the strips are still being updated.
* On some hardware (ESP32), strip updates are done asynchronously.
*/
bool WS2812FX::isUpdating() {
return !bus->CanShow();
}
/**
* Forces the next frame to be computed on all active segments.
*/
void WS2812FX::trigger() {
_triggered = true;
}
@ -890,13 +916,24 @@ void WS2812FX::handle_palette(void)
*/
uint32_t WS2812FX::color_from_palette(uint16_t i, bool mapping, bool wrap, uint8_t mcol, uint8_t pbri)
{
if (SEGMENT.palette == 0 && mcol < 3) return SEGCOLOR(mcol); //WS2812FX default
if (SEGMENT.palette == 0 && mcol < 3) {
uint32_t color = SEGCOLOR(mcol);
if (pbri != 255) {
CRGB crgb_color = col_to_crgb(color);
crgb_color.nscale8_video(pbri);
return crgb_to_col(crgb_color);
} else {
return color;
}
}
uint8_t paletteIndex = i;
if (mapping) paletteIndex = (i*255)/(SEGLEN -1);
if (!wrap) paletteIndex = scale8(paletteIndex, 240); //cut off blend at palette "end"
CRGB fastled_col;
fastled_col = ColorFromPalette( currentPalette, paletteIndex, pbri, (paletteBlend == 3)? NOBLEND:LINEARBLEND);
return fastled_col.r*65536 + fastled_col.g*256 + fastled_col.b;
return crgb_to_col(fastled_col);
}
//@returns `true` if color, mode, speed, intensity and palette match
@ -924,7 +961,7 @@ void WS2812FX::setRgbwPwm(void) {
_analogLastShow = nowUp;
RgbwColor c;
uint32_t col = bus->GetPixelColorRgbw(0);
uint32_t col = bus->GetPixelColorRgbw(PWM_INDEX);
c.R = col >> 16; c.G = col >> 8; c.B = col; c.W = col >> 24;
byte b = getBrightness();

View File

@ -296,7 +296,6 @@ public:
void Show()
{
byte b;
switch (_type)
{
case NeoPixelType_Grb: _pGrb->Show(); break;
@ -304,6 +303,22 @@ public:
}
}
/**
* This will return true if enough time has passed since the last time Show() was called.
* This also means that calling Show() will not cause any undue waiting. If the method for
* the defined bus is hardware that sends asynchronously, then call CanShow() will let
* you know if it has finished sending the data from the last Show().
*/
bool CanShow()
{
switch (_type)
{
case NeoPixelType_Grb: return _pGrb->CanShow();
case NeoPixelType_Grbw: return _pGrbw->CanShow();
default: return true;
}
}
void SetPixelColor(uint16_t indexPixel, RgbwColor c)
{
RgbwColor col;

View File

@ -91,6 +91,7 @@ void deserializeConfig() {
CJSON(strip.ablMilliampsMax, hw_led[F("maxpwr")]);
CJSON(strip.milliampsPerLed, hw_led[F("ledma")]);
CJSON(strip.reverseMode, hw_led[F("rev")]);
CJSON(strip.rgbwMode, hw_led[F("rgbwm")]);
JsonObject hw_led_ins_0 = hw_led[F("ins")][0];
//bool hw_led_ins_0_en = hw_led_ins_0[F("en")]; // true
@ -162,7 +163,7 @@ void deserializeConfig() {
CJSON(presetCycleMax, def_cy[F("range")][1]);
tdd = def_cy[F("dur")] | -1;
if (tdd >= 0) presetCycleTime = tdd * 100;
if (tdd > 0) presetCycleTime = tdd;
JsonObject interfaces = doc["if"];
@ -251,7 +252,11 @@ void deserializeConfig() {
CJSON(countdownMode, ol[F("cntdwn")]);
overlayCurrent = overlayDefault;
JsonArray ol_cntdwn = ol[F("cntdwn")]; //[20,12,31,23,59,59]
CJSON(overlayMin, ol[F("min")]);
CJSON(overlayMax, ol[F("max")]);
CJSON(analogClock12pixel, ol[F("o12pix")]);
CJSON(analogClock5MinuteMarks, ol[F("o5m")]);
CJSON(analogClockSecondsTrail, ol[F("osec")]);
//timed macro rules
JsonObject tm = doc[F("timers")];
@ -378,6 +383,7 @@ void serializeConfig() {
hw_led[F("maxpwr")] = strip.ablMilliampsMax;
hw_led[F("ledma")] = strip.milliampsPerLed;
hw_led[F("rev")] = strip.reverseMode;
hw_led[F("rgbwm")] = strip.rgbwMode;
JsonArray hw_led_ins = hw_led.createNestedArray("ins");
@ -478,7 +484,7 @@ void serializeConfig() {
JsonArray def_cy_range = def_cy.createNestedArray("range");
def_cy_range.add(presetCycleMin);
def_cy_range.add(presetCycleMax);
def_cy[F("dur")] = presetCycleTime / 100;
def_cy[F("dur")] = presetCycleTime;
}
JsonObject interfaces = doc.createNestedObject("if");
@ -562,6 +568,12 @@ void serializeConfig() {
ol[F("clock")] = overlayDefault;
ol[F("cntdwn")] = countdownMode;
ol[F("min")] = overlayMin;
ol[F("max")] = overlayMax;
ol[F("o12pix")] = analogClock12pixel;
ol[F("o5m")] = analogClock5MinuteMarks;
ol[F("osec")] = analogClockSecondsTrail;
JsonObject timers = doc.createNestedObject("timers");
JsonObject cntdwn = timers.createNestedObject("cntdwn");

View File

@ -2453,10 +2453,23 @@ let iSlide = 0, x0 = null, scrollS = 0, locked = false, w;
function unify(e) { return e.changedTouches ? e.changedTouches[0] : e }
function hasIroClass(classList) {
for (var i = 0; i < classList.length; i++) {
var element = classList[i];
if (element.startsWith('Iro')) return true;
}
return false;
}
function lock(e) {
if (pcMode) return;
var l = e.target.classList;
if (l.contains('noslide') || l.contains('iro__wheel__saturation') || l.contains('iro__slider__value') || l.contains('iro__slider')) return;
var pl = e.target.parentElement.classList;
if (l.contains('noslide') || hasIroClass(l) || hasIroClass(pl)) return;
x0 = unify(e).clientX;
scrollS = d.getElementsByClassName("tabcontent")[iSlide].scrollTop;

View File

@ -235,6 +235,7 @@ void userLoop();
void applyMacro(byte index);
void deEEP();
void deEEPSettings();
void clearEEPROM();
//wled_serial.cpp
void handleSerial();

View File

@ -9,7 +9,7 @@
// Autogenerated from wled00/data/index.htm, do not edit!!
const uint16_t PAGE_index_L = 34183;
const uint8_t PAGE_index[] PROGMEM = {
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x0a, 0xdc, 0xbd, 0x69, 0x7b, 0xe2, 0x48,
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0xdc, 0xbd, 0x69, 0x7b, 0xe2, 0x48,
0xd2, 0x28, 0xfa, 0xfd, 0xfc, 0x0a, 0x8a, 0x9a, 0xa9, 0x46, 0x85, 0x00, 0xb1, 0x1a, 0xe3, 0xa2,
0x7d, 0xc4, 0x66, 0x63, 0x1b, 0x6c, 0x56, 0x1b, 0xd7, 0x78, 0xe6, 0x11, 0x42, 0x80, 0x8c, 0x90,
0xb0, 0x24, 0x56, 0xdb, 0xf7, 0xb7, 0xdf, 0x88, 0xcc, 0xd4, 0xc2, 0x66, 0x57, 0xf5, 0xdb, 0x73,

View File

@ -107,7 +107,7 @@ void colorUpdated(int callMode)
notify(callMode);
//set flag to update blynk and mqtt
if (callMode != NOTIFIER_CALL_MODE_PRESET_CYCLE) interfaceUpdateCallMode = callMode;
interfaceUpdateCallMode = callMode;
} else {
if (nightlightActive && !nightlightActiveOld &&
callMode != NOTIFIER_CALL_MODE_NOTIFICATION &&
@ -303,10 +303,10 @@ void handleNightlight()
if (bri == 0 || nightlightActive) return;
if (presetCycCurr < presetCycleMin || presetCycCurr > presetCycleMax) presetCycCurr = presetCycleMin;
applyPreset(presetCycCurr);
applyPreset(presetCycCurr); //this handles colorUpdated() for us
presetCycCurr++;
if (presetCycCurr > 250) presetCycCurr = 1;
colorUpdated(NOTIFIER_CALL_MODE_PRESET_CYCLE);
interfaceUpdateCallMode = 0; //disable updates to MQTT and Blynk
}
}

View File

@ -266,6 +266,7 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
if (request->hasArg(F("RS"))) //complete factory reset
{
WLED_FS.format();
clearEEPROM();
serveMessage(request, 200, F("All Settings erased."), F("Connect to WLED-AP to setup again"),255);
doReboot = true;
}

View File

@ -377,6 +377,7 @@ void WLED::initAP(bool resetAP)
if (udpPort2 > 0 && udpPort2 != ntpLocalPort && udpPort2 != udpPort && udpPort2 != udpRgbPort) {
udp2Connected = notifier2Udp.begin(udpPort2);
}
e131.begin(false, e131Port, e131Universe, E131_MAX_UNIVERSE_COUNT);
dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
dnsServer.start(53, "*", WiFi.softAPIP());

View File

@ -8,7 +8,7 @@
*/
// version code in format yymmddb (b = daily build)
#define VERSION 2012020
#define VERSION 2012130
//uncomment this if you have a "my_config.h" file you'd like to use
//#define WLED_USE_MY_CONFIG

View File

@ -36,6 +36,20 @@
//21-> 0.10.1p
//22-> 2009260
/*
* Erase all (pre 0.11) configuration data on factory reset
*/
void clearEEPROM()
{
EEPROM.begin(EEPSIZE);
for (int i = 0; i < EEPSIZE; i++)
{
EEPROM.write(i, 0);
}
EEPROM.end();
}
void readStringFromEEPROM(uint16_t pos, char* str, uint16_t len)
{
for (int i = 0; i < len; ++i)