DoorPi und FHEM: Unterschied zwischen den Versionen
(114 dazwischenliegende Versionen von 7 Benutzern werden nicht angezeigt) | |||
Zeile 1: | Zeile 1: | ||
Das [https://www.doorpi.org/forum/ DoorPi- | {{ | ||
Infobox Modul | |||
|ModPurpose=Das Modul verknüpft eine IP-Türsprechstelle aus dem DoorPi-Projekt mit FHEM. | |||
|ModType=h | |||
<!-- |ModCategory= (noch?) nicht verwendet --> | |||
|ModCmdRef=DoorPi | |||
|ModForumArea=Automatisierung | |||
|ModTechName=70_DoorPi.pm | |||
|ModOwner=Prof. Dr. Peter A. Henning | |||
}} | |||
Auf dieser Seite wird beschrieben, wie man mit Hilfe der [https://www.doorpi.org/forum/ DoorPi-Software] auf Basis eines Raspberry Pi eine '''IP-Türsprechstelle''' baut und in FHEM integriert. | |||
''' | Weitere Bestandteile des Gesamtsystems sind ein '''Garagentoröffner''' und eine '''Hoftür mit selbstverriegelndem Panikschloss'''. Zur Beschreibung der beiden letztgenannten Teilprojekte verweise ich auf das Buch [https://www.dpunkt.de/buecher/12387/9783960090120-smarthome-hacks.html Smart Home Hacks]. | ||
Eine kompakte Platine, welche die im Folgenden beschriebene Schaltung enthält und einen wesentlich einfacheren Aufbau ermöglicht, wird auf der Seite [[DoorPiBoard]] beschrieben. | |||
'''Achtung: Da die DoorPi-Software nicht weiter entwickelt wird, ist derzeit eine {{Link2Forum|Topic=101299|Message=947413|LinkText=Umstellung auf FHEM im Gange, die auf derselben Hardware laufen soll}}''' | |||
[[File:Doorpi_all_s.png]] | |||
=Funktion= | =Funktion= | ||
Die Software DoorPi läuft auf einem Raspberry Pi und stellt einen IP-Phone Client zur Verfügung, der in eine IP- | Die Software DoorPi läuft auf einem Raspberry Pi und stellt einen IP-Phone Client zur Verfügung, der in eine IP-Haustelefonanlage (z.B. mit der FritzBox) eingebunden werden kann. Diesen Raspberry Pi könnte man zwar über längere Audiokabel mit der notwendigen Lautsprecher-Mikrofon-Kombination verbinden, das ist allerdings etwas anfällig für Störsignale. Die meisten Anwender von DoorPi wählen deshalb den Einbau des Raspberry Pi direkt an der Türsprechstelle. Aus Sicht des Autors stellt das eine Sicherheitslücke dar - weil ein direkter Zugang ins Heimnetz außerhalb der eigenen vier Wände geschaffen würde. | ||
Hier soll deshalb eine abgesetzte Installation beschrieben werden: | Hier soll deshalb eine abgesetzte Installation beschrieben werden: Außen an der Türsprechstelle sitzen nur Sensoren, Audiohardware und ein Display, angesteuert von einem "dummen" Arduino Mikrocontroller. Ins Innere des Hauses führen drei Kabel: | ||
*Ein USB-Kabel zur digitalen Ankopplung der Audio-Hardware an den Raspberry Pi | *Ein USB-Kabel zur digitalen Ankopplung der Audio-Hardware an den Raspberry Pi | ||
*Ein HDMI-Kabel zur digitalen Ankopplung einer Kamera an den Raspberry Pi | *Ein HDMI-Kabel zur digitalen Ankopplung einer Kamera an den Raspberry Pi | ||
*Ein 8-adriges Kabel zur Weiterleitung verschiedener Signale an den Raspberry Pi. Hierfür kann man aus Bequemlichkeitsgründen ein Netzwerk-Patchkabel verwenden - hochfrequente Signale gehen allerdings nicht über diesen Weg. | *Ein 8-adriges Kabel zur Weiterleitung verschiedener Signale an den Raspberry Pi. Hierfür kann man aus Bequemlichkeitsgründen ein Netzwerk-Patchkabel verwenden - hochfrequente Signale gehen allerdings nicht über diesen Weg. | ||
[[File:Doorpi_block.png]] | [[File:Doorpi_block.png]][[File:doorpi_completes.png]][[File:doorpi_completeb.png]] | ||
==Klingel und Türsprechstelle== | ==Klingel und Türsprechstelle== | ||
Beim Drücken auf den Klingelknopf wird ein IP-Telefonanruf gestartet - entweder bei einer internen Nummer, oder ( | Beim Drücken auf den Klingelknopf wird ein IP-Telefonanruf gestartet - entweder bei einer internen Nummer, oder (per Mausklick auswählbar in FHEM) bei einer beliebigen anderen Nummer. Wenn der Empfänger den Ruf annimmt, kann er mit dem Besucher sprechen und ggf. durch das Drücken einer Taste die Tür öffnen. Es ist in der DoorPi-Installation problemlos konfigurierbar, beliebige andere Aktionen zu starten - z.B. könnte man dem Paketlieferanten nur das Gartentor öffnen. | ||
{| | |||
| [[File:Pizzabote.jpg |200px]] | |||
| [[File:Dhlbote.jpg |200px]] | |||
| Während des Klingelvorgangs wird eine Infrarot-Lichtquelle eingeschaltet und mit einem Weitwinkelobjektiv eine Aufnahme des Besuchers angefertigt. Diese IR-Beleuchtung soll nicht die ganze Szene ausleuchten - sondern verhindern, dass man das Gesicht des Besuchers auf Grund einer hinter ihm befindlichen Beleuchtung mit sichtbarem Licht nicht gut erkennen kann. Die Infrarotempfindlichkeit der Kamera sorgt bei Tageslicht für einen Rotstich des Bildes. Lichtbrechung durch den Kameradom sorgt dafür, dass man außen um das Bild herum einen leuchtenden Ring mit 8 Segmenten erkennt. | |||
Die URL dieses Schnappschusses wird ferner im DoorPi-Device als Reading '''snapshot''' angezeigt, sowie ein Event '''<DoorPi-Device> snapshot: URL''' ausgelöst, mit dem man weitere Aktionen triggern kann. Beispielsweise wird eine Push-Nachricht an ein Tablet gesendet, das den Klingelnden für eine Minute als Bild anzeigt. | |||
|} | |||
Das Gespräch wird als WAV-Datei ebenfalls aufgezeichnet, diese ist ebenso wie die Bilddatei aus dem FHEM-Frontend abrufbar. | |||
==Türöffnung== | ==Türöffnung== | ||
Das System verfügt über einen iButton-Reader. Legt man einen iButton auf, der in der Software codiert ist, leuchtet eine im Reader integrierte Tricolor-LED in einer entsprechenden Farbe auf (z.B. roter iButton-Halter => rote LED). Der weitere Ablauf hängt vom Schließzustand der Haustür ab. | Das System verfügt über einen iButton-Reader. Legt man einen iButton auf, der in der Arduino-Software codiert ist, leuchtet eine im Reader integrierte Tricolor-LED in einer entsprechenden Farbe auf (z.B. roter iButton-Halter => rote LED). Der weitere Ablauf hängt vom Schließzustand der Haustür ab. | ||
*Ist die Haustür nur zugezogen und nicht abgeschlossen, wird sie geöffnet. Gleichzeitig wird über die Türsprechstelle die Stimmnachricht ausgegeben "Willkommen zu Hause !", und auf einem im Haus befindlichen Tablet angesagt "Ein Bewohner betritt das Haus." | *Ist die Haustür nur zugezogen und nicht abgeschlossen (Zustand '''hardlock=off'''), wird sie geöffnet. Gleichzeitig wird über die Türsprechstelle die Stimmnachricht ausgegeben "Willkommen zu Hause !", und auf einem im Haus befindlichen Tablet angesagt "Ein Bewohner betritt das Haus." | ||
*Ist die Haustür abgeschlossen, wird zunächst auf dem Touchscreen des Systems statt der Namen eine virtuelle Tastatur angezeigt. Hier kann ggf. noch die akustische Bitte eingebaut werden, eine fünfstellige PIN einzugeben. Der Fortschritt bei der Eingabe wird durch einen Balken angezeigt. | *Ist die Haustür abgeschlossen (Zustand '''hardlock=on'''), wird zunächst auf dem Touchscreen des Systems statt der Namen eine virtuelle Tastatur angezeigt. Hier kann ggf. noch die akustische Bitte eingebaut werden, eine fünfstellige PIN einzugeben. Der Fortschritt bei der Eingabe wird durch einen Balken angezeigt. | ||
**Ist die PIN falsch, geht das System wieder in den | **Ist die PIN falsch, geht das System wieder in den Ausgangszustand zurück, es wird eine Warnungsmeldung an das interne FHEM-System übermittelt. | ||
**Ist die PIN korrekt, wird zunächst der Entriegelungsvorgang der Tür eingeleitet. Je nach verwendetem System kann das eine Weile dauern - Keymatic ist hierfür ein Beispiel, das können durchaus 3-4 Sekunden werden. Danach wird die Tür geöffnet, gleichzeitig wird über die Türsprechstelle die Stimmnachricht ausgegeben "Willkommen zu Hause !", und auf einem im Haus befindlichen Tablet angesagt "Ein Bewohner betritt das Haus." Je nach Konfiguration kann FHEM an dieser Stelle auch weitere Aktionen auslösen - etwa weitere Entriegelungsvorgänge starten. | **Ist die PIN korrekt, wird zunächst der Entriegelungsvorgang der Tür eingeleitet. Je nach verwendetem System kann das eine Weile dauern - Keymatic ist hierfür ein Beispiel, das können durchaus 3-4 Sekunden werden. Danach wird die Tür geöffnet, gleichzeitig wird über die Türsprechstelle die Stimmnachricht ausgegeben "Willkommen zu Hause !", und auf einem im Haus befindlichen Tablet angesagt "Ein Bewohner betritt das Haus." Je nach Konfiguration kann FHEM an dieser Stelle auch weitere Aktionen auslösen - etwa weitere Entriegelungsvorgänge starten. | ||
==Verwendung des Hausschlüssels== | |||
*Verriegelt man die Haustür mit einem gewöhnlichen mechanischen Schlüssel, teilt dies die Keymatic dem FHEM-System mit (Zustand '''hardlock=on'''), das FHEM-System informiert den DoorPi-Rechner. | |||
*Entriegelt man die Haustür mit einem gewöhnlichen mechanischen Schlüssel, teilt dies die Keymatic dem FHEM-System mit (Zustand '''hardlock=off'''), das FHEM-System informiert den DoorPi-Rechner. | |||
==Fernbedienung== | |||
Alle Funktionen, z.B. die Ent- und Verriegelung und das Türöffnen sind selbstverständlich auch über FHEM (und damit über ein Funksystem, im konkreten Fall über HomeMatic) steuerbar. | |||
==Videostream== | ==Videostream== | ||
Auf dem Raspberry Pi ist auch die Software mjpeg_streamer installiert, Per Mausklick in der FHEM-Oberfläche wird mjpeg-streamer gestartet und gestoppt (das wird von der DoorPi-Software erledigt, die einen bestimmten virtuellen | Auf dem Raspberry Pi ist auch die Software mjpeg_streamer installiert, Per Mausklick in der FHEM-Oberfläche wird mjpeg-streamer gestartet und gestoppt (das wird von der DoorPi-Software erledigt, die einen bestimmten virtuellen Tastendruck erkennt und dann lokal ein Skript startet), und stellt dann an TCP-Port 9000 einen Videostream bereit. Wer möchte, kann das System auch so konfigurieren, dass es diesen Videostream zusammen mit dem Audiostream zur Videotelefonie verwendet. | ||
==Bewegungserkennung und Helligkeitsmessung== | ==Bewegungserkennung und Helligkeitsmessung== | ||
Erkennt das System durch den eingebauten PIR-Bewegungsmelder eine Bewegung, wird | Erkennt das System durch den eingebauten PIR-Bewegungsmelder eine Bewegung, wird | ||
*die Anzeigehelligkeit für mindestens eine Minute hochgefahren | *die Anzeigehelligkeit für mindestens eine Minute hochgefahren | ||
*ein Signal an FHEM gesendet. | *ein Signal an FHEM gesendet. | ||
Wenn eine Minute keine Bewegung erkannt wurde, geht die Anzeigehelligkeit (d.h. vom Display und dem LED- | Wenn eine Minute keine Bewegung erkannt wurde, geht die Anzeigehelligkeit (d.h. vom Display und dem LED-Ring um den Klingelknopf) wieder auf einen Wert zurück, der durch die externe Helligkeit bestimmt ist. | ||
Der Bewegungsmelder ist so geschaltet, dass er innerhalb von ca. 1 Minute (tatsächlich sind es mit der unten geposteten Schaltung 47 Sekunden), gerechnet von der letzten erkannten Bewegung, keine neuen Bewegungsmeldungen weiterleitet - sondern die Zeitdauer einfach verlängert (retriggerbares Monoflop). | |||
=Hardware= | =Hardware= | ||
==Arbeitsaufwand und Kosten== | |||
In der Zeit von Juni – September 2016 habe ich ca. 150 Arbeitsstunden für Konzeption, Prototyp und Programmierung sowie die Endmontage aufgewandt. Auf Grund der Tatsache, dass sowohl meine 3D-Druckdateien, als auch die Programmbestandteile hier frei verfügbar sind, kann man das sicher in weniger als der halben Zeit nachbauen. | |||
Die reinen Materialkosten betragen etwa 450 €, davon ca. 100 € für einen Raspberry Pi 3 mit Zubehör und Zusatzplatine PiFace 2, ca. 70 € für das Infrarot-Kamerasystem, ca. 40 € für das reine Audio-System, ca. 50 € für Bewegungsmelder, Helligkeitssensor und beleuchteten Klingelknopf. Der zusätzliche Controller vom Typ Arduino Micro und der angeschlossene iButton-Leser schlagen mit insgesamt 20 € zu Buche, das Nextion-Touchscreen mit ca. 30 €. Teuerstes Einzelstück war eine nach Maß gefräste und gebohrte Frontplatte mit ca. 100 €, entsprechende Wandeinbaugehäuse gibt es ab ca. 20 €. Da das Gesamtsystem modular aufgebaut ist, und Komponenten auch in ganz unterschiedlichen Qualitäten verwendet worden sind, kann man eine ''abgespeckte'' Version auch schon für ca. 200 € realisieren. | |||
==Raspberry Pi== | ==Raspberry Pi== | ||
Verwendet wird | Verwendet wird | ||
* ein [http://www.pollin.de/shop/dt/OTQxNzkyOTk-/Bauelemente_Bauteile/Entwicklerboards/Raspberry_Pi/Raspberry_Pi_3_Modell_B.html Raspberry Pi 3], der ein integriertes WLAN-Modul besitzt | * ein [http://www.pollin.de/shop/dt/OTQxNzkyOTk-/Bauelemente_Bauteile/Entwicklerboards/Raspberry_Pi/Raspberry_Pi_3_Modell_B.html Raspberry Pi 3], der ein integriertes WLAN-Modul besitzt. Falls man seiner eigenen WLAN-Bandbreite nicht traut: Für ca. 25 € bekommt man einen Satz Powerline-Adapter, mit denen man eine sehr stabile Verbindung zwischen dem Raspberry Pi (Innenwand neben der Tür) und dem Rest des Hauses aufbauen kann. | ||
* eine Zusatzkarte [http://www.pollin.de/shop/dt/NTc1NzkyOTk-/Bauelemente_Bauteile/Entwicklerboards/Raspberry_Pi/Raspberry_Pi_B_Zusatzplatine_PIFACE_DIGITAL_2.html PiFace 2], mit je 8 digitalen Ein- und Ausgängen. | * eine Zusatzkarte [http://www.pollin.de/shop/dt/NTc1NzkyOTk-/Bauelemente_Bauteile/Entwicklerboards/Raspberry_Pi/Raspberry_Pi_B_Zusatzplatine_PIFACE_DIGITAL_2.html PiFace 2], mit je 8 digitalen Ein- und Ausgängen. | ||
* ein klares [http://www.reichelt.de/CB-RPF-P-CLR/3/index.html?&ACTION=3&LA=446&ARTICLE=160959 Gehäuse], das die Erweiterungskarte mit aufnehmen kann | * ein klares [http://www.reichelt.de/CB-RPF-P-CLR/3/index.html?&ACTION=3&LA=446&ARTICLE=160959 Gehäuse], das die Erweiterungskarte mit aufnehmen kann. | ||
Das Gehäuse mit Raspberry Pi, ein Netzschalter, ein 5V Netzteil und ein Powerline-Adapter wurden in ein kompaktes Hutschienengehäude montiert, das durch einen Mauerdurchbruch (ca. 3cm Durchmesser wegen der HDMI-Stecker, siehe dort) mit der außenliegenden Türsprechstelle verbunden ist. | |||
==Kamera-Subsystem== | ==Kamera-Subsystem== | ||
Verwendet werden hierfür | Verwendet werden hierfür | ||
* eine Standard-NoIR-Kamera für den Raspberry Pi (ohne Infrarotfilter, somit IR-empfindlich | * eine Standard-NoIR-Kamera für den Raspberry Pi (ohne Infrarotfilter, somit IR-empfindlich, z.B. erhältlich [http://www.elv.de/raspberry-pi-noir-kamera-modul.html hier]) | ||
* ein Fisheye 180° zum Aufclippen auf Smartphones. Die untenstehenden Stereo-Litografie-Dateien sind für das [http://www.elv.de/bresser-handy-kameraaufsatz-clip-on-180-fisheye-handylinse.html Objektiv von Bresser] entworfen worden, auch erhältlich [https://www.conrad.de/de/smartphone-fish-eye-objektiv-bresser-optik-clip-on-180-fisheye-1359363.html hier]. | * ein Fisheye 180° zum Aufclippen auf Smartphones. Die untenstehenden Stereo-Litografie-Dateien sind für das [http://www.elv.de/bresser-handy-kameraaufsatz-clip-on-180-fisheye-handylinse.html Objektiv von Bresser] entworfen worden, auch erhältlich [https://www.conrad.de/de/smartphone-fish-eye-objektiv-bresser-optik-clip-on-180-fisheye-1359363.html hier]. | ||
* eine [http://www.ebay.de/itm/321915955373?_trksid=p2057872.m2749.l2649 Acrylglaskuppel] 2 Zoll (Achtung, der Durchmesser beträgt 49,5 mm). | * eine [http://www.ebay.de/itm/321915955373?_trksid=p2057872.m2749.l2649 Acrylglaskuppel] 2 Zoll (Achtung, der Durchmesser beträgt 49,5 mm). | ||
* eine [https://www.tindie.com/products/freto/pi-camera-hdmi-cable-extension/ Kabelverlängerung] Flatribbon <-> HDMI <-> Flatribbon | * eine [https://www.tindie.com/products/freto/pi-camera-hdmi-cable-extension/ Kabelverlängerung] Flatribbon <-> HDMI <-> Flatribbon. | ||
* ein Kameradom aus dem 3D-Drucker mit genauer Passung für das Fisheye vorne und die Kamera hinten, sowie Passungen für 8 LED. Die STL-Datei für diesen Kameradom findet man hier: [https:// | ** Tipp: HDMI-Stecker sind ziemlich dick und passen eher schlecht durch enge Kabelkanäle. Hat man dieses Problem, kann man noch an einer Seite einen [http://www.conrad.biz/ce/de/product/1171399/HDMI-Adapter-1x-HDMI-Stecker-1x-HDMI-Buchse-C-mini-vergoldete-Steckkontakte-Schwarz-SpeaKa-Professional Adapter] HDMI <-> Mini-HDMI einsetzen und ein entsprechendes Kabel mit deutlich dünnerem Mini-HDMI-Stecker vewenden. | ||
* ein Kameradom aus dem 3D-Drucker mit genauer Passung für das Fisheye vorne und die Kamera hinten, sowie Passungen für 8 LED. Die STL-Datei für diesen Kameradom findet man hier: [https://tinkercad.com/things/jv5x6lrihbk PiCameraDome4] | |||
* eine Rückseite des Kameradoms aus dem 3D-Drucker, wird hinten mittig aufgeklebt und hält den Deckel des Kamerasystem durch drei Nippel. Die STL-Datei dafür findet man hier [https://tinkercad.com/things/cV6rkY6DWvc PiCameraDome4addendum] | * eine Rückseite des Kameradoms aus dem 3D-Drucker, wird hinten mittig aufgeklebt und hält den Deckel des Kamerasystem durch drei Nippel. Die STL-Datei dafür findet man hier [https://tinkercad.com/things/cV6rkY6DWvc PiCameraDome4addendum] | ||
* ein Deckel des Kameradoms aus dem 3D-Drucker, wird auf die Rückseite aufgesteckt und beinhaltet zwei Halterungen für Mikrofone (die "Ohren" im Bild). Die STL-Datei dafür findet man hier [https://tinkercad.com/things/8X0fhiSTyXk PiCameraDome4back] | * ein Deckel des Kameradoms aus dem 3D-Drucker, wird auf die Rückseite aufgesteckt und beinhaltet zwei Halterungen für Mikrofone (die "Ohren" im Bild). Die STL-Datei dafür findet man hier [https://tinkercad.com/things/8X0fhiSTyXk PiCameraDome4back] | ||
Zeile 63: | Zeile 104: | ||
==Audio-Subsystem== | ==Audio-Subsystem== | ||
* | *Als Sound"karte" wird mit dem [https://www.amazon.de/gp/product/B005BYCBO8 BIGtec 7.1 USB Adapter] ein preiswertes Produkt verwendet, dessen Gehäuse sich problemlos entfernen lässt. Weitere Modelle werden im oben zitierten DoorPi-Forum empfohlen. | ||
*Der Audioverstärker ist ein Modell von [https://www.amazon.de/gp/product/B00UAA7NH8 Foxnovo] mit einer Ausgangsleistung von 2x3 W. Auch hierfür können nahezu beliebige andere Modelle verwendet werden | |||
* | *Als Lautsprecher kommen zwei [https://www.reichelt.de/VIS-K28-40-8/3/index.html?&ACTION=3&LA=446&ARTICLE=145413&artnr=VIS+K28.40-8&SEARCH=kleinlautsprecher VISATON Kleinlautsprecher VIS K28.40-8] mit den Maßen 2,8 x 4 cm und einer Impedanz von 8 Ohm zum Einsatz. Zwischen Lautsprecher und Frontplatte ist ein Lautsprechervlies angebracht - damit ergibt sich ein effektiver Spritzwasserschutz, allerdings keine vollständige Abdichtung gegen Feuchtigkeit. Betriebserfahrungen liegen noch nicht vor ! | ||
*Als Mikrofon können nahezu beliebige preiswerte [https://www.amazon.de/gp/product/B000WGW96K Mikrofonkapseln] für Sprachqualität verwendet werden. Bei diesen handelt es in der Regel um Kristallmikrofone, die einen ausreichenden Spannungspegel für den Audioverstärker erzeugen. Wichtig ist die gute akustische Entkopplung von Lautsprechern und Mikrofonen - darum werden die Lautsprecher fest auf die Frontplatte geschraubt, die Mikrofone aber in den Halter am Kameradom von hinten eingeschoben. Eine Fixierung der Mikrofone mit dem bereits vorher verwendeten Silikon ist empfehlenswert, ebenso eine Vliesschicht zwischen Mikrofon und Frontplatte. | |||
* Lautsprecher | Steckkontakte aller Art sind bei Installationen im Außenbereich immer ein Risiko. Für das gegenwärtige Projekt wurden deshalb das Gehäuse der Sound"karte" und die enthaltenen 3,5 mm Klinkenbuchsen entfernt. Zwei Mikrofonkabel wurden direkt angelötet, die Ausgänge per Draht mit der Verstärkerplatine verbunden, deren Ausgänge wiederum mit Schraubklemmen versehen. Etwas Zweikomponentenkleber macht daraus eine kompakte Einheit, die oberhalb des Displays auf einer Lochrasterplatte befestigt und per USB-Kabel mit dem Raspberry Pi verbunden wird. | ||
* | Der verwendete Verstärker hat einen Schalteingang (weißes Kabel im Bild) - wird dieser auf Low=GND gesetzt, ist der Verstärker ausgeschaltet und verbraucht keinen Strom. Dieser Schalteingang wird auf den Ausgang DLA der Arduino-Platine geführt. | ||
[[File:doorpi_audios.jpg|800x300px]] | |||
==Sensoren== | ==Sensoren== | ||
* | * Der [http://www.elv.de/bewegungsmeldermodul-pir-13.html PIR-Bewegungsmelder] ist ein sehr kleines Modul, das einfach von hinten in eine Bohrung der Frontplatte gesteckt wird. Sein Open-Collector-Ausgang muss noch mit einem retriggerbaren Monoflop = Timer versehen werden. Damit ist sichergestellt, dass erneute Triggervorgänge nicht neue Events in Doorpi bzw. FHEM auslösen. Hierfür gibt es zwei Möglichkeiten: | ||
* Fototransistor | ** Bausatz PIR13TM, seit kurzem erhältlich bei ELV. Kleine Zusatzplatine, die auf das Bewegungsmeldermodul aufgesteckt wird. | ||
* [https://www.amazon.de/gp/product/B01DKTHDXE Klingelknopf] | ** Eigenbau auf Basis des LM555, siehe Schaltplan unten. Innerhalb einer Zeit von ca. 50 Sekunden (bestimmt durch R8 und C2) wird kein neuer Impuls ausgelöst. Der Ausgang des LM555 wird durch einen MOSFET invertiert und dann über eine der unbenutzten Adern (blau im Bild) des HDMI-Kabels an den Raspberry Pi weitergeleitet. Achtung: Dieses Monoflop sitzt auf einer kleinen Zusatzplatine, die im rechten unteren Bild noch nicht aufgesteckt ist. | ||
* Mikroschalter als Sabotagekontakt | * Ein Fototransistor wird mit zwei festen und einem einstellbaren Widerstand als Helligkeitssensor verwendet. Seine Ausgangsspannung wird an den Arduino weitergeleitet, der auf Grund dieses Wertes das Display und den LED-Ring des Klingelknopfes dimmt. | ||
* Als [https://www.amazon.de/gp/product/B01DKTHDXE Klingelknopf] wird ein Modell aus Edelstahl mit umlaufendem LED-Ring verwendet. | |||
* Ein Mikroschalter dient als Sabotagekontakt, der so in die Rückwandinstallation geklebt wird, dass er bei der Abnahme der Frontplatte geschlossen wird. Dieses Signal wird über eine der unbenutzten Adern des HDMI-Kabels (orange im Bild) an den Raspberry Pi weitergeleitet. | |||
[[File:DoorPi_part1.png|500x350px]][[File:DoorPi_part2a.png|500x350px]][[File:doorpi_sensorss.jpg|800x350px]] | |||
==Arduino== | |||
Verwendet wird ein Arduino Micro mit dem Programm (Sketch in der Arduino-Terminologie), das [[#Arduino_2|weiter unten]] veröffentlicht ist. Der Arduino sitzt in einer Fassung auf einer Lochrasterplatte, die hinter dem Nextion-Display befestigt ist. | |||
Hauptaufgabe des Arduino ist, in dem iButton-Reader einen Abfragezyklus von 250 ms durchzuführen. Solche hohen Abfrageraten können in der Regel mit Busmastern, die von einer universellen 1-Wire Software wie [[OWX]] oder OWFS angesteuert werden, nicht realisiert werden. Im vorliegenden Projekt übernimmt der Arduino auch die Steuerung des Nextion-Displays über eine seiner seriellen Schnittstellen. | |||
Als | ==Nextion-Subsystem== | ||
Als interaktives Namensschild wird ein | |||
[http://wiki.iteadstudio.com/Nextion_HMI_Solution Nextion Display] 3,2" mit einer Auflösung von 400x240 Pixel verwendet (erhältlich in [http://www.aliexpress.com/item/3-2-TFT-480x240-resistive-touch-screen-display-Nextion-3-2-HMI-LCD-Display-Module-TFT/32443541471.html China] oder in [http://www.komputer.de/zen/index.php?main_page=product_info&cPath=30&products_id=354&zenid=like084ipplb747m4ur447p5h3 Deutschland]). | |||
Die Besonderheit dieses Displays ist der eingebaute Prozessor. Mit Hilfe der zugehörigen Software (erhältlich für Windows auf den Seiten des Herstellers) kann man diesem Prozessor ein regelrechtes GUI (Graphical User Interface) einprogrammieren: Bilder, Texte, Buttons, Fortschrittsbalken, aktive Regionen werden zusammengestellt und können mit Hilfe der entsprechenden Softwarebibliotheken abgerufen werden, wenn sie erst einmal im internen Flash-Memory gespeichert sind. Für diese Zusammenstellung eines Ablaufes stellt der Nextion-Editor auch einen komfortablen Emulator zur Verfügung, mit dem man den Ablauf vor dem Upload ausprobieren kann. | |||
Der Upload auf das Nextion-Display geschieht entweder über die eingebaute serielle Schnittstelle (via USB-Seriell-Adapter für ca. 5 € auch an USB anzuschließen), oder indem mit Hilfe des Nextion Editors eine Micro-SD-Karte beschrieben und in den Kartenslot des Displays gesteckt wird (unten im Bild). Die Ansteuerung des Displays geschieht ebenfalls über die diese serielle Schnittstelle (rechts im Bild). | |||
Zum Schutz des Nextion empfiehlt sich seine Montage an der Frontplatte mit einer entsprechenden [http://www.conrad.biz/ce/de/product/519111/Displayschutzfolie-Passend-fuer-Universal-100-mm-x-150-mm-1-St/SHOP_AREA_19142 Schutzfolie]. Diese Folie kann man Rand etwas überstehen lassen und dort mit Silikon gegen die Frontplatte abdichten. | |||
[[File:doorpi_displays.jpg|800x380px]] | |||
==iButton-Subsystem== | ==iButton-Subsystem== | ||
iButtons sind kompakte Knöpfe aus Stahl, in denen ein Chip mit einer eindeutigen ID sitzt. Durch Auflegen des iButtons auf das Lesegerät wird diese ausgelesen und mit den einprogrammierten IDs verglichen. | |||
* iButtonLesegerät, z.B.: | * iButtonLesegerät, z.B.: | ||
** [http://www.fuchs-shop.com/de/shop/16/1/13372564/ ohne LED] | ** [http://www.fuchs-shop.com/de/shop/16/1/13372564/ ohne LED] | ||
** [http://www.fuchs-shop.com/de/shop/16/1/13372377/ mit LED] - Achtung evtl. nicht ausreichend Wetterfest | ** [http://www.fuchs-shop.com/de/shop/16/1/13372377/ mit LED] - Achtung evtl. nicht ausreichend Wetterfest | ||
** [http://www.aliexpress.com/item/2PCS-TM-probe-DS9092-Zinc-Alloy-probe-iButton-probe-reader-with-LED-M98/32635390937.html aus Fernost] | ** [http://www.aliexpress.com/item/2PCS-TM-probe-DS9092-Zinc-Alloy-probe-iButton-probe-reader-with-LED-M98/32635390937.html aus Fernost] | ||
Mehr über die Technik der iButtons erfährt man in der [[:Kategorie:1-Wire|Katgeorie 1-Wire]]. Achtung: iButtons sind nicht fälschungssicher. Was aber manche Hotels nicht abhält, ihren Gästen als Zimmerschlüssel einen iButton auszuhändigen... | |||
==Frontplatte== | ==Frontplatte== | ||
Zeile 107: | Zeile 156: | ||
==Rückwandinstallation== | ==Rückwandinstallation== | ||
Die Rückwandinstallation, die in des Mauerwerk eingelassen wird, kommt ebenfalls aus dem 3D-Drucker. Sie besteht aus drei Teilen mit unterschiedlicher Tiefe, um das Loch im Mauerwerk nicht zu groß werden zu lassen. Das obere und das untere Teil enthalten jeweils zwei stabile Vorsprünge mit Aussparungen, in die eine normgerechte M4-Mutter von unten genau hineinpasst. Bemerkenswert ist, dass dieses Bauteil nicht mit einer konventionellen Methode (z.B. im Spritzgussverfahren) gefertigt werden kann. | Die Rückwandinstallation, die in des Mauerwerk eingelassen wird, kommt ebenfalls aus dem 3D-Drucker. Sie besteht aus drei Teilen mit unterschiedlicher Tiefe, um das Loch im Mauerwerk nicht zu groß werden zu lassen. Das obere und das untere Teil enthalten jeweils zwei stabile Vorsprünge mit Aussparungen, in die eine normgerechte M4-Mutter von unten genau hineinpasst. Bemerkenswert ist, dass dieses Bauteil nicht mit einer konventionellen Methode (z.B. im Spritzgussverfahren) gefertigt werden kann. Die hineingedrückten Muttern kommen damit genau hinter den äußeren Schraubenlöchern der Frontplatte zu liegen, Die Frontplatte kann dadurch mit vier (Sicherheits-)schrauben M4 passgenau auf die im Mauerwerk sitzende Rückwandinstallation geschraubt werden. Dafür | ||
werden Edelstahlschrauben mit Senkkopf und zwei Löchern auf der Oberseite verwendet (sog. Sicherheitsschrauben). | |||
Auf der Oberkante der drei Teile verläuft eine 1x1 mm² große Nut, in die eine Dichtung eingelegt werden kann. Zur besseren Verankerung im Putz kann das zusammengeklebte Gehäuse noch mit ''Ohren'' versehen werden - siehe die beiden letzten Links in der nachfolgenden List. | |||
Die Bauteile der Rückwandinstallation können aus beliebigem Material gefertigt werden, aber Achtung: Druckt man sie aus dme Material PLA (Poly-Milchsäure), ist zum Schutz gegen biologischen Abbau und Depolymerisation im Mauerwerk ein (äußerer) Überzug mit Sprühlack sinnvoll. | |||
[[File:Doorpi_coverparts.jpg]] | |||
Hier die Links zu den betreffenden Dateien: [https://www.tinkercad.com/things/5DvvrghWe7T-coverbottom4 CoverBottom4] | |||
[https://www.tinkercad.com/things/3oHRTA6XL1g-covermiddle4 CoverMiddle4] | |||
[https://www.tinkercad.com/things/6BOOMGKsd9Z-covertop4 CoverTop4] | |||
[https://www.tinkercad.com/things/kOnZEsCaQI9-coverearc4 CoverEarC4] | |||
[https://www.tinkercad.com/things/86ovGXH3JyK-coverears4 CoverEarS4] | |||
[[File:Doorpi_covertop.jpg]] | [[File:Doorpi_covertop.jpg]] | ||
=Software= | |||
Die Software besteht aus verschiedenen Bestandteilen | |||
* Auf dem Nextion-Display sind so genannte Seiten mit Bildern, Texten und aktiven Elementen (Widgets) gespeichert, diese werden mit dem zugehörigen Nextion-Editor des Herstellers erzeugt und entweder über serielle Schnittstelle oder über eine Micro-SD-Karte auf das Nextion übertragen. | |||
* Auf dem Arduino läuft ein kleines Programm in einer Endlosschleife. Es führt die Abfrage des iButton-Lesers durch und steuert den Seitenwechel des Nextion-Displays, verarbeitet auch dessen Bedienvorgänge. Das Programm (Sketch in der Arduino-Terminologie) wird mit Hife der Arduino-Entwicklungsumgebung übersetzt und auf diesen übertragen. | |||
* Auf dem Raspberry Pi wird nach einer der verfügbaren Anleitungen im DoorPi-Forum die DoorPi-Software installiert und konfiguriert | |||
* Auf dem FHEM-System (z.B. einem weiteren Raspberry Pi) muss eine aktuelle Version von FHEM laufen. | |||
==Nextion-Subsystem== | |||
Für das hier Projekt wurden mit dem Nextion-Editor zwei Seiten erstellt ('''page0''' und '''page1''' genannt), die verschiedene ''Widgets'' enthalten, also bedienbare Elemente. | |||
* '''page0''' ist die Default-Seite, sie zeigt die Namen der Hausbewohner. Die gesamte Fläche ist als ''picture'' mit der ID '''p0''' deklariert. Ein weiteres kleines ''picture'' mit der ID '''p1''' dient der Anzeige eine kleinen Schloss-Symbols | |||
* '''page1''' ist das virtuelle Keyboard. Wie man in der nachstehenden Abbildung sehen kann, wurden dabei 11 verschiedene ''buttons'' definiert, nämlich '''b0-b9''' für Ziffern und '''b10''' als ''Cancel''-Button. Außerdem gibt es es einen Fortschrittsbalken '''j0'''. | |||
[[File:nextion.png|400x240px]] | |||
Um die Eingabe von Ziffern über dieses Display zu verarbeiten, müssen die Button-Widgets mit Callback-Funktionen versehen werden, dies geschieht weiter unten in dem Programm des Arduino. | |||
Die fertigen Seiten werden durch den Nextion-Editor in einer kompakten Datei zusammengefasst, die entweder per serieller Schnittstelle an das Display übertragen wird, oder auf einer Micro-SD-Karte in das Nextion gesteckt wird. Beim Einschalten des Displays wird dann diese Datei automatisch in den internen Flash-Speicher des Nextion übernommen. | |||
==Arduino== | |||
Im Projekt wurde ein Arduino Micro verwendet - nahezu jede andere Version tut es auch. Das Programm darauf ist relativ einfach und wird im Folgenden erklärt. | |||
Zuerst gibt es ein paar Deklarationen. Insbesondere müssen die Bibliotheken für den 1-Wire Anschluss (=iButton-Reader) und das Nextion Display eingebunden werden. Achtung: in der Datei NexConfig.h muss der Wert für nexSerial auf Serial1 gesetzt werden. | |||
<pre> | |||
/*---------------------------------------------------------------------------------- | |||
Haustür | |||
Prof. Dr. Peter A. Henning, April 2016 | |||
------------------------------------------------------------------------------------*/ | |||
#include <OneWire.h> | |||
#include <SPI.h> | |||
#include <SD.h> | |||
#include <SoftwareSerial.h> | |||
// Make sure that in NexConfig.h nexSerial is configured properly ! | |||
#include "Nextion.h" | |||
</pre> | |||
Jetzt werden die Pins des Arduino ausgewählt. Außerdem wird in diesem Abschnitt die Sicherheits-PIN gesetzt, im Beispiel 12345 | |||
<pre> | |||
// Door Opener Subsystem | |||
const int DoorOpen = 8; // output for door opening | |||
const int LockState = 6; // output to indicate lock state | |||
byte softlock = 0; | |||
byte hardlock = 0; | |||
// Security PIN | |||
const int HardLock = 5; // input low = high security | |||
String PIN = "12345"; | |||
char pin[10]; | |||
char pindigit = ' '; | |||
byte pinctr = 0; | |||
long pinMillis = 0; | |||
const int WrongID = 7; // indicator for false 1-Wire ID or PIN | |||
// process variables | |||
const int loopLED = 13; // signal loop | |||
byte phase = 1; // phase of test | |||
long currentMillis = 0; | |||
// dimming | |||
const int Brightness = A0; // input pin for the dimming voltage | |||
const int Movement = A1; // input for movement detection | |||
const int DashDim = 3; // output for dimming further dashlights | |||
const int DashlightOn = 4; // input pin for the dashlight signal | |||
const long dimTimeout = 60000; | |||
byte isDimmed = 0; | |||
long dimMillis = 0; // timer | |||
</pre> | |||
Hier folgt der Code für das iButton-System. Natürlich nicht mit meinen echten 1-Wire IDs. Wichtig ist, dass diese hier hart in den Code eingebaut werden. Das ist zwar etwas unkomfortabel, wenn man sie ändern möchte - aber sehr sicher gegen Manipulationen. | |||
Die IDs sind auf dem jeweiligen iButton eingelasert und lesen sich wie folgt: | |||
[[datei:Dallas_key_and_sensor.jpg|200px|thumb|left|iButton]] | |||
<pre> | |||
F9 01 | |||
0000033ED394 | |||
</pre> | |||
Die Reihenfolge der Bytes ist hier gegenüber der eigentlichen 1-Wire-Adresse vertauscht, sie muss also von Rechts nach links gelesen werden: | |||
01.94D33E030000.F9, | |||
oder als hexadezimale Sequenz: | |||
0x01 0x94 0xD3 0x3E 0x03 0x00 0x00 0xF9 | |||
*01 ist die Family ID und identifiziert den Chip, hier ein DS2401 | |||
*Die nächsten 6 Byte sind die eindeutige Adresse | |||
*F9 ist ein CRC8-Code, der sich aus den anderen Bytes ergibt. | |||
Zur Überprüfung der Korrektheit eines iButton ist es also nur notwendig, die ersten 7 Byte der Adresse mit den Daten im Code zu vergleichen. Im Code wird also für diesen iButton, wenn er zu einer roten LED-Anzeige führen soll (Rot = 1,0,0 im RGB-Farbmodell, also LOW,HIGH,HIGH bei der Ansteuerung der drei LED): | |||
"iRed", {0x01, 0x94, 0xD3, 0x3E, 0x03, 0x00, 0x00}, LOW, HIGH, HIGH} | |||
eingetragen. Je nachdem, wie viele iButtons man einprogrammieren möchte, ist auch die Konstante iBNum anzupassen. | |||
<pre> | |||
// 1-Wire subsystem | |||
OneWire ds(12); // 1-Wire on pin 12 (a 4.7K resistor is necessary) | |||
const int redLED = 11; // LED on pins 9,10,11 | |||
const int greenLED = 10; | |||
const int blueLED = 9; | |||
typedef struct { | |||
char* name; | |||
byte ROM[8]; | |||
int red; | |||
int green; | |||
int blue; | |||
} iButton; | |||
const byte iBnum = 7; // Number of defined iButtons | |||
const iButton iButtons[] = { | |||
{"iRed", {0x01, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--}, LOW, HIGH, HIGH}, | |||
{"iRed*", {0x01, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--}, LOW, HIGH, HIGH}, | |||
{"iGreen", {0x01, 00x--, 0x--, 0x--, 0x--, 0x--, 0x--}, HIGH, LOW, HIGH}, | |||
{"iBlue", {0x01, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--}, HIGH, HIGH, LOW}, | |||
{"iOrange", {0x01, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--}, LOW, LOW, HIGH}, | |||
{"iPink", {0x01, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--}, LOW, HIGH, LOW}, | |||
{"iPurple", {0x01, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--}, LOW, HIGH, LOW} | |||
}; | |||
</pre> | |||
In dieser Version des Programms gibt es auf dem Nextion-Display nur zwei Seiten, eine mit den Namen und eine mit einem Keyboard. Die Keyboard-Buttons werden hier bekannt gemacht und mit Callbacks versehen. | |||
<pre> | |||
// GUI | |||
NexPage page0 = NexPage(0, 0, "page0"); | |||
NexPage page1 = NexPage(1, 0, "page1"); | |||
NexButton p0 = NexButton(0, 1, "p0"); | |||
NexPicture p1 = NexPicture(0, 2, "p1"); | |||
NexButton num0 = NexButton(1, 11, "b0"); | |||
NexButton num1 = NexButton(1, 2, "b1"); | |||
NexButton num2 = NexButton(1, 3, "b2"); | |||
NexButton num3 = NexButton(1, 4, "b3"); | |||
NexButton num4 = NexButton(1, 5, "b4"); | |||
NexButton num5 = NexButton(1, 6, "b5"); | |||
NexButton num6 = NexButton(1, 7, "b6"); | |||
NexButton num7 = NexButton(1, 8, "b7"); | |||
NexButton num8 = NexButton(1, 9, "b8"); | |||
NexButton num9 = NexButton(1, 10, "b9"); | |||
NexButton cancel = NexButton(1, 12, "b10"); | |||
NexProgressBar progress = NexProgressBar(1, 13, "j0"); | |||
NexTouch *nex_Listen_List[] = | |||
{ | |||
&num0, &num1, &num2, &num3, &num4, | |||
&num5, &num6, &num7, &num8, &num9, | |||
&cancel, &p0, NULL | |||
}; | |||
void p0Callback(void *ptr) | |||
{ | |||
dimLight(100); | |||
softlock = 1; | |||
if ( hardlock == 0) { | |||
showLock(); | |||
} | |||
} | |||
void num0PushCallback(void *ptr) | |||
{ | |||
pindigit = '0'; | |||
} | |||
void num1PushCallback(void *ptr) | |||
{ | |||
pindigit = '1'; | |||
} | |||
void num2PushCallback(void *ptr) | |||
{ | |||
pindigit = '2'; | |||
} | |||
void num3PushCallback(void *ptr) | |||
{ | |||
pindigit = '3'; | |||
} | |||
void num4PushCallback(void *ptr) | |||
{ | |||
pindigit = '4'; | |||
} | |||
void num5PushCallback(void *ptr) | |||
{ | |||
pindigit = '5'; | |||
} | |||
void num6PushCallback(void *ptr) | |||
{ | |||
pindigit = '6'; | |||
} | |||
void num7PushCallback(void *ptr) | |||
{ | |||
pindigit = '7'; | |||
} | |||
void num8PushCallback(void *ptr) | |||
{ | |||
pindigit = '8'; | |||
} | |||
void num9PushCallback(void *ptr) | |||
{ | |||
pindigit = '9'; | |||
} | |||
void CancelCallback(void *ptr) | |||
{ | |||
pinctr = 0; | |||
progress.setValue(0); | |||
} | |||
</pre> | |||
Es folgt die Initialisierungsroutine | |||
<pre> | |||
void setup() { | |||
String cmd; | |||
// set the digital pins as output: | |||
pinMode(redLED, OUTPUT); | |||
pinMode(greenLED, OUTPUT); | |||
pinMode(blueLED, OUTPUT); | |||
pinMode(loopLED, OUTPUT); | |||
pinMode(DoorOpen, OUTPUT); | |||
pinMode(WrongID, OUTPUT); | |||
pinMode(LockState, OUTPUT); | |||
pinMode(HardLock, INPUT_PULLUP); | |||
pinMode(DashlightOn, INPUT_PULLUP); | |||
digitalWrite(redLED, HIGH); | |||
digitalWrite(greenLED, HIGH); | |||
digitalWrite(blueLED, HIGH); | |||
digitalWrite(loopLED, LOW); | |||
digitalWrite(DoorOpen, HIGH); | |||
digitalWrite(WrongID, HIGH); | |||
digitalWrite(LockState, HIGH); | |||
// initialize the GUI | |||
nexInit(); | |||
p0.attachPush(p0Callback, &p0); | |||
num0.attachPush(num0PushCallback, &num0); | |||
num1.attachPush(num1PushCallback, &num1); | |||
num2.attachPush(num2PushCallback, &num2); | |||
num3.attachPush(num3PushCallback, &num3); | |||
num4.attachPush(num4PushCallback, &num4); | |||
num5.attachPush(num5PushCallback, &num5); | |||
num6.attachPush(num6PushCallback, &num6); | |||
num7.attachPush(num7PushCallback, &num7); | |||
num8.attachPush(num8PushCallback, &num8); | |||
num9.attachPush(num9PushCallback, &num9); | |||
cancel.attachPush(CancelCallback, &cancel); | |||
//dimming | |||
dimLight(100); | |||
//showlock | |||
if ( digitalRead(HardLock) == LOW) { | |||
showLock(); | |||
hardlock = 1; | |||
softlock = 0; | |||
} else { | |||
hideLock(); | |||
hardlock = 0; | |||
softlock = 0; | |||
} | |||
} | |||
</pre> | |||
Türöffnung -> Pin mit Name DoorOpen wird für eine Sekunde auf LOW gesetzt. | |||
<pre> | |||
void openDoor(int level) { | |||
digitalWrite(DoorOpen, LOW); | |||
delay(1000); | |||
digitalWrite(DoorOpen, HIGH); | |||
} | |||
</pre> | |||
Falsche ID -> Pin mit Name WrongID wird für eine Sekunde auf LOW gesetzt. | |||
<pre> | |||
void wrongID() { | |||
digitalWrite(WrongID, LOW); | |||
delay(1000); | |||
digitalWrite(WrongID, HIGH); | |||
} | |||
</pre> | |||
Mit den beiden nachfolgenden Kommandos wird ein Schloss-Symbol auf dem Nextion angezeigt bzw. ausgeblendet | |||
<pre> | |||
void showLock() { | |||
sendCommand("vis p1,1"); | |||
} | |||
void hideLock() { | |||
sendCommand("vis p1,0"); | |||
} | |||
</pre> | |||
Dimmen des Dashlight auf einen durch den Helligkeitssensor vorgegebenen Wert (Parameter level=0) oder auf einen als Parameter übergebenen Wert | |||
<pre> | |||
void dimLight(int level) { | |||
uint16_t dimVal; | |||
String cmd = "dim="; | |||
// zero level - determine from lighting conditions | |||
if ( (level == 0) && (digitalRead(DashlightOn) == HIGH) ) { | |||
dimVal = (uint32_t) analogRead(Brightness); | |||
dimVal = map(dimVal, 0, 1023, 0, 100); | |||
isDimmed = 1; | |||
// nonzero level - take as it is | |||
} else { | |||
dimVal = 100; | |||
isDimmed = 0; | |||
} | |||
cmd += dimVal; | |||
//dbSerialPrint(cmd); | |||
sendCommand(cmd.c_str()); | |||
dimVal = map(dimVal, 0, 100, 0, 255); | |||
//dbSerialPrint(" -- "); | |||
//dbSerialPrintln(dimVal); | |||
analogWrite(DashDim,dimVal); | |||
dimMillis = millis(); | |||
} | |||
</pre> | |||
Hier endlich die Schleife des Hauptprogramms | |||
<pre> | |||
void loop(void) { | |||
byte i; | |||
byte j; | |||
boolean equiv; | |||
byte iBfound; | |||
byte present = 0; | |||
byte addr[8]; | |||
char* device; | |||
// new for each loop | |||
currentMillis = millis(); | |||
digitalWrite(loopLED, HIGH); | |||
// dimming | |||
if ( isDimmed == 1 ) { | |||
if ( analogRead(Movement) < 10 ) { | |||
dimLight(100); | |||
} | |||
if ( digitalRead(DashlightOn) == LOW) { | |||
//dbSerialPrintln(" DashlightOn = LOW"); | |||
dimLight(100); | |||
} | |||
} | |||
if ( (currentMillis - dimMillis) > dimTimeout) { | |||
dimLight(0); | |||
} | |||
// locking | |||
if ( digitalRead(HardLock) == LOW) { | |||
//change display only if hardlock has changed | |||
if ( hardlock == 0 ) { | |||
showLock(); | |||
} | |||
hardlock = 1; | |||
softlock = 0; | |||
} else { | |||
//change display only if | |||
if ( (hardlock == 1) && (softlock == 0)) { | |||
hideLock(); | |||
} | |||
hardlock = 0; | |||
} | |||
//lockState display | |||
if ( (hardlock == 0) && (softlock == 0)) { | |||
digitalWrite(LockState, HIGH); | |||
} else { | |||
digitalWrite(LockState, LOW); | |||
} | |||
//1-Wire bus access only in phase 1 | |||
if ( phase == 1) { | |||
digitalWrite(redLED, HIGH); | |||
digitalWrite(greenLED, HIGH); | |||
digitalWrite(blueLED, HIGH); | |||
if ( !ds.search(addr)) { | |||
present = 0; | |||
ds.reset_search(); | |||
digitalWrite(loopLED, LOW); | |||
delay(250); | |||
} else { | |||
digitalWrite(loopLED, LOW); | |||
// Invalid 1-Wire ID | |||
if (OneWire::crc8(addr, 7) != addr[7]) { | |||
} else { | |||
dimLight(100); | |||
// the first ROM byte indicates which chip | |||
switch (addr[0]) { | |||
case 0x01: | |||
device = "DS2401"; | |||
present++; | |||
for (i = 0; i < iBnum; i++) { | |||
equiv = true; | |||
for (j = 0; j < 7; j++) { | |||
if (iButtons[i].ROM[j] != addr[j]) { | |||
equiv = false; | |||
break; | |||
} | |||
} | |||
if (equiv ) { | |||
iBfound = i; | |||
break; | |||
} | |||
} | |||
if ( equiv ) { | |||
digitalWrite(redLED, iButtons[iBfound].red); | |||
digitalWrite(greenLED, iButtons[iBfound].green); | |||
digitalWrite(blueLED, iButtons[iBfound].blue); | |||
// LowSec state => open door | |||
if ( (hardlock == 0) && (softlock == 0)) { | |||
openDoor(1); | |||
delay(4000); | |||
return; | |||
// HiSec state => go to phase 2 | |||
} else { | |||
phase = 2; | |||
pinMillis = millis(); | |||
page1.show(); | |||
return; | |||
} | |||
//sabotage ? Unknown iButton | |||
} else { | |||
digitalWrite(redLED, LOW); | |||
digitalWrite(greenLED, LOW); | |||
digitalWrite(blueLED, LOW); | |||
wrongID(); | |||
} | |||
break; | |||
default: | |||
device = "unknown"; | |||
break; | |||
} | |||
ds.reset(); | |||
} | |||
} | |||
} | |||
nexLoop(nex_Listen_List); | |||
if ( phase == 2 ) { | |||
// check for timeout | |||
if ( (currentMillis - pinMillis) > 30000 ) { | |||
progress.setValue(0); | |||
phase = 1; | |||
pinctr = 0; | |||
page0.show(); | |||
} | |||
} | |||
// phase 2 and GUI input is a number | |||
if ( (phase == 2) && ( pindigit != ' ') ) { | |||
// push this number to the PIN buffer | |||
pin[pinctr] = pindigit; | |||
pindigit = ' '; | |||
pinctr++; | |||
progress.setValue(pinctr * 20); | |||
// PIN is complete | |||
if ( pinctr == 5) { | |||
// PIN is correct => open door | |||
if ( String(pin) == PIN ) { | |||
openDoor(2); | |||
// wrong PIN | |||
} else { | |||
wrongID(); | |||
} | |||
// return to phase 1 | |||
progress.setValue(0); | |||
phase = 1; | |||
pinctr = 0; | |||
page0.show(); | |||
} | |||
} | |||
} | |||
</pre> | |||
==FHEM== | |||
Aufseiten von FHEM müssen die Perl-Module JSON und Test::JSON installiert werden. Ferner muss die Datei 70_DoorPi.pm aus dem Ordner contrib/doorpi in das Hauptverzeichnis von FHEM geschoben werden. Eine beispielhafte Konfiguration in FHEM lautet dann: | |||
<pre> | |||
define A.Door.Pi DoorPi 192.168.0.YY | |||
attr A.Door.Pi alarmDevice Sensor | |||
attr A.Door.Pi alarmSettings alarm4,alarm5,|A.Door.Pi:.*sabotage|Türstation|on | |||
attr A.Door.Pi doorlockcmd set A.Door.T locked | |||
attr A.Door.Pi doorunlockcmd set A.Door.T unlocked | |||
attr A.Door.Pi target0 telefonnummer1 | |||
attr A.Door.Pi target1 telefonnummer2 | |||
</pre> | |||
*Dabei ist natürlich die IP-Adresse des DoorPi-Rechners einzutragen. | |||
*Die Attribute alarmDevice/alarmSettings sind nur zu verwenden, wenn man das Modul 95_Alarm.pm benutzt. | |||
*Die Attribute doorlockcmd/doorunlockcmd sind die Kommandos, die von FHEM ausgeführt werden, wenn die Haustür wirklich abgeschlossen werden soll. | |||
===Notify zur Anzeige des gegenwärtigen Bildes=== | |||
Ruft man im FHEM-Frontend das Kommando '''set A.Door.Pi snapshot''' auf, oder wird der Klingelknopf betätigt, gibt es in FHEM ein Event '''A.Door.Pi snapshot: <url>'''. Dieses kann man mit einem Notify abfangen, beispielsweise wird mit | |||
<pre> | |||
define A.Door.Pi.img notify A.Door.Pi:snapshot.* { | |||
fhem("set GalaxyTab tickerMessage A.Door.Pi $EVTPART1");; | |||
fhem("set Archos7Tab tickerMessage A.Door.Pi $EVTPART1")} | |||
</pre> | |||
eine entsprechende Message an zwei Tablets gesendet. | |||
==DoorPi== | |||
Die DoorPi-Software wird laut diversen Anleitungen im DoorPi Forum auf dem Raspberry Pi installiert. Danach liegt sie im Pfad der Python-Installation - ziemlich ungünstig für weitere Arbeiten. Deshalb wird zunächst ein Verzeichnis /home/doorpi angelegt. In dieser liegen dann verschiedene Hilfsdateien und Hilfsverzeichnisse. | |||
===csrfToken=== | |||
In der aktuellen Version von FHEM wird ein so genanntes csrf-Token eingesetzt, um die Sicherheit gegen die so genannte "Cross Site Request Forgery" zu gewährleisten. Das ist ein zufällig festgelegter String, der für das Ausführung von webbasierten Kommandos dem Aufruf der entsprechenden FHEM-Seite angefügt werden muss. Eine genaue Anleitung dazu gibt es im [[CsrfToken-HowTo]]. Für dieses DoorPi-Projekt ist es zwar möglich, dieses csrf-Token jedesmal neu zu holen, auf der Seite von DoorPi zwischenzuspeichern und dann beim Kommando-Aufruf für FHEM zu nutzen. Sinnvoller ist es, für DoorPi-Aufrufe von FHEM einen speziellen Zugang in FHEM anzulegen, so wie dies im Abschnitt [[CsrfToken-HowTo#API_Web|API_Web]] beschrieben wurde, und nur für diesen Zugang dann das csrf-Token abzuschalten. | |||
===FHEMHelper.sh=== | |||
Diese Datei ist eine Skriptdatei, mit der diverse Hilfsfunktionen ausgeführt werden. Zwar lassen sich diese in einigen Fällen auch als Einzeiler in der doorpi.ini verstecken, als separate Datei im Verzeichnis /home/doorpi ist diese Helferdatei jedoch leichter änderbar. | |||
<pre> | |||
# /bin/sh | |||
FHEMDP="A.Door.Pi" | |||
FHEMIP="192.168.0.XX" | |||
FHEM="http://192.168.0.XX:8083/fhem?XHR=1&cmd.$FHEMDP" | |||
HOME="/home/doorpi" | |||
default_target="yyyyyy" | |||
case $1 in | |||
init) target=`cat $HOME/calltarget` | |||
curl "$FHEM=setreading%20$FHEMDP%20call_target%20$target" & | |||
streampid=`pidof mjpg_streamer` | |||
if [ -z "$streampid" ]; then | |||
curl "$FHEM=setreading%20$FHEMDP%20stream%20off" & | |||
else | |||
curl "$FHEM=setreading%20$FHEMDP%20stream%20on" & | |||
fi | |||
;; | |||
doorunlockandopen) | |||
curl "$FHEM=set%20GalaxyTab%20ttsSay%20Ein%20Bewohner%20betritt%20das%20Haus" & | |||
curl "$FHEM=set%20$FHEMDP%20door%20unlockandopen" & | |||
;; | |||
wrongid) | |||
curl "$FHEM=set%20GalaxyTab%20ttsSay%20Unerlaubter%20Zutrittsversuch" & | |||
curl "$FHEM=set%20$FHEMDP%20door%20wrong_id" & | |||
;; | |||
softlock) | |||
curl "$FHEM=set%20$FHEMDP%20door%20softlock" & | |||
;; | |||
call) | |||
curl "$FHEM=set%20$FHEMDP%20call%20$2" & | |||
;; | |||
gettarget) | |||
echo "{ReadingsVal('$FHEMDP','call_target','$default_target')}" | socat -t50 - TCP:$FHEMIP:7072 > $HOME/calltarget | |||
;; | |||
purge) | |||
find $HOME/records/ -type f -ctime 1 -delete | |||
;; | |||
movement) | |||
curl "$FHEM=set%20$FHEMDP%20door%20movement" & | |||
;; | |||
sabotage) | |||
curl "$FHEM=set%20$FHEMDP%20door%20sabotage" & | |||
;; | |||
esac | |||
</pre> | |||
===doorpi.ini=== | |||
Die Konfigurationsdatei '''doorpi.ini''' liegt in der Standardinstallation von DoorPi im Verzeichnis /usr/local/etc/DoorPi/conf, sie wird deshalb per Softlink mit /home/doorpi.ini verbunden. In dieser Datei stehen diverse Konfigurationsangaben für DoorPi. | |||
<pre> | |||
[DoorPi] | |||
base_path = /usr/local/etc/DoorPi | |||
snapshot_path = /home/doorpi/records | |||
number_of_snapshots = 10 | |||
eventlog = /home/doorpi/log/eventlog.db | |||
is_alive_led = blinking_led | |||
last_snapshot = | |||
[DoorPiWeb] | |||
indexfile = index.html | |||
loginfile = login.html | |||
online_fallback = http://motom001.github.io/DoorPiWeb | |||
port = 80 | |||
public = AREA_public | |||
www = /home/doorpi/records | |||
[AREA_public] | |||
.* | |||
[AREA_config] | |||
/control/config_value_get | |||
/control/config_value_set | |||
/control/config_value_delete | |||
/control/config_save | |||
/control/config_get_configfile | |||
[AREA_dashboard] | |||
/dashboard/pages/.*html | |||
[AREA_status] | |||
/status | |||
/mirror | |||
[AREA_control] | |||
.* | |||
[User] | |||
admin = admin | |||
visitor = visitor | |||
[Group] | |||
administrators = admin | |||
guests = visitor | |||
[WritePermission] | |||
administrators = dashboard,status,config | |||
[ReadPermission] | |||
guests = dashboard | |||
[AdminNumbers] | |||
**621 = active | |||
[DTMF] | |||
"#" = out:door,1,0,3 | |||
####################### SIP phone ####################### | |||
[SIP-Phone] | |||
identity = DoorPi | |||
local_port = 5060 | |||
firewallpolicy = PolicyNoFirewall | |||
# | |||
sipphonetyp = linphone | |||
sipserver_password = xxxxxxxxxxxxx | |||
sipserver_realm = fritz.box | |||
sipserver_server = yyyyyyyyyyyy | |||
sipserver_username = 620 | |||
stun_server = | |||
# | |||
max_call_time = 300 | |||
call_timeout = 60 | |||
ua.max_calls = 2 | |||
# | |||
capture_device = ALSA: USB PnP Sound Device | |||
playback_device = ALSA: USB PnP Sound Device | |||
audio_codecs = PCMA,PCMU | |||
record_while_dialing = False | |||
records = /home/doorpi/records/%Y-%m-%d_%H-%M-%S.wav | |||
# | |||
dialtone = /home/doorpi/sounds/ShortDialTone.wav | |||
dialtone_renew_every_start = False | |||
dialtone_volume = 35 | |||
echo_cancellation_enabled = False | |||
# | |||
video_codecs = VP8 | |||
video_device = StaticImage: Static picture | |||
video_display_enabled = False | |||
video_size = vga | |||
</pre> | |||
Mit den nachfolgenden Events sendet DoorPi Nachrichten an FHEM: | |||
<pre style="color: blue"> | |||
####################### Events ####################### | |||
[EVENT_OnStartup] | |||
10 = sleep:1 | |||
20 = os_execute:/home/doorpi/FHEMHelper.sh call init | |||
[EVENT_BeforeSipPhoneMakeCall] | |||
10 = out:irlight,1 | |||
20 = os_execute:/home/doorpi/FHEMHelper.sh call startup | |||
30 = take_snapshot | |||
40 = out:irlight,0 | |||
[EVENT_OnCallStateDisconnect] | |||
10 = os_execute:/home/doorpi/FHEMHelper.sh call end | |||
[EVENT_OnCallStateDismissed] | |||
10 = os_execute:/home/doorpi/FHEMHelper.sh call dismissed | |||
[EVENT_OnCallStateReject] | |||
10 = os_execute:/home/doorpi/FHEMHelper.sh call rejected | |||
[EVENT_OnTimeMinuteEvery5] | |||
10=statuswatchdog:/tmp/doorpi.watchdog | |||
</pre> | |||
Hier wird ein virtuelles Keyboard (=I/O-System) mit dem Namen '''webservice'' sowie die reale I/O-Hardware, in diesem Falle das PiFace2-Board definiert. | |||
<pre> | |||
####################### Keyboards ############################## | |||
[keyboards] | |||
webservice = filesystem | |||
onboardpins = piface | |||
</pre> | |||
Wichtig sind die virtuellen Buttons, die von FHEM per URL-Aufruf aktiviert werden können. | |||
*Dabei lösen diese Aufrufe entweder eine direkte Reaktion des PiFace-Moduls aus, z.B. wird mit dem virtuellen Button '''doorlocked''' (Aufruf '<URL des DoorPi>/control/trigger_event?event_name=OnKeyPressed_webservice.doorlocked&event_source=doorpi.keyboard.from_filesystem') der reale Output '''hardlock''' eingeschaltet (Zustand 1 => Ausgang Low). | |||
*Oder sie rufen dass DoorPi-seitige Hilfsprogramm FHEMHelper.sh auf. | |||
<pre style="color: red"> | |||
####################### Virtual keyboard ####################### | |||
[webservice_keyboard] | |||
base_path_input = /home/doorpi/keyboard/inputs/ | |||
base_path_output = /home/doorpi/keyboard/outputs/ | |||
reset_input=false | |||
[webservice_InputPins] | |||
dooropen = out:door,1,0,3 | |||
doorlocked = out:hardlock,1 | |||
doorunlocked = out:hardlock,0 | |||
snapshot = sleep:0 | |||
streamon = sleep:0 | |||
streamoff = sleep:0 | |||
lighton = out:light,1 | |||
#lightonfortimer = out:light,1,0,60 | |||
lightoff = out:light,0 | |||
dashlighton = out:dashlight,1 | |||
dashlightoff = out:dashlight,0 | |||
gettarget = sleep:0 | |||
purge = sleep:0 | |||
clear = sleep:0 | |||
button1 = sleep:0 | |||
button2 = sleep:0 | |||
#-- communicate to FHEM that a snapshot has been taken | |||
[EVENT_OnKeyPressed_webservice.snapshot] | |||
10 = out:irlight,1 | |||
20 = os_execute:/home/doorpi/FHEMHelper.sh call snapshot | |||
30 = take_snapshot | |||
40 = out:irlight,0 | |||
#-- start video stream | |||
[EVENT_OnKeyPressed_webservice.streamon] | |||
10 = os_execute:/etc/init.d/mjpg_streamer start | |||
#-- stop video stream | |||
[EVENT_OnKeyPressed_webservice.streamoff] | |||
10 = os_execute:/etc/init.d/mjpg_streamer stop | |||
#-- obtain the target call number from FHEM | |||
[EVENT_OnKeyPressed_webservice.gettarget] | |||
10 = os_execute:/home/doorpi/FHEMHelper.sh gettarget | |||
#-- purge all files older than one day | |||
[EVENT_OnKeyPressed_webservice.purge] | |||
10 = os_execute:/home/doorpi/FHEMHelper.sh purge | |||
[EVENT_OnKeyPressed_webservice.button1] | |||
10 = os_execute:/home/doorpi/FHEMHelper.sh sabotage | |||
[EVENT_OnKeyPressed_webservice.button2] | |||
10 = file_call_value:/home/doorpi/calltarget | |||
</pre> | |||
Es folgt die Definition der realen Buttons: | |||
<pre> | |||
####################### Real keyboard ####################### | |||
[onboardpins_keyboard] | |||
pull_up_down = PUD_UP | |||
[onboardpins_OutputPins] | |||
0 = door | |||
1 = light | |||
2 = dashlight | |||
3 = irlight | |||
4 = hardlock | |||
7 = blinking_led | |||
[onboardpins_InputPins] | |||
0 = file_call_value:/home/doorpi/calltarget | |||
1 = sleep:0 | |||
4 = sleep:0 | |||
5 = sleep:0 | |||
6 = sleep:0 | |||
7 = sleep:0 | |||
#-- DoorOpen pin from Arduino | |||
[EVENT_OnKeyPressed_onboardpins.1] | |||
10 = os_execute:/home/doorpi/FHEMHelper.sh doorunlockandopen | |||
20 = os_execute:aplay -D plughw:1,0 /home/doorpi/sounds/067_willkommen.wav | |||
#-- WrongID pin from Arduino | |||
[EVENT_OnKeyPressed_onboardpins.4] | |||
10 = out:irlight,1 | |||
20 = os_execute:/home/doorpi/FHEMHelper.sh wrongid | |||
30 = take_snapshot | |||
40 = out:irlight,0 | |||
#-- LockState pin from Arduino - FHEM will transform softlock into hardlock | |||
[EVENT_OnKeyPressed_onboardpins.5] | |||
10 = os_execute:/home/doorpi/FHEMHelper.sh softlock | |||
#-- Movement detection | |||
[EVENT_OnKeyPressed_onboardpins.6] | |||
10 = out:dashlight,1,0,3 | |||
20 = os_execute:/home/doorpi/FHEMHelper.sh movement | |||
#-- Sabotage detection | |||
[EVENT_OnKeyPressed_onboardpins.7] | |||
10 = os_execute:/home/doorpi/FHEMHelper.sh sabotage | |||
</pre> | |||
==mjpg_streamer== | |||
Diese Software dient dazu, einen kleinen Webserver mit MJPEG-Datenstrom aus der Raspberry Pi-Kamera zur Verfügung zu stellen. Sie wird mit Hilfes des Skriptes /etc/init.d/mjpeg_streamer gestartet und gestoppt | |||
==sonstige Software== | |||
Auf dem Raspberry Pi wird noch benötigt | |||
*curl - zum Aufruf einer URL | |||
*socat - zum Aufruf einer URL und Verarbeitung der Rückgabe (könnte man auch durch curl ersetzen) | |||
[[Kategorie:1-Wire]] | |||
[[Kategorie:Code_Snippets]] |
Aktuelle Version vom 11. November 2019, 18:06 Uhr
DoorPi | |
---|---|
Zweck / Funktion | |
Das Modul verknüpft eine IP-Türsprechstelle aus dem DoorPi-Projekt mit FHEM. | |
Allgemein | |
Typ | Hilfsmodul |
Details | |
Dokumentation | EN / DE |
Support (Forum) | Automatisierung |
Modulname | 70_DoorPi.pm |
Ersteller | Prof. Dr. Peter A. Henning |
Wichtig: sofern vorhanden, gilt im Zweifel immer die (englische) Beschreibung in der commandref! |
Auf dieser Seite wird beschrieben, wie man mit Hilfe der DoorPi-Software auf Basis eines Raspberry Pi eine IP-Türsprechstelle baut und in FHEM integriert.
Weitere Bestandteile des Gesamtsystems sind ein Garagentoröffner und eine Hoftür mit selbstverriegelndem Panikschloss. Zur Beschreibung der beiden letztgenannten Teilprojekte verweise ich auf das Buch Smart Home Hacks.
Eine kompakte Platine, welche die im Folgenden beschriebene Schaltung enthält und einen wesentlich einfacheren Aufbau ermöglicht, wird auf der Seite DoorPiBoard beschrieben.
Achtung: Da die DoorPi-Software nicht weiter entwickelt wird, ist derzeit eine Umstellung auf FHEM im Gange, die auf derselben Hardware laufen soll
Funktion
Die Software DoorPi läuft auf einem Raspberry Pi und stellt einen IP-Phone Client zur Verfügung, der in eine IP-Haustelefonanlage (z.B. mit der FritzBox) eingebunden werden kann. Diesen Raspberry Pi könnte man zwar über längere Audiokabel mit der notwendigen Lautsprecher-Mikrofon-Kombination verbinden, das ist allerdings etwas anfällig für Störsignale. Die meisten Anwender von DoorPi wählen deshalb den Einbau des Raspberry Pi direkt an der Türsprechstelle. Aus Sicht des Autors stellt das eine Sicherheitslücke dar - weil ein direkter Zugang ins Heimnetz außerhalb der eigenen vier Wände geschaffen würde.
Hier soll deshalb eine abgesetzte Installation beschrieben werden: Außen an der Türsprechstelle sitzen nur Sensoren, Audiohardware und ein Display, angesteuert von einem "dummen" Arduino Mikrocontroller. Ins Innere des Hauses führen drei Kabel:
- Ein USB-Kabel zur digitalen Ankopplung der Audio-Hardware an den Raspberry Pi
- Ein HDMI-Kabel zur digitalen Ankopplung einer Kamera an den Raspberry Pi
- Ein 8-adriges Kabel zur Weiterleitung verschiedener Signale an den Raspberry Pi. Hierfür kann man aus Bequemlichkeitsgründen ein Netzwerk-Patchkabel verwenden - hochfrequente Signale gehen allerdings nicht über diesen Weg.
Klingel und Türsprechstelle
Beim Drücken auf den Klingelknopf wird ein IP-Telefonanruf gestartet - entweder bei einer internen Nummer, oder (per Mausklick auswählbar in FHEM) bei einer beliebigen anderen Nummer. Wenn der Empfänger den Ruf annimmt, kann er mit dem Besucher sprechen und ggf. durch das Drücken einer Taste die Tür öffnen. Es ist in der DoorPi-Installation problemlos konfigurierbar, beliebige andere Aktionen zu starten - z.B. könnte man dem Paketlieferanten nur das Gartentor öffnen.
Das Gespräch wird als WAV-Datei ebenfalls aufgezeichnet, diese ist ebenso wie die Bilddatei aus dem FHEM-Frontend abrufbar.
Türöffnung
Das System verfügt über einen iButton-Reader. Legt man einen iButton auf, der in der Arduino-Software codiert ist, leuchtet eine im Reader integrierte Tricolor-LED in einer entsprechenden Farbe auf (z.B. roter iButton-Halter => rote LED). Der weitere Ablauf hängt vom Schließzustand der Haustür ab.
- Ist die Haustür nur zugezogen und nicht abgeschlossen (Zustand hardlock=off), wird sie geöffnet. Gleichzeitig wird über die Türsprechstelle die Stimmnachricht ausgegeben "Willkommen zu Hause !", und auf einem im Haus befindlichen Tablet angesagt "Ein Bewohner betritt das Haus."
- Ist die Haustür abgeschlossen (Zustand hardlock=on), wird zunächst auf dem Touchscreen des Systems statt der Namen eine virtuelle Tastatur angezeigt. Hier kann ggf. noch die akustische Bitte eingebaut werden, eine fünfstellige PIN einzugeben. Der Fortschritt bei der Eingabe wird durch einen Balken angezeigt.
- Ist die PIN falsch, geht das System wieder in den Ausgangszustand zurück, es wird eine Warnungsmeldung an das interne FHEM-System übermittelt.
- Ist die PIN korrekt, wird zunächst der Entriegelungsvorgang der Tür eingeleitet. Je nach verwendetem System kann das eine Weile dauern - Keymatic ist hierfür ein Beispiel, das können durchaus 3-4 Sekunden werden. Danach wird die Tür geöffnet, gleichzeitig wird über die Türsprechstelle die Stimmnachricht ausgegeben "Willkommen zu Hause !", und auf einem im Haus befindlichen Tablet angesagt "Ein Bewohner betritt das Haus." Je nach Konfiguration kann FHEM an dieser Stelle auch weitere Aktionen auslösen - etwa weitere Entriegelungsvorgänge starten.
Verwendung des Hausschlüssels
- Verriegelt man die Haustür mit einem gewöhnlichen mechanischen Schlüssel, teilt dies die Keymatic dem FHEM-System mit (Zustand hardlock=on), das FHEM-System informiert den DoorPi-Rechner.
- Entriegelt man die Haustür mit einem gewöhnlichen mechanischen Schlüssel, teilt dies die Keymatic dem FHEM-System mit (Zustand hardlock=off), das FHEM-System informiert den DoorPi-Rechner.
Fernbedienung
Alle Funktionen, z.B. die Ent- und Verriegelung und das Türöffnen sind selbstverständlich auch über FHEM (und damit über ein Funksystem, im konkreten Fall über HomeMatic) steuerbar.
Videostream
Auf dem Raspberry Pi ist auch die Software mjpeg_streamer installiert, Per Mausklick in der FHEM-Oberfläche wird mjpeg-streamer gestartet und gestoppt (das wird von der DoorPi-Software erledigt, die einen bestimmten virtuellen Tastendruck erkennt und dann lokal ein Skript startet), und stellt dann an TCP-Port 9000 einen Videostream bereit. Wer möchte, kann das System auch so konfigurieren, dass es diesen Videostream zusammen mit dem Audiostream zur Videotelefonie verwendet.
Bewegungserkennung und Helligkeitsmessung
Erkennt das System durch den eingebauten PIR-Bewegungsmelder eine Bewegung, wird
- die Anzeigehelligkeit für mindestens eine Minute hochgefahren
- ein Signal an FHEM gesendet.
Wenn eine Minute keine Bewegung erkannt wurde, geht die Anzeigehelligkeit (d.h. vom Display und dem LED-Ring um den Klingelknopf) wieder auf einen Wert zurück, der durch die externe Helligkeit bestimmt ist.
Der Bewegungsmelder ist so geschaltet, dass er innerhalb von ca. 1 Minute (tatsächlich sind es mit der unten geposteten Schaltung 47 Sekunden), gerechnet von der letzten erkannten Bewegung, keine neuen Bewegungsmeldungen weiterleitet - sondern die Zeitdauer einfach verlängert (retriggerbares Monoflop).
Hardware
Arbeitsaufwand und Kosten
In der Zeit von Juni – September 2016 habe ich ca. 150 Arbeitsstunden für Konzeption, Prototyp und Programmierung sowie die Endmontage aufgewandt. Auf Grund der Tatsache, dass sowohl meine 3D-Druckdateien, als auch die Programmbestandteile hier frei verfügbar sind, kann man das sicher in weniger als der halben Zeit nachbauen.
Die reinen Materialkosten betragen etwa 450 €, davon ca. 100 € für einen Raspberry Pi 3 mit Zubehör und Zusatzplatine PiFace 2, ca. 70 € für das Infrarot-Kamerasystem, ca. 40 € für das reine Audio-System, ca. 50 € für Bewegungsmelder, Helligkeitssensor und beleuchteten Klingelknopf. Der zusätzliche Controller vom Typ Arduino Micro und der angeschlossene iButton-Leser schlagen mit insgesamt 20 € zu Buche, das Nextion-Touchscreen mit ca. 30 €. Teuerstes Einzelstück war eine nach Maß gefräste und gebohrte Frontplatte mit ca. 100 €, entsprechende Wandeinbaugehäuse gibt es ab ca. 20 €. Da das Gesamtsystem modular aufgebaut ist, und Komponenten auch in ganz unterschiedlichen Qualitäten verwendet worden sind, kann man eine abgespeckte Version auch schon für ca. 200 € realisieren.
Raspberry Pi
Verwendet wird
- ein Raspberry Pi 3, der ein integriertes WLAN-Modul besitzt. Falls man seiner eigenen WLAN-Bandbreite nicht traut: Für ca. 25 € bekommt man einen Satz Powerline-Adapter, mit denen man eine sehr stabile Verbindung zwischen dem Raspberry Pi (Innenwand neben der Tür) und dem Rest des Hauses aufbauen kann.
- eine Zusatzkarte PiFace 2, mit je 8 digitalen Ein- und Ausgängen.
- ein klares Gehäuse, das die Erweiterungskarte mit aufnehmen kann.
Das Gehäuse mit Raspberry Pi, ein Netzschalter, ein 5V Netzteil und ein Powerline-Adapter wurden in ein kompaktes Hutschienengehäude montiert, das durch einen Mauerdurchbruch (ca. 3cm Durchmesser wegen der HDMI-Stecker, siehe dort) mit der außenliegenden Türsprechstelle verbunden ist.
Kamera-Subsystem
Verwendet werden hierfür
- eine Standard-NoIR-Kamera für den Raspberry Pi (ohne Infrarotfilter, somit IR-empfindlich, z.B. erhältlich hier)
- ein Fisheye 180° zum Aufclippen auf Smartphones. Die untenstehenden Stereo-Litografie-Dateien sind für das Objektiv von Bresser entworfen worden, auch erhältlich hier.
- eine Acrylglaskuppel 2 Zoll (Achtung, der Durchmesser beträgt 49,5 mm).
- eine Kabelverlängerung Flatribbon <-> HDMI <-> Flatribbon.
- Tipp: HDMI-Stecker sind ziemlich dick und passen eher schlecht durch enge Kabelkanäle. Hat man dieses Problem, kann man noch an einer Seite einen Adapter HDMI <-> Mini-HDMI einsetzen und ein entsprechendes Kabel mit deutlich dünnerem Mini-HDMI-Stecker vewenden.
- ein Kameradom aus dem 3D-Drucker mit genauer Passung für das Fisheye vorne und die Kamera hinten, sowie Passungen für 8 LED. Die STL-Datei für diesen Kameradom findet man hier: PiCameraDome4
- eine Rückseite des Kameradoms aus dem 3D-Drucker, wird hinten mittig aufgeklebt und hält den Deckel des Kamerasystem durch drei Nippel. Die STL-Datei dafür findet man hier PiCameraDome4addendum
- ein Deckel des Kameradoms aus dem 3D-Drucker, wird auf die Rückseite aufgesteckt und beinhaltet zwei Halterungen für Mikrofone (die "Ohren" im Bild). Die STL-Datei dafür findet man hier PiCameraDome4back
- 9 x IR-LED TSAL 6200 mit hoher Leistung
- 3 Widerstände 10 Ohm 0,2 W.
Zur Montage werden vier M2-Gewindebolzen in die hintere Aussparung des Kameradoms eingeklebt, und auf diese mit entsprechenden Abstandshaltern und Muttern sowohl die Kamera, als auch die eine Hälfte der Kabelverlängerung geschraubt. Dabei empfiehlt es sich, die LED vorher an Ort und Stelle zu haben - ggf. muss bei ungünstiger Klebung der Gewindebolzen bei den Eck-LED ein Gehäusevorsprung etwas abgefeilt werden.
Die Schaltung der LED für dem Betrieb an einer Spannung von 5V ausgelegt - damit fließen durch drei in Reihe geschaltete LED und einen 10 Ohm-Widerstand ziemlich genau 100mA. Bei der Schaltung über einen MOSFET ist das etwas weniger, wegen des Drain-Source-Widerstandes des MOSFET, reicht aber für die notwendige Helligkeit aus. Aus Symmetriegründen werden drei solche Stränge (insgesamt 9 LED) montiert - aber nur 8 davon sind bei dem angegebenen Kameradom nach außen gerichtet (wer möchte, kann gerne ein 3D-Design mit 9 Löchern erstellen), eine LED zeigt also nutzlos nach innen (erkennbar im 3. Bild der obigen Reihe).
Das Fischaugenobjektiv wird mit wenig (!) klarem Silikon in die vordere Öffnung des Kameradoms geklebt, der rückwärtige Aufsatz mit den 3 Nippeln mit Zweikomponentenkleber auf der Rückseite des Kameradoms befestigt. Dabei zur Ausrichtung den Mikrofonhalter (rechtes Bild, mit den "Ohren") probeweise aufstecken.
Die Acrylglaskuppel wird mit Silikon oder einem äquivalenten Dichtmaterial wasserdicht in die Frontplatte eingesetzt, auf diese dann von hinten der Kameradom. Eine staubdichte Verbindung erfordert auch hier die Abdichtung mit Silikon. Für den Fall einer möglichen Demontage empfiehlt es sich, diese Abdichtung nicht als Klebung zu verwenden, sondern als umlaufenden Rand nachträglich aufzubringen - der ist dann einfach mit einem Cutter zu entfernen.
Audio-Subsystem
- Als Sound"karte" wird mit dem BIGtec 7.1 USB Adapter ein preiswertes Produkt verwendet, dessen Gehäuse sich problemlos entfernen lässt. Weitere Modelle werden im oben zitierten DoorPi-Forum empfohlen.
- Der Audioverstärker ist ein Modell von Foxnovo mit einer Ausgangsleistung von 2x3 W. Auch hierfür können nahezu beliebige andere Modelle verwendet werden
- Als Lautsprecher kommen zwei VISATON Kleinlautsprecher VIS K28.40-8 mit den Maßen 2,8 x 4 cm und einer Impedanz von 8 Ohm zum Einsatz. Zwischen Lautsprecher und Frontplatte ist ein Lautsprechervlies angebracht - damit ergibt sich ein effektiver Spritzwasserschutz, allerdings keine vollständige Abdichtung gegen Feuchtigkeit. Betriebserfahrungen liegen noch nicht vor !
- Als Mikrofon können nahezu beliebige preiswerte Mikrofonkapseln für Sprachqualität verwendet werden. Bei diesen handelt es in der Regel um Kristallmikrofone, die einen ausreichenden Spannungspegel für den Audioverstärker erzeugen. Wichtig ist die gute akustische Entkopplung von Lautsprechern und Mikrofonen - darum werden die Lautsprecher fest auf die Frontplatte geschraubt, die Mikrofone aber in den Halter am Kameradom von hinten eingeschoben. Eine Fixierung der Mikrofone mit dem bereits vorher verwendeten Silikon ist empfehlenswert, ebenso eine Vliesschicht zwischen Mikrofon und Frontplatte.
Steckkontakte aller Art sind bei Installationen im Außenbereich immer ein Risiko. Für das gegenwärtige Projekt wurden deshalb das Gehäuse der Sound"karte" und die enthaltenen 3,5 mm Klinkenbuchsen entfernt. Zwei Mikrofonkabel wurden direkt angelötet, die Ausgänge per Draht mit der Verstärkerplatine verbunden, deren Ausgänge wiederum mit Schraubklemmen versehen. Etwas Zweikomponentenkleber macht daraus eine kompakte Einheit, die oberhalb des Displays auf einer Lochrasterplatte befestigt und per USB-Kabel mit dem Raspberry Pi verbunden wird.
Der verwendete Verstärker hat einen Schalteingang (weißes Kabel im Bild) - wird dieser auf Low=GND gesetzt, ist der Verstärker ausgeschaltet und verbraucht keinen Strom. Dieser Schalteingang wird auf den Ausgang DLA der Arduino-Platine geführt.
Sensoren
- Der PIR-Bewegungsmelder ist ein sehr kleines Modul, das einfach von hinten in eine Bohrung der Frontplatte gesteckt wird. Sein Open-Collector-Ausgang muss noch mit einem retriggerbaren Monoflop = Timer versehen werden. Damit ist sichergestellt, dass erneute Triggervorgänge nicht neue Events in Doorpi bzw. FHEM auslösen. Hierfür gibt es zwei Möglichkeiten:
- Bausatz PIR13TM, seit kurzem erhältlich bei ELV. Kleine Zusatzplatine, die auf das Bewegungsmeldermodul aufgesteckt wird.
- Eigenbau auf Basis des LM555, siehe Schaltplan unten. Innerhalb einer Zeit von ca. 50 Sekunden (bestimmt durch R8 und C2) wird kein neuer Impuls ausgelöst. Der Ausgang des LM555 wird durch einen MOSFET invertiert und dann über eine der unbenutzten Adern (blau im Bild) des HDMI-Kabels an den Raspberry Pi weitergeleitet. Achtung: Dieses Monoflop sitzt auf einer kleinen Zusatzplatine, die im rechten unteren Bild noch nicht aufgesteckt ist.
- Ein Fototransistor wird mit zwei festen und einem einstellbaren Widerstand als Helligkeitssensor verwendet. Seine Ausgangsspannung wird an den Arduino weitergeleitet, der auf Grund dieses Wertes das Display und den LED-Ring des Klingelknopfes dimmt.
- Als Klingelknopf wird ein Modell aus Edelstahl mit umlaufendem LED-Ring verwendet.
- Ein Mikroschalter dient als Sabotagekontakt, der so in die Rückwandinstallation geklebt wird, dass er bei der Abnahme der Frontplatte geschlossen wird. Dieses Signal wird über eine der unbenutzten Adern des HDMI-Kabels (orange im Bild) an den Raspberry Pi weitergeleitet.
Arduino
Verwendet wird ein Arduino Micro mit dem Programm (Sketch in der Arduino-Terminologie), das weiter unten veröffentlicht ist. Der Arduino sitzt in einer Fassung auf einer Lochrasterplatte, die hinter dem Nextion-Display befestigt ist.
Hauptaufgabe des Arduino ist, in dem iButton-Reader einen Abfragezyklus von 250 ms durchzuführen. Solche hohen Abfrageraten können in der Regel mit Busmastern, die von einer universellen 1-Wire Software wie OWX oder OWFS angesteuert werden, nicht realisiert werden. Im vorliegenden Projekt übernimmt der Arduino auch die Steuerung des Nextion-Displays über eine seiner seriellen Schnittstellen.
Nextion-Subsystem
Als interaktives Namensschild wird ein Nextion Display 3,2" mit einer Auflösung von 400x240 Pixel verwendet (erhältlich in China oder in Deutschland).
Die Besonderheit dieses Displays ist der eingebaute Prozessor. Mit Hilfe der zugehörigen Software (erhältlich für Windows auf den Seiten des Herstellers) kann man diesem Prozessor ein regelrechtes GUI (Graphical User Interface) einprogrammieren: Bilder, Texte, Buttons, Fortschrittsbalken, aktive Regionen werden zusammengestellt und können mit Hilfe der entsprechenden Softwarebibliotheken abgerufen werden, wenn sie erst einmal im internen Flash-Memory gespeichert sind. Für diese Zusammenstellung eines Ablaufes stellt der Nextion-Editor auch einen komfortablen Emulator zur Verfügung, mit dem man den Ablauf vor dem Upload ausprobieren kann.
Der Upload auf das Nextion-Display geschieht entweder über die eingebaute serielle Schnittstelle (via USB-Seriell-Adapter für ca. 5 € auch an USB anzuschließen), oder indem mit Hilfe des Nextion Editors eine Micro-SD-Karte beschrieben und in den Kartenslot des Displays gesteckt wird (unten im Bild). Die Ansteuerung des Displays geschieht ebenfalls über die diese serielle Schnittstelle (rechts im Bild).
Zum Schutz des Nextion empfiehlt sich seine Montage an der Frontplatte mit einer entsprechenden Schutzfolie. Diese Folie kann man Rand etwas überstehen lassen und dort mit Silikon gegen die Frontplatte abdichten.
iButton-Subsystem
iButtons sind kompakte Knöpfe aus Stahl, in denen ein Chip mit einer eindeutigen ID sitzt. Durch Auflegen des iButtons auf das Lesegerät wird diese ausgelesen und mit den einprogrammierten IDs verglichen.
- iButtonLesegerät, z.B.:
- ohne LED
- mit LED - Achtung evtl. nicht ausreichend Wetterfest
- aus Fernost
Mehr über die Technik der iButtons erfährt man in der Katgeorie 1-Wire. Achtung: iButtons sind nicht fälschungssicher. Was aber manche Hotels nicht abhält, ihren Gästen als Zimmerschlüssel einen iButton auszuhändigen...
Frontplatte
Die Frontplatte besteht aus 4 mm starken Aluminium, eloxiert und auf 1/10 mm genau mit den passenden Bohrungen und Ausschnitten sowie rückseitig angeschweißten Gewindebolzen versehen. Online bestellbar z.B. bei der Schaeffer AG, für das vorliegende Exemplar wurden ca. 115 € in Rechnung gestellt. Auf Grund ihrer Stabilität dient diese Frontplatte als Baugruppenträger für die Außeninstallation.
Rückwandinstallation
Die Rückwandinstallation, die in des Mauerwerk eingelassen wird, kommt ebenfalls aus dem 3D-Drucker. Sie besteht aus drei Teilen mit unterschiedlicher Tiefe, um das Loch im Mauerwerk nicht zu groß werden zu lassen. Das obere und das untere Teil enthalten jeweils zwei stabile Vorsprünge mit Aussparungen, in die eine normgerechte M4-Mutter von unten genau hineinpasst. Bemerkenswert ist, dass dieses Bauteil nicht mit einer konventionellen Methode (z.B. im Spritzgussverfahren) gefertigt werden kann. Die hineingedrückten Muttern kommen damit genau hinter den äußeren Schraubenlöchern der Frontplatte zu liegen, Die Frontplatte kann dadurch mit vier (Sicherheits-)schrauben M4 passgenau auf die im Mauerwerk sitzende Rückwandinstallation geschraubt werden. Dafür werden Edelstahlschrauben mit Senkkopf und zwei Löchern auf der Oberseite verwendet (sog. Sicherheitsschrauben).
Auf der Oberkante der drei Teile verläuft eine 1x1 mm² große Nut, in die eine Dichtung eingelegt werden kann. Zur besseren Verankerung im Putz kann das zusammengeklebte Gehäuse noch mit Ohren versehen werden - siehe die beiden letzten Links in der nachfolgenden List.
Die Bauteile der Rückwandinstallation können aus beliebigem Material gefertigt werden, aber Achtung: Druckt man sie aus dme Material PLA (Poly-Milchsäure), ist zum Schutz gegen biologischen Abbau und Depolymerisation im Mauerwerk ein (äußerer) Überzug mit Sprühlack sinnvoll.
Hier die Links zu den betreffenden Dateien: CoverBottom4 CoverMiddle4 CoverTop4 CoverEarC4 CoverEarS4
Software
Die Software besteht aus verschiedenen Bestandteilen
- Auf dem Nextion-Display sind so genannte Seiten mit Bildern, Texten und aktiven Elementen (Widgets) gespeichert, diese werden mit dem zugehörigen Nextion-Editor des Herstellers erzeugt und entweder über serielle Schnittstelle oder über eine Micro-SD-Karte auf das Nextion übertragen.
- Auf dem Arduino läuft ein kleines Programm in einer Endlosschleife. Es führt die Abfrage des iButton-Lesers durch und steuert den Seitenwechel des Nextion-Displays, verarbeitet auch dessen Bedienvorgänge. Das Programm (Sketch in der Arduino-Terminologie) wird mit Hife der Arduino-Entwicklungsumgebung übersetzt und auf diesen übertragen.
- Auf dem Raspberry Pi wird nach einer der verfügbaren Anleitungen im DoorPi-Forum die DoorPi-Software installiert und konfiguriert
- Auf dem FHEM-System (z.B. einem weiteren Raspberry Pi) muss eine aktuelle Version von FHEM laufen.
Nextion-Subsystem
Für das hier Projekt wurden mit dem Nextion-Editor zwei Seiten erstellt (page0 und page1 genannt), die verschiedene Widgets enthalten, also bedienbare Elemente.
- page0 ist die Default-Seite, sie zeigt die Namen der Hausbewohner. Die gesamte Fläche ist als picture mit der ID p0 deklariert. Ein weiteres kleines picture mit der ID p1 dient der Anzeige eine kleinen Schloss-Symbols
- page1 ist das virtuelle Keyboard. Wie man in der nachstehenden Abbildung sehen kann, wurden dabei 11 verschiedene buttons definiert, nämlich b0-b9 für Ziffern und b10 als Cancel-Button. Außerdem gibt es es einen Fortschrittsbalken j0.
Um die Eingabe von Ziffern über dieses Display zu verarbeiten, müssen die Button-Widgets mit Callback-Funktionen versehen werden, dies geschieht weiter unten in dem Programm des Arduino.
Die fertigen Seiten werden durch den Nextion-Editor in einer kompakten Datei zusammengefasst, die entweder per serieller Schnittstelle an das Display übertragen wird, oder auf einer Micro-SD-Karte in das Nextion gesteckt wird. Beim Einschalten des Displays wird dann diese Datei automatisch in den internen Flash-Speicher des Nextion übernommen.
Arduino
Im Projekt wurde ein Arduino Micro verwendet - nahezu jede andere Version tut es auch. Das Programm darauf ist relativ einfach und wird im Folgenden erklärt.
Zuerst gibt es ein paar Deklarationen. Insbesondere müssen die Bibliotheken für den 1-Wire Anschluss (=iButton-Reader) und das Nextion Display eingebunden werden. Achtung: in der Datei NexConfig.h muss der Wert für nexSerial auf Serial1 gesetzt werden.
/*---------------------------------------------------------------------------------- Haustür Prof. Dr. Peter A. Henning, April 2016 ------------------------------------------------------------------------------------*/ #include <OneWire.h> #include <SPI.h> #include <SD.h> #include <SoftwareSerial.h> // Make sure that in NexConfig.h nexSerial is configured properly ! #include "Nextion.h"
Jetzt werden die Pins des Arduino ausgewählt. Außerdem wird in diesem Abschnitt die Sicherheits-PIN gesetzt, im Beispiel 12345
// Door Opener Subsystem const int DoorOpen = 8; // output for door opening const int LockState = 6; // output to indicate lock state byte softlock = 0; byte hardlock = 0; // Security PIN const int HardLock = 5; // input low = high security String PIN = "12345"; char pin[10]; char pindigit = ' '; byte pinctr = 0; long pinMillis = 0; const int WrongID = 7; // indicator for false 1-Wire ID or PIN // process variables const int loopLED = 13; // signal loop byte phase = 1; // phase of test long currentMillis = 0; // dimming const int Brightness = A0; // input pin for the dimming voltage const int Movement = A1; // input for movement detection const int DashDim = 3; // output for dimming further dashlights const int DashlightOn = 4; // input pin for the dashlight signal const long dimTimeout = 60000; byte isDimmed = 0; long dimMillis = 0; // timer
Hier folgt der Code für das iButton-System. Natürlich nicht mit meinen echten 1-Wire IDs. Wichtig ist, dass diese hier hart in den Code eingebaut werden. Das ist zwar etwas unkomfortabel, wenn man sie ändern möchte - aber sehr sicher gegen Manipulationen. Die IDs sind auf dem jeweiligen iButton eingelasert und lesen sich wie folgt:
F9 01 0000033ED394
Die Reihenfolge der Bytes ist hier gegenüber der eigentlichen 1-Wire-Adresse vertauscht, sie muss also von Rechts nach links gelesen werden:
01.94D33E030000.F9,
oder als hexadezimale Sequenz:
0x01 0x94 0xD3 0x3E 0x03 0x00 0x00 0xF9
- 01 ist die Family ID und identifiziert den Chip, hier ein DS2401
- Die nächsten 6 Byte sind die eindeutige Adresse
- F9 ist ein CRC8-Code, der sich aus den anderen Bytes ergibt.
Zur Überprüfung der Korrektheit eines iButton ist es also nur notwendig, die ersten 7 Byte der Adresse mit den Daten im Code zu vergleichen. Im Code wird also für diesen iButton, wenn er zu einer roten LED-Anzeige führen soll (Rot = 1,0,0 im RGB-Farbmodell, also LOW,HIGH,HIGH bei der Ansteuerung der drei LED):
"iRed", {0x01, 0x94, 0xD3, 0x3E, 0x03, 0x00, 0x00}, LOW, HIGH, HIGH}
eingetragen. Je nachdem, wie viele iButtons man einprogrammieren möchte, ist auch die Konstante iBNum anzupassen.
// 1-Wire subsystem OneWire ds(12); // 1-Wire on pin 12 (a 4.7K resistor is necessary) const int redLED = 11; // LED on pins 9,10,11 const int greenLED = 10; const int blueLED = 9; typedef struct { char* name; byte ROM[8]; int red; int green; int blue; } iButton; const byte iBnum = 7; // Number of defined iButtons const iButton iButtons[] = { {"iRed", {0x01, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--}, LOW, HIGH, HIGH}, {"iRed*", {0x01, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--}, LOW, HIGH, HIGH}, {"iGreen", {0x01, 00x--, 0x--, 0x--, 0x--, 0x--, 0x--}, HIGH, LOW, HIGH}, {"iBlue", {0x01, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--}, HIGH, HIGH, LOW}, {"iOrange", {0x01, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--}, LOW, LOW, HIGH}, {"iPink", {0x01, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--}, LOW, HIGH, LOW}, {"iPurple", {0x01, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--}, LOW, HIGH, LOW} };
In dieser Version des Programms gibt es auf dem Nextion-Display nur zwei Seiten, eine mit den Namen und eine mit einem Keyboard. Die Keyboard-Buttons werden hier bekannt gemacht und mit Callbacks versehen.
// GUI NexPage page0 = NexPage(0, 0, "page0"); NexPage page1 = NexPage(1, 0, "page1"); NexButton p0 = NexButton(0, 1, "p0"); NexPicture p1 = NexPicture(0, 2, "p1"); NexButton num0 = NexButton(1, 11, "b0"); NexButton num1 = NexButton(1, 2, "b1"); NexButton num2 = NexButton(1, 3, "b2"); NexButton num3 = NexButton(1, 4, "b3"); NexButton num4 = NexButton(1, 5, "b4"); NexButton num5 = NexButton(1, 6, "b5"); NexButton num6 = NexButton(1, 7, "b6"); NexButton num7 = NexButton(1, 8, "b7"); NexButton num8 = NexButton(1, 9, "b8"); NexButton num9 = NexButton(1, 10, "b9"); NexButton cancel = NexButton(1, 12, "b10"); NexProgressBar progress = NexProgressBar(1, 13, "j0"); NexTouch *nex_Listen_List[] = { &num0, &num1, &num2, &num3, &num4, &num5, &num6, &num7, &num8, &num9, &cancel, &p0, NULL }; void p0Callback(void *ptr) { dimLight(100); softlock = 1; if ( hardlock == 0) { showLock(); } } void num0PushCallback(void *ptr) { pindigit = '0'; } void num1PushCallback(void *ptr) { pindigit = '1'; } void num2PushCallback(void *ptr) { pindigit = '2'; } void num3PushCallback(void *ptr) { pindigit = '3'; } void num4PushCallback(void *ptr) { pindigit = '4'; } void num5PushCallback(void *ptr) { pindigit = '5'; } void num6PushCallback(void *ptr) { pindigit = '6'; } void num7PushCallback(void *ptr) { pindigit = '7'; } void num8PushCallback(void *ptr) { pindigit = '8'; } void num9PushCallback(void *ptr) { pindigit = '9'; } void CancelCallback(void *ptr) { pinctr = 0; progress.setValue(0); }
Es folgt die Initialisierungsroutine
void setup() { String cmd; // set the digital pins as output: pinMode(redLED, OUTPUT); pinMode(greenLED, OUTPUT); pinMode(blueLED, OUTPUT); pinMode(loopLED, OUTPUT); pinMode(DoorOpen, OUTPUT); pinMode(WrongID, OUTPUT); pinMode(LockState, OUTPUT); pinMode(HardLock, INPUT_PULLUP); pinMode(DashlightOn, INPUT_PULLUP); digitalWrite(redLED, HIGH); digitalWrite(greenLED, HIGH); digitalWrite(blueLED, HIGH); digitalWrite(loopLED, LOW); digitalWrite(DoorOpen, HIGH); digitalWrite(WrongID, HIGH); digitalWrite(LockState, HIGH); // initialize the GUI nexInit(); p0.attachPush(p0Callback, &p0); num0.attachPush(num0PushCallback, &num0); num1.attachPush(num1PushCallback, &num1); num2.attachPush(num2PushCallback, &num2); num3.attachPush(num3PushCallback, &num3); num4.attachPush(num4PushCallback, &num4); num5.attachPush(num5PushCallback, &num5); num6.attachPush(num6PushCallback, &num6); num7.attachPush(num7PushCallback, &num7); num8.attachPush(num8PushCallback, &num8); num9.attachPush(num9PushCallback, &num9); cancel.attachPush(CancelCallback, &cancel); //dimming dimLight(100); //showlock if ( digitalRead(HardLock) == LOW) { showLock(); hardlock = 1; softlock = 0; } else { hideLock(); hardlock = 0; softlock = 0; } }
Türöffnung -> Pin mit Name DoorOpen wird für eine Sekunde auf LOW gesetzt.
void openDoor(int level) { digitalWrite(DoorOpen, LOW); delay(1000); digitalWrite(DoorOpen, HIGH); }
Falsche ID -> Pin mit Name WrongID wird für eine Sekunde auf LOW gesetzt.
void wrongID() { digitalWrite(WrongID, LOW); delay(1000); digitalWrite(WrongID, HIGH); }
Mit den beiden nachfolgenden Kommandos wird ein Schloss-Symbol auf dem Nextion angezeigt bzw. ausgeblendet
void showLock() { sendCommand("vis p1,1"); } void hideLock() { sendCommand("vis p1,0"); }
Dimmen des Dashlight auf einen durch den Helligkeitssensor vorgegebenen Wert (Parameter level=0) oder auf einen als Parameter übergebenen Wert
void dimLight(int level) { uint16_t dimVal; String cmd = "dim="; // zero level - determine from lighting conditions if ( (level == 0) && (digitalRead(DashlightOn) == HIGH) ) { dimVal = (uint32_t) analogRead(Brightness); dimVal = map(dimVal, 0, 1023, 0, 100); isDimmed = 1; // nonzero level - take as it is } else { dimVal = 100; isDimmed = 0; } cmd += dimVal; //dbSerialPrint(cmd); sendCommand(cmd.c_str()); dimVal = map(dimVal, 0, 100, 0, 255); //dbSerialPrint(" -- "); //dbSerialPrintln(dimVal); analogWrite(DashDim,dimVal); dimMillis = millis(); }
Hier endlich die Schleife des Hauptprogramms
void loop(void) { byte i; byte j; boolean equiv; byte iBfound; byte present = 0; byte addr[8]; char* device; // new for each loop currentMillis = millis(); digitalWrite(loopLED, HIGH); // dimming if ( isDimmed == 1 ) { if ( analogRead(Movement) < 10 ) { dimLight(100); } if ( digitalRead(DashlightOn) == LOW) { //dbSerialPrintln(" DashlightOn = LOW"); dimLight(100); } } if ( (currentMillis - dimMillis) > dimTimeout) { dimLight(0); } // locking if ( digitalRead(HardLock) == LOW) { //change display only if hardlock has changed if ( hardlock == 0 ) { showLock(); } hardlock = 1; softlock = 0; } else { //change display only if if ( (hardlock == 1) && (softlock == 0)) { hideLock(); } hardlock = 0; } //lockState display if ( (hardlock == 0) && (softlock == 0)) { digitalWrite(LockState, HIGH); } else { digitalWrite(LockState, LOW); } //1-Wire bus access only in phase 1 if ( phase == 1) { digitalWrite(redLED, HIGH); digitalWrite(greenLED, HIGH); digitalWrite(blueLED, HIGH); if ( !ds.search(addr)) { present = 0; ds.reset_search(); digitalWrite(loopLED, LOW); delay(250); } else { digitalWrite(loopLED, LOW); // Invalid 1-Wire ID if (OneWire::crc8(addr, 7) != addr[7]) { } else { dimLight(100); // the first ROM byte indicates which chip switch (addr[0]) { case 0x01: device = "DS2401"; present++; for (i = 0; i < iBnum; i++) { equiv = true; for (j = 0; j < 7; j++) { if (iButtons[i].ROM[j] != addr[j]) { equiv = false; break; } } if (equiv ) { iBfound = i; break; } } if ( equiv ) { digitalWrite(redLED, iButtons[iBfound].red); digitalWrite(greenLED, iButtons[iBfound].green); digitalWrite(blueLED, iButtons[iBfound].blue); // LowSec state => open door if ( (hardlock == 0) && (softlock == 0)) { openDoor(1); delay(4000); return; // HiSec state => go to phase 2 } else { phase = 2; pinMillis = millis(); page1.show(); return; } //sabotage ? Unknown iButton } else { digitalWrite(redLED, LOW); digitalWrite(greenLED, LOW); digitalWrite(blueLED, LOW); wrongID(); } break; default: device = "unknown"; break; } ds.reset(); } } } nexLoop(nex_Listen_List); if ( phase == 2 ) { // check for timeout if ( (currentMillis - pinMillis) > 30000 ) { progress.setValue(0); phase = 1; pinctr = 0; page0.show(); } } // phase 2 and GUI input is a number if ( (phase == 2) && ( pindigit != ' ') ) { // push this number to the PIN buffer pin[pinctr] = pindigit; pindigit = ' '; pinctr++; progress.setValue(pinctr * 20); // PIN is complete if ( pinctr == 5) { // PIN is correct => open door if ( String(pin) == PIN ) { openDoor(2); // wrong PIN } else { wrongID(); } // return to phase 1 progress.setValue(0); phase = 1; pinctr = 0; page0.show(); } } }
FHEM
Aufseiten von FHEM müssen die Perl-Module JSON und Test::JSON installiert werden. Ferner muss die Datei 70_DoorPi.pm aus dem Ordner contrib/doorpi in das Hauptverzeichnis von FHEM geschoben werden. Eine beispielhafte Konfiguration in FHEM lautet dann:
define A.Door.Pi DoorPi 192.168.0.YY attr A.Door.Pi alarmDevice Sensor attr A.Door.Pi alarmSettings alarm4,alarm5,|A.Door.Pi:.*sabotage|Türstation|on attr A.Door.Pi doorlockcmd set A.Door.T locked attr A.Door.Pi doorunlockcmd set A.Door.T unlocked attr A.Door.Pi target0 telefonnummer1 attr A.Door.Pi target1 telefonnummer2
- Dabei ist natürlich die IP-Adresse des DoorPi-Rechners einzutragen.
- Die Attribute alarmDevice/alarmSettings sind nur zu verwenden, wenn man das Modul 95_Alarm.pm benutzt.
- Die Attribute doorlockcmd/doorunlockcmd sind die Kommandos, die von FHEM ausgeführt werden, wenn die Haustür wirklich abgeschlossen werden soll.
Notify zur Anzeige des gegenwärtigen Bildes
Ruft man im FHEM-Frontend das Kommando set A.Door.Pi snapshot auf, oder wird der Klingelknopf betätigt, gibt es in FHEM ein Event A.Door.Pi snapshot: <url>. Dieses kann man mit einem Notify abfangen, beispielsweise wird mit
define A.Door.Pi.img notify A.Door.Pi:snapshot.* { fhem("set GalaxyTab tickerMessage A.Door.Pi $EVTPART1");; fhem("set Archos7Tab tickerMessage A.Door.Pi $EVTPART1")}
eine entsprechende Message an zwei Tablets gesendet.
DoorPi
Die DoorPi-Software wird laut diversen Anleitungen im DoorPi Forum auf dem Raspberry Pi installiert. Danach liegt sie im Pfad der Python-Installation - ziemlich ungünstig für weitere Arbeiten. Deshalb wird zunächst ein Verzeichnis /home/doorpi angelegt. In dieser liegen dann verschiedene Hilfsdateien und Hilfsverzeichnisse.
csrfToken
In der aktuellen Version von FHEM wird ein so genanntes csrf-Token eingesetzt, um die Sicherheit gegen die so genannte "Cross Site Request Forgery" zu gewährleisten. Das ist ein zufällig festgelegter String, der für das Ausführung von webbasierten Kommandos dem Aufruf der entsprechenden FHEM-Seite angefügt werden muss. Eine genaue Anleitung dazu gibt es im CsrfToken-HowTo. Für dieses DoorPi-Projekt ist es zwar möglich, dieses csrf-Token jedesmal neu zu holen, auf der Seite von DoorPi zwischenzuspeichern und dann beim Kommando-Aufruf für FHEM zu nutzen. Sinnvoller ist es, für DoorPi-Aufrufe von FHEM einen speziellen Zugang in FHEM anzulegen, so wie dies im Abschnitt API_Web beschrieben wurde, und nur für diesen Zugang dann das csrf-Token abzuschalten.
FHEMHelper.sh
Diese Datei ist eine Skriptdatei, mit der diverse Hilfsfunktionen ausgeführt werden. Zwar lassen sich diese in einigen Fällen auch als Einzeiler in der doorpi.ini verstecken, als separate Datei im Verzeichnis /home/doorpi ist diese Helferdatei jedoch leichter änderbar.
# /bin/sh FHEMDP="A.Door.Pi" FHEMIP="192.168.0.XX" FHEM="http://192.168.0.XX:8083/fhem?XHR=1&cmd.$FHEMDP" HOME="/home/doorpi" default_target="yyyyyy" case $1 in init) target=`cat $HOME/calltarget` curl "$FHEM=setreading%20$FHEMDP%20call_target%20$target" & streampid=`pidof mjpg_streamer` if [ -z "$streampid" ]; then curl "$FHEM=setreading%20$FHEMDP%20stream%20off" & else curl "$FHEM=setreading%20$FHEMDP%20stream%20on" & fi ;; doorunlockandopen) curl "$FHEM=set%20GalaxyTab%20ttsSay%20Ein%20Bewohner%20betritt%20das%20Haus" & curl "$FHEM=set%20$FHEMDP%20door%20unlockandopen" & ;; wrongid) curl "$FHEM=set%20GalaxyTab%20ttsSay%20Unerlaubter%20Zutrittsversuch" & curl "$FHEM=set%20$FHEMDP%20door%20wrong_id" & ;; softlock) curl "$FHEM=set%20$FHEMDP%20door%20softlock" & ;; call) curl "$FHEM=set%20$FHEMDP%20call%20$2" & ;; gettarget) echo "{ReadingsVal('$FHEMDP','call_target','$default_target')}" | socat -t50 - TCP:$FHEMIP:7072 > $HOME/calltarget ;; purge) find $HOME/records/ -type f -ctime 1 -delete ;; movement) curl "$FHEM=set%20$FHEMDP%20door%20movement" & ;; sabotage) curl "$FHEM=set%20$FHEMDP%20door%20sabotage" & ;; esac
doorpi.ini
Die Konfigurationsdatei doorpi.ini liegt in der Standardinstallation von DoorPi im Verzeichnis /usr/local/etc/DoorPi/conf, sie wird deshalb per Softlink mit /home/doorpi.ini verbunden. In dieser Datei stehen diverse Konfigurationsangaben für DoorPi.
[DoorPi] base_path = /usr/local/etc/DoorPi snapshot_path = /home/doorpi/records number_of_snapshots = 10 eventlog = /home/doorpi/log/eventlog.db is_alive_led = blinking_led last_snapshot = [DoorPiWeb] indexfile = index.html loginfile = login.html online_fallback = http://motom001.github.io/DoorPiWeb port = 80 public = AREA_public www = /home/doorpi/records [AREA_public] .* [AREA_config] /control/config_value_get /control/config_value_set /control/config_value_delete /control/config_save /control/config_get_configfile [AREA_dashboard] /dashboard/pages/.*html [AREA_status] /status /mirror [AREA_control] .* [User] admin = admin visitor = visitor [Group] administrators = admin guests = visitor [WritePermission] administrators = dashboard,status,config [ReadPermission] guests = dashboard [AdminNumbers] **621 = active [DTMF] "#" = out:door,1,0,3 ####################### SIP phone ####################### [SIP-Phone] identity = DoorPi local_port = 5060 firewallpolicy = PolicyNoFirewall # sipphonetyp = linphone sipserver_password = xxxxxxxxxxxxx sipserver_realm = fritz.box sipserver_server = yyyyyyyyyyyy sipserver_username = 620 stun_server = # max_call_time = 300 call_timeout = 60 ua.max_calls = 2 # capture_device = ALSA: USB PnP Sound Device playback_device = ALSA: USB PnP Sound Device audio_codecs = PCMA,PCMU record_while_dialing = False records = /home/doorpi/records/%Y-%m-%d_%H-%M-%S.wav # dialtone = /home/doorpi/sounds/ShortDialTone.wav dialtone_renew_every_start = False dialtone_volume = 35 echo_cancellation_enabled = False # video_codecs = VP8 video_device = StaticImage: Static picture video_display_enabled = False video_size = vga
Mit den nachfolgenden Events sendet DoorPi Nachrichten an FHEM:
####################### Events ####################### [EVENT_OnStartup] 10 = sleep:1 20 = os_execute:/home/doorpi/FHEMHelper.sh call init [EVENT_BeforeSipPhoneMakeCall] 10 = out:irlight,1 20 = os_execute:/home/doorpi/FHEMHelper.sh call startup 30 = take_snapshot 40 = out:irlight,0 [EVENT_OnCallStateDisconnect] 10 = os_execute:/home/doorpi/FHEMHelper.sh call end [EVENT_OnCallStateDismissed] 10 = os_execute:/home/doorpi/FHEMHelper.sh call dismissed [EVENT_OnCallStateReject] 10 = os_execute:/home/doorpi/FHEMHelper.sh call rejected [EVENT_OnTimeMinuteEvery5] 10=statuswatchdog:/tmp/doorpi.watchdog
Hier wird ein virtuelles Keyboard (=I/O-System) mit dem Namen 'webservice sowie die reale I/O-Hardware, in diesem Falle das PiFace2-Board definiert.
####################### Keyboards ############################## [keyboards] webservice = filesystem onboardpins = piface
Wichtig sind die virtuellen Buttons, die von FHEM per URL-Aufruf aktiviert werden können.
- Dabei lösen diese Aufrufe entweder eine direkte Reaktion des PiFace-Moduls aus, z.B. wird mit dem virtuellen Button doorlocked (Aufruf '<URL des DoorPi>/control/trigger_event?event_name=OnKeyPressed_webservice.doorlocked&event_source=doorpi.keyboard.from_filesystem') der reale Output hardlock eingeschaltet (Zustand 1 => Ausgang Low).
- Oder sie rufen dass DoorPi-seitige Hilfsprogramm FHEMHelper.sh auf.
####################### Virtual keyboard ####################### [webservice_keyboard] base_path_input = /home/doorpi/keyboard/inputs/ base_path_output = /home/doorpi/keyboard/outputs/ reset_input=false [webservice_InputPins] dooropen = out:door,1,0,3 doorlocked = out:hardlock,1 doorunlocked = out:hardlock,0 snapshot = sleep:0 streamon = sleep:0 streamoff = sleep:0 lighton = out:light,1 #lightonfortimer = out:light,1,0,60 lightoff = out:light,0 dashlighton = out:dashlight,1 dashlightoff = out:dashlight,0 gettarget = sleep:0 purge = sleep:0 clear = sleep:0 button1 = sleep:0 button2 = sleep:0 #-- communicate to FHEM that a snapshot has been taken [EVENT_OnKeyPressed_webservice.snapshot] 10 = out:irlight,1 20 = os_execute:/home/doorpi/FHEMHelper.sh call snapshot 30 = take_snapshot 40 = out:irlight,0 #-- start video stream [EVENT_OnKeyPressed_webservice.streamon] 10 = os_execute:/etc/init.d/mjpg_streamer start #-- stop video stream [EVENT_OnKeyPressed_webservice.streamoff] 10 = os_execute:/etc/init.d/mjpg_streamer stop #-- obtain the target call number from FHEM [EVENT_OnKeyPressed_webservice.gettarget] 10 = os_execute:/home/doorpi/FHEMHelper.sh gettarget #-- purge all files older than one day [EVENT_OnKeyPressed_webservice.purge] 10 = os_execute:/home/doorpi/FHEMHelper.sh purge [EVENT_OnKeyPressed_webservice.button1] 10 = os_execute:/home/doorpi/FHEMHelper.sh sabotage [EVENT_OnKeyPressed_webservice.button2] 10 = file_call_value:/home/doorpi/calltarget
Es folgt die Definition der realen Buttons:
####################### Real keyboard ####################### [onboardpins_keyboard] pull_up_down = PUD_UP [onboardpins_OutputPins] 0 = door 1 = light 2 = dashlight 3 = irlight 4 = hardlock 7 = blinking_led [onboardpins_InputPins] 0 = file_call_value:/home/doorpi/calltarget 1 = sleep:0 4 = sleep:0 5 = sleep:0 6 = sleep:0 7 = sleep:0 #-- DoorOpen pin from Arduino [EVENT_OnKeyPressed_onboardpins.1] 10 = os_execute:/home/doorpi/FHEMHelper.sh doorunlockandopen 20 = os_execute:aplay -D plughw:1,0 /home/doorpi/sounds/067_willkommen.wav #-- WrongID pin from Arduino [EVENT_OnKeyPressed_onboardpins.4] 10 = out:irlight,1 20 = os_execute:/home/doorpi/FHEMHelper.sh wrongid 30 = take_snapshot 40 = out:irlight,0 #-- LockState pin from Arduino - FHEM will transform softlock into hardlock [EVENT_OnKeyPressed_onboardpins.5] 10 = os_execute:/home/doorpi/FHEMHelper.sh softlock #-- Movement detection [EVENT_OnKeyPressed_onboardpins.6] 10 = out:dashlight,1,0,3 20 = os_execute:/home/doorpi/FHEMHelper.sh movement #-- Sabotage detection [EVENT_OnKeyPressed_onboardpins.7] 10 = os_execute:/home/doorpi/FHEMHelper.sh sabotage
mjpg_streamer
Diese Software dient dazu, einen kleinen Webserver mit MJPEG-Datenstrom aus der Raspberry Pi-Kamera zur Verfügung zu stellen. Sie wird mit Hilfes des Skriptes /etc/init.d/mjpeg_streamer gestartet und gestoppt
sonstige Software
Auf dem Raspberry Pi wird noch benötigt
- curl - zum Aufruf einer URL
- socat - zum Aufruf einer URL und Verarbeitung der Rückgabe (könnte man auch durch curl ersetzen)