mirror of
https://github.com/wled/WLED.git
synced 2025-07-09 11:56:35 +00:00
Multi PIR usermod
This commit is contained in:
parent
00038453e1
commit
246746a82e
@ -15,6 +15,9 @@
|
||||
#define PIR_SENSOR_OFF_SEC 600
|
||||
#endif
|
||||
|
||||
#ifndef PIR_SENSOR_MAX_SENSORS
|
||||
#define PIR_SENSOR_MAX_SENSORS 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This usermod handles PIR sensor states.
|
||||
@ -50,13 +53,13 @@ private:
|
||||
|
||||
volatile unsigned long offTimerStart = 0; // off timer start time
|
||||
volatile bool PIRtriggered = false; // did PIR trigger?
|
||||
bool sensorPinState = LOW; // current PIR sensor pin state
|
||||
bool initDone = false; // status of initialization
|
||||
unsigned long lastLoop = 0;
|
||||
bool initDone = false; // status of initialization
|
||||
unsigned long lastLoop = 0;
|
||||
bool sensorPinState[PIR_SENSOR_MAX_SENSORS] = {LOW}; // current PIR sensor pin state
|
||||
|
||||
// configurable parameters
|
||||
bool enabled = true; // PIR sensor enabled
|
||||
int8_t PIRsensorPin = PIR_SENSOR_PIN; // PIR sensor pin
|
||||
int8_t PIRsensorPin[PIR_SENSOR_MAX_SENSORS] = {PIR_SENSOR_PIN}; // PIR sensor pin
|
||||
uint32_t m_switchOffDelay = PIR_SENSOR_OFF_SEC*1000; // delay before switch off after the sensor state goes LOW (10min)
|
||||
uint8_t m_onPreset = 0; // on preset
|
||||
uint8_t m_offPreset = 0; // off preset
|
||||
@ -325,21 +328,29 @@ void PIRsensorSwitch::publishHomeAssistantAutodiscovery()
|
||||
|
||||
bool PIRsensorSwitch::updatePIRsensorState()
|
||||
{
|
||||
bool pinState = digitalRead(PIRsensorPin);
|
||||
if (pinState != sensorPinState) {
|
||||
sensorPinState = pinState; // change previous state
|
||||
bool stateChanged = false;
|
||||
bool allOff = true;
|
||||
for (int i = 0; i < PIR_SENSOR_MAX_SENSORS; i++) {
|
||||
if (PIRsensorPin[i] < 0) continue;
|
||||
|
||||
if (sensorPinState == HIGH) {
|
||||
offTimerStart = 0;
|
||||
if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(true);
|
||||
} else {
|
||||
// start switch off timer
|
||||
offTimerStart = millis();
|
||||
bool pinState = digitalRead(PIRsensorPin[i]);
|
||||
if (pinState != sensorPinState[i]) {
|
||||
sensorPinState[i] = pinState; // change previous state
|
||||
stateChanged = true;
|
||||
|
||||
if (sensorPinState[i] == HIGH) {
|
||||
offTimerStart = 0;
|
||||
allOff = false;
|
||||
if (!m_mqttOnly && (!m_nightTimeOnly || (m_nightTimeOnly && !isDayTime()))) switchStrip(true);
|
||||
}
|
||||
}
|
||||
publishMqtt(sensorPinState == HIGH);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
if (stateChanged) {
|
||||
publishMqtt(!allOff);
|
||||
// start switch off timer
|
||||
if (allOff) offTimerStart = millis();
|
||||
}
|
||||
return stateChanged;
|
||||
}
|
||||
|
||||
bool PIRsensorSwitch::handleOffTimer()
|
||||
@ -356,18 +367,21 @@ bool PIRsensorSwitch::handleOffTimer()
|
||||
|
||||
void PIRsensorSwitch::setup()
|
||||
{
|
||||
if (enabled) {
|
||||
for (int i = 0; i < PIR_SENSOR_MAX_SENSORS; i++) {
|
||||
sensorPinState[i] = LOW;
|
||||
if (PIRsensorPin[i] < 0) continue;
|
||||
// pin retrieved from cfg.json (readFromConfig()) prior to running setup()
|
||||
if (PIRsensorPin >= 0 && pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) {
|
||||
// PIR Sensor mode INPUT_PULLUP
|
||||
pinMode(PIRsensorPin, INPUT_PULLUP);
|
||||
sensorPinState = digitalRead(PIRsensorPin);
|
||||
if (pinManager.allocatePin(PIRsensorPin[i], false, PinOwner::UM_PIR)) {
|
||||
// PIR Sensor mode INPUT_PULLDOWN
|
||||
#ifdef ESP8266
|
||||
pinMode(PIRsensorPin[i], PIRsensorPin[i]==16 ? INPUT_PULLDOWN : INPUT_PULLUP); // ESP8266 has INPUT_PULLDOWN on GPIO16 only
|
||||
#else
|
||||
pinMode(PIRsensorPin[i], INPUT_PULLDOWN);
|
||||
#endif
|
||||
sensorPinState[i] = digitalRead(PIRsensorPin[i]);
|
||||
} else {
|
||||
if (PIRsensorPin >= 0) {
|
||||
DEBUG_PRINTLN(F("PIRSensorSwitch pin allocation failed."));
|
||||
}
|
||||
PIRsensorPin = -1; // allocation failed
|
||||
enabled = false;
|
||||
DEBUG_PRINT(F("PIRSensorSwitch pin ")); DEBUG_PRINTLN(i); DEBUG_PRINTLN(F(" allocation failed."));
|
||||
PIRsensorPin[i] = -1; // allocation failed
|
||||
}
|
||||
}
|
||||
initDone = true;
|
||||
@ -382,8 +396,8 @@ void PIRsensorSwitch::onMqttConnect(bool sessionPresent)
|
||||
|
||||
void PIRsensorSwitch::loop()
|
||||
{
|
||||
// only check sensors 4x/s
|
||||
if (!enabled || millis() - lastLoop < 250 || strip.isUpdating()) return;
|
||||
// only check sensors 5x/s
|
||||
if (!enabled || millis() - lastLoop < 200) return;
|
||||
lastLoop = millis();
|
||||
|
||||
if (!updatePIRsensorState()) {
|
||||
@ -396,37 +410,35 @@ void PIRsensorSwitch::addToJsonInfo(JsonObject &root)
|
||||
JsonObject user = root["u"];
|
||||
if (user.isNull()) user = root.createNestedObject("u");
|
||||
|
||||
bool state = LOW;
|
||||
for (int i = 0; i < PIR_SENSOR_MAX_SENSORS; i++)
|
||||
if (PIRsensorPin[i] >= 0) state |= sensorPinState[i];
|
||||
|
||||
JsonArray infoArr = user.createNestedArray(FPSTR(_name));
|
||||
|
||||
String uiDomString;
|
||||
if (enabled) {
|
||||
if (offTimerStart > 0)
|
||||
{
|
||||
if (offTimerStart > 0) {
|
||||
uiDomString = "";
|
||||
unsigned int offSeconds = (m_switchOffDelay - (millis() - offTimerStart)) / 1000;
|
||||
if (offSeconds >= 3600)
|
||||
{
|
||||
if (offSeconds >= 3600) {
|
||||
uiDomString += (offSeconds / 3600);
|
||||
uiDomString += F("h ");
|
||||
offSeconds %= 3600;
|
||||
}
|
||||
if (offSeconds >= 60)
|
||||
{
|
||||
if (offSeconds >= 60) {
|
||||
uiDomString += (offSeconds / 60);
|
||||
offSeconds %= 60;
|
||||
}
|
||||
else if (uiDomString.length() > 0)
|
||||
{
|
||||
} else if (uiDomString.length() > 0) {
|
||||
uiDomString += 0;
|
||||
}
|
||||
if (uiDomString.length() > 0)
|
||||
{
|
||||
if (uiDomString.length() > 0) {
|
||||
uiDomString += F("min ");
|
||||
}
|
||||
uiDomString += (offSeconds);
|
||||
infoArr.add(uiDomString + F("s"));
|
||||
} else {
|
||||
infoArr.add(sensorPinState ? F("sensor on") : F("inactive"));
|
||||
infoArr.add(state ? F("sensor on") : F("inactive"));
|
||||
}
|
||||
} else {
|
||||
infoArr.add(F("disabled"));
|
||||
@ -446,9 +458,11 @@ void PIRsensorSwitch::addToJsonInfo(JsonObject &root)
|
||||
uiDomString += F("</button>");
|
||||
infoArr.add(uiDomString);
|
||||
|
||||
JsonObject sensor = root[F("sensor")];
|
||||
if (sensor.isNull()) sensor = root.createNestedObject(F("sensor"));
|
||||
sensor[F("motion")] = sensorPinState || offTimerStart>0 ? true : false;
|
||||
if (enabled) {
|
||||
JsonObject sensor = root[F("sensor")];
|
||||
if (sensor.isNull()) sensor = root.createNestedObject(F("sensor"));
|
||||
sensor[F("motion")] = state || offTimerStart>0 ? true : false;
|
||||
}
|
||||
}
|
||||
|
||||
void PIRsensorSwitch::onStateChange(uint8_t mode) {
|
||||
@ -478,7 +492,8 @@ void PIRsensorSwitch::addToConfig(JsonObject &root)
|
||||
JsonObject top = root.createNestedObject(FPSTR(_name));
|
||||
top[FPSTR(_enabled)] = enabled;
|
||||
top[FPSTR(_switchOffDelay)] = m_switchOffDelay / 1000;
|
||||
top["pin"] = PIRsensorPin;
|
||||
JsonArray pinArray = top.createNestedArray("pin");
|
||||
for (int i = 0; i < PIR_SENSOR_MAX_SENSORS; i++) pinArray.add(PIRsensorPin[i]);
|
||||
top[FPSTR(_onPreset)] = m_onPreset;
|
||||
top[FPSTR(_offPreset)] = m_offPreset;
|
||||
top[FPSTR(_nightTime)] = m_nightTimeOnly;
|
||||
@ -494,12 +509,20 @@ void PIRsensorSwitch::appendConfigData()
|
||||
{
|
||||
oappend(SET_F("addInfo('PIRsensorSwitch:HA-discovery',1,'HA=Home Assistant');")); // 0 is field type, 1 is actual field
|
||||
oappend(SET_F("addInfo('PIRsensorSwitch:override',1,'Cancel timer on change');")); // 0 is field type, 1 is actual field
|
||||
for (int i = 0; i < PIR_SENSOR_MAX_SENSORS; i++) {
|
||||
char str[128];
|
||||
sprintf_P(str, PSTR("addInfo('PIRsensorSwitch:pin[]',%d,'','#%d');"), i, i);
|
||||
oappend(str);
|
||||
}
|
||||
}
|
||||
|
||||
bool PIRsensorSwitch::readFromConfig(JsonObject &root)
|
||||
{
|
||||
bool oldEnabled = enabled;
|
||||
int8_t oldPin = PIRsensorPin;
|
||||
int8_t oldPin[PIR_SENSOR_MAX_SENSORS];
|
||||
for (int i = 0; i < PIR_SENSOR_MAX_SENSORS; i++) {
|
||||
oldPin[i] = PIRsensorPin[i];
|
||||
PIRsensorPin[i] = -1;
|
||||
}
|
||||
|
||||
DEBUG_PRINT(FPSTR(_name));
|
||||
JsonObject top = root[FPSTR(_name)];
|
||||
@ -508,7 +531,13 @@ bool PIRsensorSwitch::readFromConfig(JsonObject &root)
|
||||
return false;
|
||||
}
|
||||
|
||||
PIRsensorPin = top["pin"] | PIRsensorPin;
|
||||
JsonArray pins = top["pin"];
|
||||
if (!pins.isNull()) {
|
||||
for (int i = 0; i < PIR_SENSOR_MAX_SENSORS; i++)
|
||||
if (i < pins.size()) PIRsensorPin[i] = pins[i] | PIRsensorPin[i];
|
||||
} else {
|
||||
PIRsensorPin[0] = top["pin"] | oldPin[0];
|
||||
}
|
||||
|
||||
enabled = top[FPSTR(_enabled)] | enabled;
|
||||
|
||||
@ -530,26 +559,11 @@ bool PIRsensorSwitch::readFromConfig(JsonObject &root)
|
||||
// reading config prior to setup()
|
||||
DEBUG_PRINTLN(F(" config loaded."));
|
||||
} else {
|
||||
if (oldPin != PIRsensorPin || oldEnabled != enabled) {
|
||||
// check if pin is OK
|
||||
if (oldPin != PIRsensorPin && oldPin >= 0) {
|
||||
// if we are changing pin in settings page
|
||||
// deallocate old pin
|
||||
pinManager.deallocatePin(oldPin, PinOwner::UM_PIR);
|
||||
if (pinManager.allocatePin(PIRsensorPin, false, PinOwner::UM_PIR)) {
|
||||
pinMode(PIRsensorPin, INPUT_PULLUP);
|
||||
} else {
|
||||
// allocation failed
|
||||
PIRsensorPin = -1;
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
if (enabled) {
|
||||
sensorPinState = digitalRead(PIRsensorPin);
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < PIR_SENSOR_MAX_SENSORS; i++)
|
||||
if (oldPin[i] >= 0) pinManager.deallocatePin(oldPin[i], PinOwner::UM_PIR);
|
||||
setup();
|
||||
DEBUG_PRINTLN(F(" config (re)loaded."));
|
||||
}
|
||||
// use "return !top["newestParameter"].isNull();" when updating Usermod with new features
|
||||
return !top[FPSTR(_domoticzIDX)].isNull();
|
||||
return !(pins.isNull() || pins.size() != PIR_SENSOR_MAX_SENSORS);
|
||||
}
|
||||
|
@ -426,7 +426,7 @@
|
||||
#ifdef ESP8266
|
||||
#define SETTINGS_STACK_BUF_SIZE 2048
|
||||
#else
|
||||
#define SETTINGS_STACK_BUF_SIZE 3608 // warning: quite a large value for stack
|
||||
#define SETTINGS_STACK_BUF_SIZE 3840 // warning: quite a large value for stack (640 * WLED_MAX_USERMODS)
|
||||
#endif
|
||||
|
||||
#ifdef WLED_USE_ETHERNET
|
||||
|
@ -227,10 +227,10 @@
|
||||
} else if (typeof(fld) === "number") sel.classList.add("pin"); // a hack to add a class
|
||||
let arr = d.getElementsByName(um);
|
||||
let idx = arr[0].type==="hidden"?1:0; // ignore hidden field
|
||||
if (arr.length > 2) {
|
||||
if (arr.length > 1+idx) {
|
||||
// we have array of values (usually pins)
|
||||
for (let i of arr) {
|
||||
if (i.type === "number") break;
|
||||
if (i.nodeName === "INPUT" && i.type === "number") break;
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user