From 028b59210934100ab8ef6faf3d27560ac381c7d6 Mon Sep 17 00:00:00 2001
From: Darrell
Date: Tue, 21 Sep 2021 13:55:41 -0400
Subject: [PATCH] Feature/motion (#49)
* New Feature: Add Motion Sensor via GPIO
- Use PIR Motion Sensor (HC-SR501) or Radar Sensor (RCWL-0516)
* New Feature Fork WifiSettings and use that for constant portal
Co-authored-by: Stefan Knaak
---
platformio.ini | 2 +-
src/main.cpp | 74 ++++++++++++++++++++++++++++++++++----
src/main.h | 98 ++++++++++++++++++++++++++++++++++----------------
3 files changed, 136 insertions(+), 38 deletions(-)
diff --git a/platformio.ini b/platformio.ini
index d444d19..98dabd1 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -16,7 +16,7 @@ lib_deps_external =
haimoz/SoftFilters@^0.1.0
marvinroger/AsyncMqttClient@^0.9.0
bblanchon/ArduinoJson@^6.17.3
- juerd/ESP-WiFiSettings@^3.7.2
+ https://github.com/ESPresense/ESP-WiFiSettings.git
h2zero/NimBLE-Arduino@^1.3.1
me-no-dev/AsyncTCP@^1.1.1
bbx10/DNSServer@^1.1.0
diff --git a/src/main.cpp b/src/main.cpp
index c2f8284..414504d 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -4,7 +4,7 @@ bool sendTelemetry(int totalSeen = -1, int totalReported = -1, int totalAdverts
{
if (!online)
{
- if (sendOnline() && sendDiscoveryConnectivity() && sendDiscoveryMaxDistance())
+ if (sendOnline() && sendDiscoveryConnectivity() && sendDiscoveryMaxDistance() && sendDiscoveryMotion())
{
online = true;
reconnectTries = 0;
@@ -82,7 +82,8 @@ void connectToWifi()
Display.connecting();
return 150;
};
- WiFiSettings.onPortalWaitLoop = []() {
+ WiFiSettings.onPortalWaitLoop = []()
+ {
if (getUptimeSeconds() > 600)
ESP.restart();
};
@@ -101,6 +102,8 @@ void connectToWifi()
publishRooms = WiFiSettings.checkbox("pub_rooms", true, "Send to rooms topic");
publishDevices = WiFiSettings.checkbox("pub_devices", true, "Send to devices topic");
discovery = WiFiSettings.checkbox("discovery", true, "Hass Discovery");
+ pirPin = WiFiSettings.integer("pir_pin", 0, "PIR Motion Sensor pin (0 for disable)");
+ radarPin = WiFiSettings.integer("radar_pin", 0, "Radar Motion pin (0 for disable)");
maxDistance = WiFiSettings.integer("max_dist", DEFAULT_MAX_DISTANCE, "Maximum distance to report (in meters)");
WiFiSettings.hostname = "espresense-" + room;
@@ -128,6 +131,10 @@ void connectToWifi()
Serial.print("Discovery: ");
Serial.println(discovery ? "enabled" : "disabled");
Serial.printf("Max Distance: %d\n", maxDistance);
+ Serial.print("PIR Sensor: ");
+ Serial.println(pirPin ? "enabled" : "disabled");
+ Serial.print("Radar Sensor: ");
+ Serial.println(radarPin ? "enabled" : "disabled");
localIp = WiFi.localIP().toString();
roomsTopic = CHANNEL + "/rooms/" + room;
@@ -139,7 +146,7 @@ void connectToWifi()
void onMqttConnect(bool sessionPresent)
{
xTimerStop(reconnectTimer, 0);
- uint16_t packetIdSub = mqttClient.subscribe(subTopic.c_str(), 2);
+ mqttClient.subscribe(subTopic.c_str(), 2);
Display.connected(true, true);
}
@@ -156,11 +163,15 @@ void onMqttMessage(char *topic, char *payload, AsyncMqttClientMessageProperties
char new_payload[len + 1];
new_payload[len] = '\0';
strncpy(new_payload, payload, len);
- String pay = String(new_payload);
-
Serial.printf("%s: %s\n", topic, new_payload);
- maxDistance = pay.toInt();
- spurt("/max_dist", String(new_payload));
+
+ String top = String(topic);
+ String pay = String(new_payload);
+ if (top == roomsTopic + "/max_distance/set")
+ {
+ maxDistance = pay.toInt();
+ spurt("/max_dist", pay);
+ }
}
void reconnect(TimerHandle_t xTimer)
@@ -276,6 +287,8 @@ void setup()
#endif
spiffsInit();
connectToWifi();
+ if (pirPin) pinMode(pirPin, INPUT);
+ if (radarPin) pinMode(radarPin, INPUT);
#if NTP
setClock();
#endif
@@ -284,9 +297,56 @@ void setup()
configureOTA();
}
+void pirLoop()
+{
+ if (!pirPin) return;
+ int pirValue = digitalRead(pirPin);
+
+ if (pirValue != lastPirValue)
+ {
+ if (pirValue == HIGH)
+ {
+ mqttClient.publish((roomsTopic + "/motion").c_str(), 0, 1, "on");
+ Serial.println("PIR MOTION DETECTED!!!");
+ }
+ else
+ {
+ mqttClient.publish((roomsTopic + "/motion").c_str(), 0, 1, "off");
+ Serial.println("NO PIR MOTION DETECTED!!!");
+ }
+
+ lastPirValue = pirValue;
+ }
+}
+
+void radarLoop()
+{
+ if (!radarPin) return;
+ int radarValue = digitalRead(radarPin);
+
+ if (radarValue != lastRadarValue)
+ {
+ if (radarValue == HIGH)
+ {
+ mqttClient.publish((roomsTopic + "/motion").c_str(), 0, 1, "on");
+ Serial.println("Radar MOTION DETECTED!!!");
+ }
+ else
+ {
+ mqttClient.publish((roomsTopic + "/motion").c_str(), 0, 1, "off");
+ Serial.println("NO Radar MOTION DETECTED!!!");
+ }
+
+ lastRadarValue = radarValue;
+ }
+}
+
void loop()
{
ArduinoOTA.handle();
firmwareUpdate();
Display.update();
+ pirLoop();
+ radarLoop();
+ WiFiSettings.httpLoop();
}
diff --git a/src/main.h b/src/main.h
index b773b09..853c851 100644
--- a/src/main.h
+++ b/src/main.h
@@ -46,6 +46,11 @@ bool publishRooms;
bool publishDevices;
bool discovery;
int maxDistance;
+int pirPin;
+int radarPin;
+
+int lastPirValue = -1;
+int lastRadarValue = -1;
BleFingerprintCollection fingerprints(MAX_MAC_ADDRESSES);
@@ -113,36 +118,40 @@ void setClock()
void configureOTA()
{
ArduinoOTA
- .onStart([]() {
- Serial.println("OTA Start");
- updateInProgress = true;
- fingerprints.setDisable(updateInProgress);
- })
- .onEnd([]() {
- updateInProgress = false;
- fingerprints.setDisable(updateInProgress);
- Display.updateEnd();
- Serial.println("\n\rEnd");
- })
- .onProgress([](unsigned int progress, unsigned int total) {
- byte percent = (progress / (total / 100));
- Serial.printf("Progress: %u\r\n", percent);
- Display.updateProgress(progress);
- })
- .onError([](ota_error_t error) {
- Serial.printf("Error[%u]: ", error);
- if (error == OTA_AUTH_ERROR)
- Serial.println("Auth Failed");
- else if (error == OTA_BEGIN_ERROR)
- Serial.println("Begin Failed");
- else if (error == OTA_CONNECT_ERROR)
- Serial.println("Connect Failed");
- else if (error == OTA_RECEIVE_ERROR)
- Serial.println("Receive Failed");
- else if (error == OTA_END_ERROR)
- Serial.println("End Failed");
- updateInProgress = false;
- });
+ .onStart([]()
+ {
+ Serial.println("OTA Start");
+ updateInProgress = true;
+ fingerprints.setDisable(updateInProgress);
+ })
+ .onEnd([]()
+ {
+ updateInProgress = false;
+ fingerprints.setDisable(updateInProgress);
+ Display.updateEnd();
+ Serial.println("\n\rEnd");
+ })
+ .onProgress([](unsigned int progress, unsigned int total)
+ {
+ byte percent = (progress / (total / 100));
+ Serial.printf("Progress: %u\r\n", percent);
+ Display.updateProgress(progress);
+ })
+ .onError([](ota_error_t error)
+ {
+ Serial.printf("Error[%u]: ", error);
+ if (error == OTA_AUTH_ERROR)
+ Serial.println("Auth Failed");
+ else if (error == OTA_BEGIN_ERROR)
+ Serial.println("Begin Failed");
+ else if (error == OTA_CONNECT_ERROR)
+ Serial.println("Connect Failed");
+ else if (error == OTA_RECEIVE_ERROR)
+ Serial.println("Receive Failed");
+ else if (error == OTA_END_ERROR)
+ Serial.println("End Failed");
+ updateInProgress = false;
+ });
ArduinoOTA.setHostname(WiFi.getHostname());
ArduinoOTA.setPort(3232);
ArduinoOTA.begin();
@@ -289,6 +298,35 @@ bool sendDiscoveryConnectivity()
return false;
}
+bool sendDiscoveryMotion()
+{
+ if (!discovery) return true;
+ if (!pirPin && !radarPin) return true;
+
+ String discoveryTopic = "homeassistant/binary_sensor/espresense_" + room + "/motion/config";
+
+ DynamicJsonDocument doc(1200);
+ char buffer[1200];
+
+ doc["~"] = roomsTopic;
+ doc["name"] = "ESPresense " + room + " Motion";
+ doc["unique_id"] = WiFi.macAddress() + "_motion";
+ doc["availability_topic"] = "~/status";
+ doc["stat_t"] = "~/motion";
+ doc["dev_cla"] = "motion";
+
+ commonDiscovery(&doc);
+ serializeJson(doc, buffer);
+
+ for (int i = 0; i < 10; i++)
+ {
+ if (mqttClient.publish(discoveryTopic.c_str(), 0, true, buffer))
+ return true;
+ delay(50);
+ }
+ return false;
+}
+
bool sendDiscoveryMaxDistance()
{
if (!discovery) return true;