Nachdem ich bereits mit dem Feinstaubsensor und dem Temeraturfühler erste Arduino-Luft geschnuppert hab, stand das nächste „Problem“ auf der Liste: Meine Garagentore.
Bisher hatte ich für jedes Garagentor einen Raspberry Pi mit Homebridge und homebridge-rasppi-gpio-garagedoor im Einsatz. Das hat zwar funktioniert, war mir aber immer schon ein Dorn im Auge, da die Raspberry Pis für diese einzelne Anwendung etwas oversized waren. Zudem hat ein Raspberry Pi bzw. die SD-Karte den Geist aufgegeben. Daher war es wieder etwas Zeit für eine Bastelstunde.
Da sich meine C++ Fähigkeit stark in Grenzen hält, hab ich mir große Teile des Codes aus dem Internet zusammengeklaut. Mein Ziel war es, auf dem Arduino einen Webserver laufen zu lassen. Dieser sollte bei einem normalen Aufruf den Status des Tors (OPEN, CLOSED, OPENING, CLOSING) ausgeben, mit einem Parameter (?switch=1) das angeschlossene Relais schalten.
Neben dem Relais sollte noch ein Kontaktschalter am Tor abgefragt werden, der bei geschlossenem Tor ebenfalls geschlossen ist. Auf einen „Offen“-Kontaktschalter habe ich verzichtet, da ich nur bei einem Garagentor eine zuverlässige Möglichkeit zur Befestigung gefunden hab.
Der Code
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#define RelaisPin D4
#define ClosePin D1
const char* ssid = "WLAN-SSID";
const char* password = "WLAN-PASSWORD";
const char* doorstatus = "";
int currentstate;
int timetoopen = 17;
unsigned long switchedtime;
ESP8266WebServer server(80);
void setup()
{
pinMode(RelaisPin, OUTPUT);
pinMode(ClosePin, INPUT_PULLUP);
digitalWrite(RelaisPin, 1);
Serial.begin(115200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
}
server.on("/", BuildIndex);
server.onNotFound ( handleNotFound );
InitialDoorStatus();
server.begin();
}
void loop()
{
server.handleClient();
if (millis() < (switchedtime + 200)) {
}
else if (digitalRead(ClosePin) != currentstate && digitalRead(ClosePin) == 1) {
doorstatus = "OPENING";
switchedtime = millis();
currentstate = 1;
}
else if (digitalRead(ClosePin) != currentstate && digitalRead(ClosePin) == 0) {
InitialDoorStatus();
}
else if (millis() > (1000 * timetoopen) + switchedtime && doorstatus == "OPENING") {
InitialDoorStatus();
}
else if (millis() > (30000 + switchedtime)) {
InitialDoorStatus();
}
delay(100);
}
void DoSwitch() {
digitalWrite(RelaisPin, 1 ^ 1);
delay(1000);
digitalWrite(RelaisPin, 1 ^ 0);
switchedtime = millis();
}
void BuildIndex()
{
server.sendHeader("Cache-Control", "no-cache");
if (server.arg("switch") == "1")
{
if (digitalRead(ClosePin) == 1) {
doorstatus = "CLOSING";
}
else if (digitalRead(ClosePin) == 0) {
doorstatus = "OPENING";
}
DoSwitch();
}
server.send(200, "text/html", doorstatus);
}
void InitialDoorStatus()
{
currentstate = digitalRead(ClosePin);
if (digitalRead(ClosePin) == 1) {
doorstatus = "OPEN";
}
else if (digitalRead(ClosePin) == 0) {
doorstatus = "CLOSED";
}
}
void handleNotFound()
{
server.send(404, "text/plain", "404: Not found");
}
Mein Code lädt zunächst die beiden Libraries für WLAN und den Webserver. Danach definiere ich den Relais-Pin (D4), der das Relais schaltet und den Kontaktschalter-PIN (D1) der den „Geschlossen“-Status des Tors liefert. Die timetoopen ist die Zeit, wie lange der Status des Tors nach dem Öffnen-Impuls auf „OPENING“ steht, bevor es dann auf „OPEN“ springt. Dieser Status ist später für die Homebridge Implementierung sinnvoll.
Die Variable „doorstatus“ hält den jeweiligen Status des Tors und gibt ihn über den Webserver aus. Dazu überprüft es regelmäßig den Zustand des Kontaktschalters. Bei einem Schaltvorgang setze ich den doorstatus auf OPENING (wenn der Kontaktschalter geschlossen war) bzw. CLOSING (wenn der Kontaktschalter offen war). Gleichzeitig merke ich mir die Laufzeit des Arduino beim Auslösen des Schaltvorgangs.
Beim öffnenden Tor setze ich den Status nach der angegebenen Zeit in timetoopen auf OPEN, beim Schließvorgang setzt das Schließen des Kontakts den Status von CLOSING auf CLOSED. Sollte hier nach 30sek keine Änderung des Kontaktzustands erfolgen (z.B: wenn das Tor manuell angehalten wird), frage ich den Status erneut ab.
Ich hoffe, das Script ist einigermaßen verständlich…
Die Homebridge-Anbindung
Für die Einbindung in Homebridge benutze ich homebridge-garagedoor-command mit folgenden Einstellungen:
{
"accessory": "GarageCommand",
"name": "Garage Door",
"open": "curl 'http://MY_IP/?switch=1'",
"close": "curl 'http://MY_IP/?switch=1'",
"state": "curl 'http://MY_IP/'",
"status_update_delay": 17,
"poll_state_delay": 20
}
Das war’s.