PIR sensor usermod pin reservation.

Time settings UI tweaks.
This commit is contained in:
Blaz Kristan 2021-03-10 18:46:13 +01:00
parent cb38976162
commit f91384596c
4 changed files with 100 additions and 34 deletions

View File

@ -2,6 +2,15 @@
#include "wled.h" #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. * This usermod handles PIR sensor states.
* The strip will be switched on and the off timer will be resetted when the sensor goes HIGH. * The strip will be switched on and the off timer will be resetted when the sensor goes HIGH.
@ -24,6 +33,9 @@
* 2. Register the usermod by adding #include "usermod_filename.h" in the top and registerUsermod(new MyUsermodClass()) in the bottom of usermods_list.cpp * 2. Register the usermod by adding #include "usermod_filename.h" in the top and registerUsermod(new MyUsermodClass()) in the bottom of usermods_list.cpp
*/ */
// MQTT topic for sensor values
const char MQTT_TOPIC[] = "/motion";
class PIRsensorSwitch : public Usermod class PIRsensorSwitch : public Usermod
{ {
public: public:
@ -60,7 +72,7 @@ public:
private: private:
// PIR sensor pin // PIR sensor pin
const uint8_t PIRsensorPin = 13; // D7 on D1 mini int8_t PIRsensorPin = PIR_SENSOR_PIN;
// notification mode for colorUpdated() // notification mode for colorUpdated()
const byte NotifyUpdateMode = NOTIFIER_CALL_MODE_NO_NOTIFY; // NOTIFIER_CALL_MODE_DIRECT_CHANGE const byte NotifyUpdateMode = NOTIFIER_CALL_MODE_NO_NOTIFY; // NOTIFIER_CALL_MODE_DIRECT_CHANGE
// delay before switch off after the sensor state goes LOW // delay before switch off after the sensor state goes LOW
@ -107,6 +119,17 @@ private:
} }
} }
void publishMqtt(const char* state)
{
//Check if MQTT Connected, otherwise it will crash the 8266
if (mqtt != nullptr){
char subuf[64];
strcpy(subuf, mqttDeviceTopic);
strcat(subuf, MQTT_TOPIC);
mqtt->publish(subuf, 0, true, state);
}
}
/** /**
* Read and update PIR sensor state. * Read and update PIR sensor state.
* Initilize/reset switch off timer * Initilize/reset switch off timer
@ -121,6 +144,7 @@ private:
{ {
m_offTimerStart = 0; m_offTimerStart = 0;
switchStrip(true); switchStrip(true);
publishMqtt("on");
} }
else if (bri != 0) else if (bri != 0)
{ {
@ -143,6 +167,7 @@ private:
if (m_PIRenabled == true) if (m_PIRenabled == true)
{ {
switchStrip(false); switchStrip(false);
publishMqtt("off");
} }
m_offTimerStart = 0; m_offTimerStart = 0;
return true; return true;
@ -159,12 +184,19 @@ public:
*/ */
void setup() void setup()
{ {
// PIR Sensor mode INPUT_PULLUP // pin retrieved from cfg.json (readFromConfig()) prior to running setup()
pinMode(PIRsensorPin, INPUT_PULLUP); if (!pinManager.allocatePin(PIRsensorPin,false)) {
if (m_PIRenabled) PIRsensorPin = -1; // allocation failed
{ m_PIRenabled = false;
// assign interrupt function and set CHANGE mode DEBUG_PRINTLN(F("PIRSensorSwitch pin allocation failed."));
attachInterrupt(digitalPinToInterrupt(PIRsensorPin), ISR_PIRstateChange, CHANGE); } else {
// 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);
}
} }
} }
@ -273,8 +305,8 @@ after <input type=\"number\" min=\"1\" max=\"720\" value=\"";
*/ */
void addToJsonState(JsonObject &root) void addToJsonState(JsonObject &root)
{ {
root["PIRenabled"] = m_PIRenabled; root[F("PIRenabled")] = m_PIRenabled;
root["PIRoffSec"] = (m_switchOffDelay / 1000); root[F("PIRoffSec")] = (m_switchOffDelay / 1000);
} }
/** /**
@ -285,15 +317,37 @@ after <input type=\"number\" min=\"1\" max=\"720\" value=\"";
*/ */
void readFromJsonState(JsonObject &root) void readFromJsonState(JsonObject &root)
{ {
if (root["PIRoffSec"] != nullptr) if (root[F("PIRoffSec")] != nullptr)
{ {
m_switchOffDelay = (1000 * max(60UL, min(43200UL, root["PIRoffSec"].as<unsigned long>()))); m_switchOffDelay = (1000 * max(60UL, min(43200UL, root[F("PIRoffSec")].as<unsigned long>())));
m_updateConfig = true; m_updateConfig = true;
} }
if (root["PIRenabled"] != nullptr) if (root[F("pin")] != nullptr)
{ {
if (root["PIRenabled"] && !m_PIRenabled) int8_t pin = (int)root[F("pin")];
// check if pin is OK
if (pin != PIRsensorPin && pin>=0 && pinManager.allocatePin(pin,false)) {
// deallocate old pin
pinManager.deallocatePin(PIRsensorPin);
// PIR Sensor mode INPUT_PULLUP
pinMode(pin, INPUT_PULLUP);
if (m_PIRenabled)
{
// remove old ISR
detachInterrupt(PIRsensorPin);
// assign interrupt function and set CHANGE mode
attachInterrupt(digitalPinToInterrupt(pin), ISR_PIRstateChange, CHANGE);
newPIRsensorState(true, true);
}
PIRsensorPin = pin;
m_updateConfig = true;
}
}
if (root[F("PIRenabled")] != nullptr)
{
if (root[F("PIRenabled")] && !m_PIRenabled)
{ {
attachInterrupt(digitalPinToInterrupt(PIRsensorPin), ISR_PIRstateChange, CHANGE); attachInterrupt(digitalPinToInterrupt(PIRsensorPin), ISR_PIRstateChange, CHANGE);
newPIRsensorState(true, true); newPIRsensorState(true, true);
@ -302,7 +356,7 @@ after <input type=\"number\" min=\"1\" max=\"720\" value=\"";
{ {
detachInterrupt(PIRsensorPin); detachInterrupt(PIRsensorPin);
} }
m_PIRenabled = root["PIRenabled"]; m_PIRenabled = root[F("PIRenabled")];
m_updateConfig = true; m_updateConfig = true;
} }
} }
@ -312,19 +366,24 @@ after <input type=\"number\" min=\"1\" max=\"720\" value=\"";
*/ */
void addToConfig(JsonObject &root) void addToConfig(JsonObject &root)
{ {
JsonObject top = root.createNestedObject("PIRsensorSwitch"); JsonObject top = root.createNestedObject(F("PIRsensorSwitch"));
top["PIRenabled"] = m_PIRenabled; top[F("PIRenabled")] = m_PIRenabled;
top["PIRoffSec"] = m_switchOffDelay; top[F("PIRoffSec")] = m_switchOffDelay;
top[F("pin")] = PIRsensorPin;
} }
/** /**
* restore the changeable values * restore the changeable values
* readFromConfig() is called before setup() to populate properties from values stored in cfg.json
*/ */
void readFromConfig(JsonObject &root) void readFromConfig(JsonObject &root)
{ {
JsonObject top = root["PIRsensorSwitch"]; JsonObject top = root[F("PIRsensorSwitch")];
m_PIRenabled = (top["PIRenabled"] != nullptr ? top["PIRenabled"] : true); if (!top.isNull() && top[F("pin")] != nullptr) {
m_switchOffDelay = top["PIRoffSec"] | m_switchOffDelay; PIRsensorPin = (int)top[F("pin")];
}
m_PIRenabled = (top[F("PIRenabled")] != nullptr ? top[F("PIRenabled")] : true);
m_switchOffDelay = top[F("PIRoffSec")] | m_switchOffDelay;
} }
/** /**

View File

@ -46,12 +46,12 @@
var ih="<tr><th>Active</th><th>Hour</th><th>Minute</th><th>Preset</th><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr>"; var ih="<tr><th>Active</th><th>Hour</th><th>Minute</th><th>Preset</th><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr>";
for (i=0;i<8;i++) for (i=0;i<8;i++)
{ {
ih+="<tr><td><input name=\"W"+i+"\" id=\"W"+i+"\" type=\"number\" style=\"display:none\"><input id=\"W"+i+"0\" type=\"checkbox\"></td><td><input name=\"H"+i+"\" type=\"number\" min=\"0\" max=\"24\"></td><td><input name=\"N"+i+"\" type=\"number\" min=\"0\" max=\"59\"></td><td><input name=\"T"+i+"\" type=\"number\" min=\"0\" max=\"250\"></td>"; ih+="<tr><td><input name=\"W"+i+"\" id=\"W"+i+"\" type=\"number\" style=\"display:none\"><input id=\"W"+i+"0\" type=\"checkbox\"></td><td><input name=\"H"+i+"\" class=\"small\" type=\"number\" min=\"0\" max=\"24\"></td><td><input name=\"N"+i+"\" class=\"small\" type=\"number\" min=\"0\" max=\"59\"></td><td><input name=\"T"+i+"\" class=\"small\" type=\"number\" min=\"0\" max=\"250\"></td>";
for (j=1;j<8;j++) ih+="<td><input id=\"W"+i+j+"\" type=\"checkbox\"></td>"; for (j=1;j<8;j++) ih+="<td><input id=\"W"+i+j+"\" type=\"checkbox\"></td>";
} }
ih+="<tr><td><input name=\"W8\" id=\"W8\" type=\"number\" style=\"display:none\"><input id=\"W80\" type=\"checkbox\"></td><td>Sunrise<input name=\"H8\" value=\"255\" type=\"hidden\"></td><td><input name=\"N8\" type=\"number\" min=\"-59\" max=\"59\"></td><td><input name=\"T8\" type=\"number\" min=\"0\" max=\"250\"></td>"; ih+="<tr><td><input name=\"W8\" id=\"W8\" type=\"number\" style=\"display:none\"><input id=\"W80\" type=\"checkbox\"></td><td>Sunrise<input name=\"H8\" class=\"small\" value=\"255\" type=\"hidden\"></td><td><input name=\"N8\" class=\"small\" type=\"number\" min=\"-59\" max=\"59\"></td><td><input name=\"T8\" class=\"small\" type=\"number\" min=\"0\" max=\"250\"></td>";
for (j=1;j<8;j++) ih+="<td><input id=\"W8"+j+"\" type=\"checkbox\"></td>"; for (j=1;j<8;j++) ih+="<td><input id=\"W8"+j+"\" type=\"checkbox\"></td>";
ih+="<tr><td><input name=\"W9\" id=\"W9\" type=\"number\" style=\"display:none\"><input id=\"W90\" type=\"checkbox\"></td><td>Sunset<input name=\"H9\" value=\"255\" type=\"hidden\"></td><td><input name=\"N9\" type=\"number\" min=\"-59\" max=\"59\"><td><input name=\"T9\" type=\"number\" min=\"0\" max=\"250\"></td>"; ih+="<tr><td><input name=\"W9\" id=\"W9\" type=\"number\" style=\"display:none\"><input id=\"W90\" type=\"checkbox\"></td><td>Sunset<input name=\"H9\" class=\"small\" value=\"255\" type=\"hidden\"></td><td><input name=\"N9\" class=\"small\" type=\"number\" min=\"-59\" max=\"59\"><td><input name=\"T9\" class=\"small\" type=\"number\" min=\"0\" max=\"250\"></td>";
for (j=1;j<8;j++) ih+="<td><input id=\"W9"+j+"\" type=\"checkbox\"></td>"; for (j=1;j<8;j++) ih+="<td><input id=\"W9"+j+"\" type=\"checkbox\"></td>";
gId("TMT").innerHTML=ih; gId("TMT").innerHTML=ih;
} }
@ -142,8 +142,8 @@
</div> </div>
Countdown Mode: <input type="checkbox" name="CE"><br> Countdown Mode: <input type="checkbox" name="CE"><br>
Countdown Goal:<br> Countdown Goal:<br>
Year: 20 <input name="CY" type="number" min="0" max="99" required> Month: <input name="CI" type="number" min="1" max="12" required> Day: <input name="CD" type="number" min="1" max="31" required><br> Year: 20 <input name="CY" class="small" type="number" min="0" max="99" required> Month: <input name="CI" class="small" type="number" min="1" max="12" required> Day: <input name="CD" class="small" type="number" min="1" max="31" required><br>
Hour: <input name="CH" type="number" min="0" max="23" required> Minute: <input name="CM" type="number" min="0" max="59" required> Second: <input name="CS" type="number" min="0" max="59" required><br> Hour: <input name="CH" class="small" type="number" min="0" max="23" required> Minute: <input name="CM" class="small" type="number" min="0" max="59" required> Second: <input name="CS" class="small" type="number" min="0" max="59" required><br>
<h3>Macro presets</h3> <h3>Macro presets</h3>
<b>Macros have moved!</b><br> <b>Macros have moved!</b><br>
<i>Presets now also can be used as macros to save both JSON and HTTP API commands.<br> <i>Presets now also can be used as macros to save both JSON and HTTP API commands.<br>

View File

@ -43,6 +43,12 @@ input[type="number"] {
font-size: medium; font-size: medium;
margin: 2px; margin: 2px;
} }
input[type="number"].big {
width: 80px;
}
input[type="number"].small {
width: 40px;
}
select { select {
margin: 2px; margin: 2px;
font-size: medium; font-size: medium;

View File

@ -6,7 +6,7 @@
*/ */
// Autogenerated from wled00/data/style.css, do not edit!! // Autogenerated from wled00/data/style.css, do not edit!!
const char PAGE_settingsCss[] PROGMEM = R"=====(<style>body{font-family:Verdana,sans-serif;text-align:center;background:#222;color:#fff;line-height:200%;margin:0}hr{border-color:#666}button{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.3ch solid #333;border-radius:24px;display:inline-block;font-size:20px;margin:12px 8px 8px;padding:8px 12px;min-width:48px}.toprow{top:0;position:sticky;background-color:#222;z-index:1}.helpB{text-align:left;position:absolute;width:60px}input{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.5ch solid #333}input[type=number]{width:4em;font-size:medium;margin:2px}select{margin:2px;font-size:medium}input[type=checkbox]{-ms-transform:scale(2);-moz-transform:scale(2);-webkit-transform:scale(2);-o-transform:scale(2);transform:scale(2)}select{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.5ch solid #333}td{padding:2px}.d5{width:4.5em!important}</style>)====="; const char PAGE_settingsCss[] PROGMEM = R"=====(<style>body{font-family:Verdana,sans-serif;text-align:center;background:#222;color:#fff;line-height:200%;margin:0}hr{border-color:#666}button{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.3ch solid #333;border-radius:24px;display:inline-block;font-size:20px;margin:12px 8px 8px;padding:8px 12px;min-width:48px}.toprow{top:0;position:sticky;background-color:#222;z-index:1}.helpB{text-align:left;position:absolute;width:60px}input{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.5ch solid #333}input[type=number]{width:4em;font-size:medium;margin:2px}input[type=number].big{width:80px}input[type=number].small{width:40px}select{margin:2px;font-size:medium}input[type=checkbox]{-ms-transform:scale(2);-moz-transform:scale(2);-webkit-transform:scale(2);-o-transform:scale(2);transform:scale(2)}select{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.5ch solid #333}td{padding:2px}.d5{width:4.5em!important}</style>)=====";
// Autogenerated from wled00/data/settings.htm, do not edit!! // Autogenerated from wled00/data/settings.htm, do not edit!!
@ -297,7 +297,7 @@ type="submit">Save</button></form></body></html>)=====";
// Autogenerated from wled00/data/settings_time.htm, do not edit!! // Autogenerated from wled00/data/settings_time.htm, do not edit!!
const char PAGE_settings_time[] PROGMEM = R"=====(<!DOCTYPE html><html lang="en"><head><meta name="viewport" content="width=500"> const char PAGE_settings_time[] PROGMEM = R"=====(<!DOCTYPE html><html lang="en"><head><meta name="viewport" content="width=500">
<meta charset="utf-8"><title>Time Settings</title><script> <meta charset="utf-8"><title>Time Settings</title><script>
var d=document;function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#time-settings")}function B(){window.open("/settings","_self")}function S(){BTa(),GetV(),Cs(),FC()}function gId(t){return d.getElementById(t)}function Cs(){gId("cac").style.display="none",gId("coc").style.display="block",gId("ccc").style.display="none",gId("ca").selected&&(gId("cac").style.display="block"),gId("cc").selected&&(gId("coc").style.display="none",gId("ccc").style.display="block"),gId("cn").selected&&(gId("coc").style.display="none")}function BTa(){var t="<tr><th>Active</th><th>Hour</th><th>Minute</th><th>Preset</th><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr>";for(i=0;i<8;i++)for(t+='<tr><td><input name="W'+i+'" id="W'+i+'" type="number" style="display:none"><input id="W'+i+'0" type="checkbox"></td><td><input name="H'+i+'" type="number" min="0" max="24"></td><td><input name="N'+i+'" type="number" min="0" max="59"></td><td><input name="T'+i+'" type="number" min="0" max="250"></td>',j=1;j<8;j++)t+='<td><input id="W'+i+j+'" type="checkbox"></td>';for(t+='<tr><td><input name="W8" id="W8" type="number" style="display:none"><input id="W80" type="checkbox"></td><td>Sunrise<input name="H8" value="255" type="hidden"></td><td><input name="N8" type="number" min="-59" max="59"></td><td><input name="T8" type="number" min="0" max="250"></td>',j=1;j<8;j++)t+='<td><input id="W8'+j+'" type="checkbox"></td>';for(t+='<tr><td><input name="W9" id="W9" type="number" style="display:none"><input id="W90" type="checkbox"></td><td>Sunset<input name="H9" value="255" type="hidden"></td><td><input name="N9" type="number" min="-59" max="59"><td><input name="T9" type="number" min="0" max="250"></td>',j=1;j<8;j++)t+='<td><input id="W9'+j+'" type="checkbox"></td>';gId("TMT").innerHTML=t}function FC(){for(j=0;j<8;j++)for(i=0;i<10;i++)gId("W"+i+j).checked=gId("W"+i).value>>j&1}function Wd(){for(a=[0,0,0,0,0,0,0,0,0,0],i=0;i<10;i++){for(m=1,j=0;j<8;j++)a[i]+=gId("W"+i+j).checked*m,m*=2;gId("W"+i).value=a[i]}}function GetV() { var d=document;function H(){window.open("https://github.com/Aircoookie/WLED/wiki/Settings#time-settings")}function B(){window.open("/settings","_self")}function S(){BTa(),GetV(),Cs(),FC()}function gId(t){return d.getElementById(t)}function Cs(){gId("cac").style.display="none",gId("coc").style.display="block",gId("ccc").style.display="none",gId("ca").selected&&(gId("cac").style.display="block"),gId("cc").selected&&(gId("coc").style.display="none",gId("ccc").style.display="block"),gId("cn").selected&&(gId("coc").style.display="none")}function BTa(){var t="<tr><th>Active</th><th>Hour</th><th>Minute</th><th>Preset</th><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th><th>S</th></tr>";for(i=0;i<8;i++)for(t+='<tr><td><input name="W'+i+'" id="W'+i+'" type="number" style="display:none"><input id="W'+i+'0" type="checkbox"></td><td><input name="H'+i+'" class="small" type="number" min="0" max="24"></td><td><input name="N'+i+'" class="small" type="number" min="0" max="59"></td><td><input name="T'+i+'" class="small" type="number" min="0" max="250"></td>',j=1;j<8;j++)t+='<td><input id="W'+i+j+'" type="checkbox"></td>';for(t+='<tr><td><input name="W8" id="W8" type="number" style="display:none"><input id="W80" type="checkbox"></td><td>Sunrise<input name="H8" class="small" value="255" type="hidden"></td><td><input name="N8" class="small" type="number" min="-59" max="59"></td><td><input name="T8" class="small" type="number" min="0" max="250"></td>',j=1;j<8;j++)t+='<td><input id="W8'+j+'" type="checkbox"></td>';for(t+='<tr><td><input name="W9" id="W9" type="number" style="display:none"><input id="W90" type="checkbox"></td><td>Sunset<input name="H9" class="small" value="255" type="hidden"></td><td><input name="N9" class="small" type="number" min="-59" max="59"><td><input name="T9" class="small" type="number" min="0" max="250"></td>',j=1;j<8;j++)t+='<td><input id="W9'+j+'" type="checkbox"></td>';gId("TMT").innerHTML=t}function FC(){for(j=0;j<8;j++)for(i=0;i<10;i++)gId("W"+i+j).checked=gId("W"+i).value>>j&1}function Wd(){for(a=[0,0,0,0,0,0,0,0,0,0],i=0;i<10;i++){for(m=1,j=0;j<8;j++)a[i]+=gId("W"+i+j).checked*m,m*=2;gId("W"+i).value=a[i]}}function GetV() {
%CSS%%SCSS%</head><body onload="S()"><form %CSS%%SCSS%</head><body onload="S()"><form
id="form_s" name="Sf" method="post" onsubmit="Wd()"><div class="toprow"><div id="form_s" name="Sf" method="post" onsubmit="Wd()"><div class="toprow"><div
class="helpB"><button type="button" onclick="H()">?</button></div><button class="helpB"><button type="button" onclick="H()">?</button></div><button
@ -330,13 +330,14 @@ name="O2" type="number" min="0" max="255" required><br><div id="cac">12h LED:
type="checkbox" name="OS"><br></div><div id="ccc">Cronixie Display: <input type="checkbox" name="OS"><br></div><div id="ccc">Cronixie Display: <input
name="CX" maxlength="6"><br>Cronixie Backlight: <input type="checkbox" name="CX" maxlength="6"><br>Cronixie Backlight: <input type="checkbox"
name="CB"><br></div>Countdown Mode: <input type="checkbox" name="CE"><br> name="CB"><br></div>Countdown Mode: <input type="checkbox" name="CE"><br>
Countdown Goal:<br>Year: 20 <input name="CY" type="number" min="0" max="99" Countdown Goal:<br>Year: 20 <input name="CY" class="small" type="number"
required> Month: <input name="CI" type="number" min="1" max="12" required> Day: min="0" max="99" required> Month: <input name="CI" class="small" type="number"
<input name="CD" type="number" min="1" max="31" required><br>Hour: <input min="1" max="12" required> Day: <input name="CD" class="small" type="number"
name="CH" type="number" min="0" max="23" required> Minute: <input name="CM" min="1" max="31" required><br>Hour: <input name="CH" class="small"
type="number" min="0" max="59" required> Second: <input name="CS" type="number" type="number" min="0" max="23" required> Minute: <input name="CM" class="small"
min="0" max="59" required><br><h3>Macro presets</h3><b>Macros have moved!</b> type="number" min="0" max="59" required> Second: <input name="CS" class="small"
<br><i> type="number" min="0" max="59" required><br><h3>Macro presets</h3><b>
Macros have moved!</b><br><i>
Presets now also can be used as macros to save both JSON and HTTP API commands. Presets now also can be used as macros to save both JSON and HTTP API commands.
<br>Just enter the preset id below!</i> <i> <br>Just enter the preset id below!</i> <i>
Use 0 for the default action instead of a preset</i><br>Alexa On/Off Preset: Use 0 for the default action instead of a preset</i><br>Alexa On/Off Preset: