Möglichkeiten zur Anwesenheitserkennung gibt in openHab viele. Ich habe es mit dem Network Binding und über Geolocation mit IFTTT versucht. Beide Varianten waren leider nicht so zuverlässig wie ich mir das gewünscht habe. Eine neue Lösung musste her. In meinem Fall bin ich immer dann anwesend, wenn mein Auto in der Garage steht und umgekehrt. Da liegt es nahe die Anwesenheit in Abhängigkeit vom Auto in der Garage zu schalten.
Ein Ultraschallsensor bietet die perfekte Lösung. An der Wand der Garage installiert, misst der Sensor den Abstand zum nächsten Hindernis. Steht das Auto in der Garage ist der Abstand sehr gering. Für den Sensor kommt ein NodeMCU (ESP8266) und der Ultraschallsensor RCW-0001 zum Einsatz. Die Artikelliste:
Ultraschallsensor RCW-0001, 3,3Volt
Vorbereitung für Anwesenheitserkennung
Zunächst einmal muss der NodeMCU per USB mit dem Computer verbunden und für die Programmierung vorbereitete werden. Wie das im einzelnen funktioniert werde ich an dieser Stelle ausklammern und verweise auf eine super Anleitung von OpenDataLab, die auch mir sehr geholfen hat. Sobald das erledigt ist, kann der Sensor an das Board angeschlossen werden. Um einen ersten schnellen (fliegenden) Aufbau zu bewerkstelligen, kann entweder eine Steckplatine genutzt werden, oder Female Jumper Kabel. Beides ist in der Artikelliste oben aufgeführt. Die Pins werden folgendermaßen miteinander verbunden:
Programmierung NodeMCU
Um die Daten vom Sensor auslesen und an openHAB übertragen zu können, muss der NodeMCU entsprechend programmiert werden. Zur Übertragung an openHAB nutzen wir das MQTT Protokoll. Für openHAB gibt es ein passendes MQTT Binding. Als Broker kann ein openMQTT Dienst wie http://broker.mqtt-dashboard.com genutzt werden.
Benötigte Bibliotheken
Für dieses Projekt benötigen wir folgende Bibliotheken einige Bibliotheken, die, sofern noch nicht geschehen, installiert werden müssen. Das wird im Menü Sketch – Bibilothek einbinden – Bibliotheken verwalten. Es öffnet sich der Bibliotheksverwalter. Hier über die Suche die folgenden Bibliotheken suchen und installieren:
- ESP8266WiFi
- PubSubClient
Der Abstandssensor hat folgenden Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
#include <ESP8266WiFi.h> #include <PubSubClient.h> // NodeMCU Pin D1 (5) > TRIGGER | Pin D2 (4) > ECHO #define TRIGGER 5 #define ECHO 4 // Update these with values suitable for your network. const char* ssid = "XXXXXXXXXX"; const char* password = "XXXXXXXX"; const char* mqtt_server = "broker.hivemq.com"; WiFiClient espClient; PubSubClient client(espClient); long lastMsg = 0; char msg[50]; int value = 0; void setup_wifi() { delay(100); // We start by connecting to a WiFi network Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } randomSeed(micros()); Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); } void reconnect() { // Loop until we're reconnected while (!client.connected()) { Serial.print("Attempting MQTT connection..."); // Create a random client ID String clientId = "ESP8266Client-"; clientId += String(random(0xffff), HEX); // Attempt to connect if (client.connect(clientId.c_str())) { Serial.println("connected"); //once connected to MQTT broker, subscribe command if any client.subscribe("OsoyooCommand"); } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" try again in 5 seconds"); // Wait 6 seconds before retrying delay(6000); } } } //end reconnect() void setup() { Serial.begin(115200); Serial.setTimeout(2000); // Wait for serial to initialize. while(!Serial) { } Serial.begin(115200); setup_wifi(); client.setServer(mqtt_server, 1883); pinMode(TRIGGER, OUTPUT); pinMode(ECHO, INPUT); if (!client.connected()) { reconnect(); } } void loop() { long duration, distance; digitalWrite(TRIGGER, LOW); delayMicroseconds(2); digitalWrite(TRIGGER, HIGH); delayMicroseconds(10); digitalWrite(TRIGGER, LOW); duration = pulseIn(ECHO, HIGH); distance = (duration/2) / 29.1; //Serial.print(distance); //Serial.println("Centimeter:"); delay(1000); if (!client.connected()) { reconnect(); } client.loop(); long now = millis(); if (now - lastMsg > 15000) { lastMsg = now; String msg = (String)distance; char messagedistance[58]; msg.toCharArray(messagedistance, 58); Serial.println(messagedistance); //publish sensor data to MQTT broker client.publish("Haus/EG/Garage/Sensor/Abstand", messagedistance); } } |
Die Werte für SSID und Password müssen durch die eigenen Netzwerkdaten ersetzt werden. Sollte ein eigener MQTT Broker im Einsatz sein, ist auch die entsprechende Adresse bei mqtt_server einzutragen. Solange keine sensiblen Daten übertragen werden, kann auch durchaus auf einen openMQTT Service zurückgegriffen werden. In diesem Fall muss nichts geändert werden. Wichtig: Der NodeMCU unterstützt nur das 2,4 GHz Band.
Noch einmal alle anpassbaren Parameter zusammengefasst:
const char* ssid = „XXXXXXXXXX“; //Eigenen Netzwerknamen eintragen
const char* password = „XXXXXXXX“; //Passwort Netzwerk eingeben
const char* mqtt_server = „broker.hivemq.com“; //MQTT Broker eintragen, bzw. hivemq nutzen.
client.publish(„Haus/EG/Garage/Sensor/Abstand“, messagedistance); // Topic vergeben in diesem Bereich “ „. Sinnvoll nach Räumen und Funktion benennen.
Funktion testen
Bevor der Sensor in openHab eingebunden wird, ist es sinnvoll zu testen ob die Werte korrekt zum Broker übertragen werden. So können Fehler direkt an der Quelle ausgeschlossen werden. Hierzu nutze ich den kostenlosen MQTT Client MQTT.fx Damit kannst du beim Broker, in unserem Fall broker.hivemq.com, das Topic abonnieren (Subscribe) und schauen ob die Werte ankommen.
In der Adressleiste des Clients wird die Adresse des Brokers eingetragen. Der Port ist bereits ausgefüllt. Mit Connect wird die Verbindung aufgebaut.
Im nächsten Schritt wird Subscribe ausgewählt und die darunterliegende Leiste das Topic eingetragen. Hier Haus/EG/Garage/Sensor/Abstand. Mit Enter bestätigen. Die Daten werden vom Sensor im 15 Sekunden Takt übertragen. Nach spätestens 15 Sekunden sollte der erste Wert auftauchen. Wenn das funktioniert, kann der Sensor in openHAB integriert werden.
Items in openHAB anlegen
Sofern das MQTT Binding installiert ist, muss jetzt noch ein entsprechendes Item angelegt werden, das die Abstandswerte liest. Wie das MQTT Binding installiert und eingerichtet wird, kannst du hier nachlesen. Hierzu legst du am besten eine neue .items Datei mit dem Namen mqtt.items an. So behältst du die Übersicht wo sich deine Items befinden.
1 |
Number Abstand_Garage "Abstand [%s]" {mqtt="<[mosquitto:Haus/EG/Garage/Sensor/Abstand:state:default]"} |
Aufgeschlüsselt steht in diesem Item: Es ist eine Nummer (Number) mit dem Namen Abstand_Garage, der Bezeichnung „Abstand [%s]“ ([%s] zeigt den String Wert an, also den Abstand, den der Sensor liefert) und als Quelle fungiert mqtt. Bitte beachte, dass die mqtt Adresse abweichen kann. Ich habe meinen Broker in der mqtt.cfg mosquitto genannt, daher mosquitto:.
Anwesenheitserkennung in Sitemap und Rules
In erster Linie möchten wir ja über diesen Wert definieren wann jemand zu Hause ist. Dafür benötigen wir einen Dummy Switch und eine Regel. Den Dummy Switch kannst du direkt in die mqtt.rules packen.
1 |
Switch Anwesenheit "Anwesend" |
In der Regel legen wir fest wann die Anwesenheit auf AN oder AUS schalten soll. Die einfachste Version der Regel sieht dann so aus:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
rule "Anwesend" when Item Abstand_Garage received update then if (Abstand_Garage.state < 60 && Anwesenheit.state == OFF) { sendCommand(Anwesenheit, ON) } if (Abstand_Garage.state > 200 && Anwesenheit.state == ON) { sendCommand(Anwesenheit, OFF) } end |
Die Regel besagt:
Wenn das Item Abstand_Garage einen neuen Wert bekommt
Sofern der Wert kleiner 60 ist und mein Schalter „Anwesenheit“ AUS ist –> Schalte Anwesenheit AN
Sofern der Wert größer 200 ist und mein Schalter „Anwesenheit“ AN ist –> Schalte Anwesenheit AUS
Die zusätzliche Prüfung des aktuellen Status von Anwesenheit bewirkt, dass nicht bei jedem Updates des Abstandswertes (alle 15 Sekunden) ein Kommando in Richtung Anwesenheit geschickt wird. Es würde sich zwar der der Status des Schalters nicht immer zwingend ändern, aber dennoch verursacht es unnötige Zeilen im Log 😉
Nun kommt es bei mir in der Garage ab und zu zu einem Lesefehler des Sensors. Immer nur, wenn die Garage leer ist. Ich vermute Schall „Querschläger“, die ein Hindernis vorgaukeln (Achtung: gefährliches Halbwissen! 😀 ) Das hatte zur Folge, dass trotz Abwesenheit der Abstand unter 60 gefallen ist und somit die Anwesenheit inklusive aller dazugehöriger Aktionen (Licht schalten, etc.) ausgelöst hat. Das wollte ich umgehen. Die Idee: Ich baue einen Zähler ein. Der Schwellwert muss mindestens 3x in Folge erreicht werden, bevor die Anwesenheit den Status ändert. An dieser Stelle möchte ich mich bei udo1toni aus dem KNX User Forum bedanken, der mir mit dem Zähler auf die Sprünge geholfen hat.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
var int CounterAnwesenheit = 0 rule "Anwesend" when Item Abstand_Garage received update then if(Abstand_Garage.state < 60 && CounterAnwesenheit < 3) { CounterAnwesenheit = CounterAnwesenheit + 1 } if(Abstand_Garage.state > 200 && CounterAnwesenheit > 0) { CounterAnwesenheit = CounterAnwesenheit - 1 } if (CounterAnwesenheit == 3 && Anwesenheit.state == OFF) { Anwesenheit.sendCommand(ON) } if (CounterAnwesenheit == 0 && Anwesenheit.state == ON) { Anwesenheit.sendCommand(OFF) } end |
Hier passiert zusätzlich zur oben genannten Variante Folgendes:
Wenn der Abstand_Garage < 60 und der Counter < 3 ist, dann zähle beim Counter +1
Wenn der Abstand_Garage > 200 und der Counter > 0 ist, dann zähle beim Counter -1
Erst wenn der Counter 3 erreicht hat, schalte Anwesenheit AN
Erst wenn der Counter 0 erreicht hat, schalte Anwesenheit AUS
Ist man also tatsächlich abwesend ist der Counter dauerhaft auf 0. Kommt es zu einem Lesefehler wird +1 addiert. Der Counter steht bei 1. Es wird noch keine Aktion ausgelöst. Ist der Wert beim nächsten Update wieder hoch genug wird wieder 1 abgezogen und der Counter steht wieder auf 0.
Jetzt ist die Regel zur Anwesenheitserkennung fertig. Jetzt heißt es das Ergebnis noch in der Sitemap anzuzeigen.
1 |
Text item=Anwesenheit label="Status [MAP(anwesend.map):%s]" valuecolor=[==ON="#008000", ==OFF="red"] icon="present" |
Bei der Frage Anwesend oder nicht passe ON oder OFF nicht wirklich. Daher kannst du im Ordner transform eine Datei mit dem Namen anwesend.map und folgendem Inhalt anlegen:
1 2 |
OFF=Abwesend ON=Anwesend |
Zusätzlich habe ich mit valuecolor=[==ON=“#008000″, ==OFF=“red“] dem jeweiligen Status noch eine Farbe gegeben. Das Ergebnis kann sich sehen lassen.
Jetzt kannst du noch weitere Aktion in Abhängigkeit der Anwesenheit schalten. Ich schalte das Flurlicht im Eingang bei Anwesenheit für 4 Minuten, wenn es draußen bereits dunkel ist. So komme ich immer in ein beleuchtetes Haus.
Hier noch einige Bilder der Eigenkonstruktion. Das Gehäuse ist selbst gedruckt. Die Vorlage zum Druck findest du hier.
Hallo Patrick
Vielen Dank für den Tip!
das war mein Fehler 5v 🙂
VCC am Vin am Nodemcu angeschlossen und es geht 🙂
Ausgezeichnet 🙂 Dann viel Spaß mit dem Sensor. Falls du Bilder im Einsatz hast, kannst du gerne was rüberschicken an kontakt@interwebs-ug.de. Ich plane eine kleine Rubrik „Userprojekte“ 🙂
Hallo Patrick
Hab alles nach deiner Anleitung gemacht . bekomme aber nur den 0 gesendet. Hab schon 2 verschiedene Sensoren und nodemcu ausprobiert.
Was mache ich denn falsch?
Hallo Andreas,
hast du darauf geachtet, dass dein Ultraschallsensor mit 3,3Volt arbeitet? Die meisten Sensoren benötigen 5 Volt, der ESP8266 stellt aber nur 3,3 Volt zur verfügung. Sollte dein Sensor mit 5 Volt arbeiten, kannst du ein USB Ladegerät missbrauchen und dir die 5 Volt so holen. Am besten du nutzt dann auch die 5 Volt für dein NodeMCU. Bitte achte darauf, dass du beim NodeMCU niemals externe Spannung und den Micro USB Anschluss gleichzeitig nutzt.
Hallo!
Ich würde gerne zwei Ultraschallsensor mit einem ESP8266 anbinden. Könntest du dazu ein Codebeispiel in deinem Blog aufnehmen?
Danke
Hallo Stefan,
ich habe den Code einmal angepasst. Im Prinzip musst du einen 2. Sensor anlegen und die angeschlossenen Pins definieren und danach alles was mit dem Sensor zu tun hat verdoppeln. Ich konnte es allerdings nicht live testen, da ich aktuell keine 2 Sensoren mehr frei habe.
#include
#include
// Sensor1 NodeMCU Pin D1 (5) > TRIGGER | Pin D2 (4) > ECHO
#define TRIGGER1 5
#define ECHO1 4
// Sensor2 NodeMCU Pin D3 (0) > TRIGGER | Pin D4 (2) > ECHO
#define TRIGGER2 0
#define ECHO2 2
// Update these with values suitable for your network.
const char* ssid = "XXXXXXXXX";
const char* password = "12345678";
const char* mqtt_server = "192.168.X.XXX";
WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char msg[50];
int value = 0;
void setup_wifi() {
delay(100);
// We start by connecting to a WiFi network
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
randomSeed(micros());
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
void reconnect() {
// Loop until we're reconnected
while (!client.connected())
{
Serial.print("Attempting MQTT connection...");
// Create a random client ID
String clientId = "ESP8266Client-";
clientId += String(random(0xffff), HEX);
// Attempt to connect
//if you MQTT broker has clientID,username and password
//please change following line to if (client.connect(clientId,userName,passWord))
if (client.connect(clientId.c_str()))
{
Serial.println("connected");
//once connected to MQTT broker, subscribe command if any
client.subscribe("OsoyooCommand");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 6 seconds before retrying
delay(6000);
}
}
} //end reconnect()
void setup() {
Serial.begin(115200);
Serial.setTimeout(2000);
// Wait for serial to initialize.
while(!Serial) { }
Serial.begin(115200);
setup_wifi();
client.setServer(mqtt_server, 1883);
pinMode(TRIGGER1, OUTPUT);
pinMode(ECHO1, INPUT);
pinMode(TRIGGER2, OUTPUT);
pinMode(ECHO2, INPUT);
if (!client.connected()) {
reconnect();
}
}
void loop() {
long duration1, distance1;
digitalWrite(TRIGGER1, LOW);
delayMicroseconds(2);
digitalWrite(TRIGGER1, HIGH);
delayMicroseconds(10);
digitalWrite(TRIGGER1, LOW);
duration1 = pulseIn(ECHO1, HIGH);
distance1 = (duration1/2) / 29.1;
long duration2, distance2;
digitalWrite(TRIGGER2, LOW);
delayMicroseconds(2);
digitalWrite(TRIGGER2, HIGH);
delayMicroseconds(10);
digitalWrite(TRIGGER2, LOW);
duration2 = pulseIn(ECHO2, HIGH);
distance2 = (duration2/2) / 29.1;
//Serial.print(distance);
//Serial.println("Centimeter:");
delay(1000);
if (!client.connected()) {
reconnect();
}
client.loop();
long now = millis();
if (now - lastMsg > 15000) {
lastMsg = now;
String msg = (String)distance1;
char messagedistance1[58];
msg.toCharArray(messagedistance1, 58);
Serial.println(messagedistance1);
//publish sensor data to MQTT broker
client.publish("Haus/EG/Garage/Sensor/Abstand1", messagedistance1);
String msg = (String)distance2;
char messagedistance2[58];
msg.toCharArray(messagedistance2, 58);
Serial.println(messagedistance2);
//publish sensor data to MQTT broker
client.publish("Haus/EG/Garage/Sensor/Abstand2", messagedistance2);
}
}
Hallo!
Danke für die schnelle Antwort! Ich würde den Sensor gerne mit einem Akku betreiben, weil ich keine Stromquelle am Bestimmungsort habe. Sind mit dem aktuellen Programm alle Stormsparmöglichkeiten ausgenützt!? Verstehe ich richtig, dass alle 15 Sekunden das WLAN aufgebaut wird und die Daten gesendet? Oder ist das WLAN Module ständig in Kommunikation?
LG Stefan
Hallo Stefan,
das mit dem Akku ist bei dem Node so eine Sache. Im aktuellen Code ist noch kein DeepSleep enthalten. Für den DeepSleep Mode musst du
1.
den GPIO16 Pin mit dem Reset Pin verbinden und
2.
vor der abschließenden } des loops ESP.deepSleep(3600e6); einfügen. Die Zahl in der Klammer steht für die Mikrosekunden, die der Node schlafen soll.
Meine Versuche haben allerdings gezeigt, dass die Batterie/Akku ziemlich groß sein müsste, um eine sinnvolle Betriebszeit zu erreichen. Im DeepSleep hat der Node um die 10mA verbraucht. Wenn er „wach“ ist liegt der Wert bei guten 80-90mA. Das hat bei stündlichem Aufwachen bei einer 2000mAh Batterie für ca. 6 Tage gereicht. Es gibt wohl Anleitungen wie man durch Ablöten verschiedener Teile am Node deutlich längere Zeiten erreichen kann. Das war mir persönlich allerdings zu aufwändig 😉
Viele Grüße
Patrick