Bewegungsmelder Typ PIR am Raspberry Pi
Zu einer vollständigen Hausautomatisierung oder einer kleinen Alarmanlage gehört natürlich das Erkennen von Bewegungen bzw. der Präsenz von Personen. Eine relativ günstige Variante eines solchen Sensors ist als PIR in den weiten des Internets erhältlich. Dieser Artikel beschreibt den Anschluss, die Konfiguration sowie den Betrieb dieses Sensors.
Material
- PIR-Sensor, zu beziehen beispielsweise von hier
- Anschlusskabel, wie diese
- Raspberry Pi mit allen notwendigen Komponenten (SD-Karte mit Raspian, Netzteil usw.)
Den Sensor anschließen
Das Anschließen gestaltet sich recht einfach, da die PINs am Sensor beschriftet sind. Der PIR-Sensor bentötigt zum Betrieb eine Versorgungsspannung von 5VDC und sollte demnach mit einem entsprechenden Anschluss am Raspberry verbunden werden. Ground erklärt sich selbst und als Data-Anschluss nehmen wir einen noch freien GPIO-Port. Daraus ergibt sich folgendes Anschlussbild:
Zum Auslesen des PIR bieten sich mehrere Möglichkeiten an.
Auslesen via wiringPi und einem Shell-Script
Ist die wiringPi-Bibliothek bereits wie in diesem Beitrag beschrieben installiert, kann der Sensor mit
gpio read 11
ausgelesen werden (GPIO 7 aus dem Bild ergibt wiringPi-Port 11).
Wurde im Moment des Auslesens eine Bewegung vom Sensor registriert, wird „1“ ausgegeben, ansonsten „0„. Nun könnte dieses Befehlsaufruf mit einem Script in einer Schleife ausgeführt werden. Wir erstellen eines leeres Dokument z.B. mit dem Editor „nano„:
nano /tmp/pir.sh
und befüllen dieses mit folgenden Zeilen:
#!/bin/bash while true do gpio read 11 done
Anschließend kann der Editor nano mit „STRG + X“ geschlossen werden. Die Nachfrage, ob die soeben erstellte Datei gespeichert werden sollen, sollte jeder selbst beantworten können. Nun sorgt der Aufruf von
chmod +x /tmp/pir.sh
dafür, dass unser Script ausführbar ist.
Jetzt kann dieses mit
/tmp/pir.sh
gestartet werden.
Durch das Script wird eine erkannte Bewegung live ausgegeben. Gestoppt wird dieses kleine Script mit der Tastenkombination „STRG + C„. Leider ist diese Methode sehr prozessorlastig, da viele Male pro Sekunde der Status des GPIO-Ports abgefragt wird. Daher stelle ich nun eine elegantere Lösung mittels Python vor.
Auslesen via Python
Das hier vorgestellte Python-Script nutzt eine Funktion des Raspberrys, um den Status eines GPIO-Ports abzufragen. Dazu wird keine sekündliche Endlosschleife wie im Shell-Script oben genutzt, sondern der jeweilige Port gezielt initialisiert, so dass dieser eine Statusänderung an das Script meldet. Daher ist auch eine Wartezeit (in diesem Script von 100 Sekunden) zwischen den Durchläufen der Schleife möglich und die CPU wird deutlich entlastet. Im folgenden Beispiel wurde der PIR-Sensor am GPIO-Port 25 des Raspberry Pi’s angeschlossen.
import RPi.GPIO as GPIO import time import os GPIO.setmode(GPIO.BCM) PIR_PIN = 25 GPIO.setup(PIR_PIN, GPIO.IN) def MOTION(PIR_PIN): os.system("echo "Bewegung erkannt"") print "PIR Testscript (STRG+C zum beenden)" time.sleep(2) print "Bereit" try: GPIO.add_event_detect(PIR_PIN, GPIO.RISING, callback=MOTION) while 1: time.sleep(100) except KeyboardInterrupt: print "Beenden" GPIO.cleanup()
Da es des Öfteren zu Problemen beim Kopieren des Scripts kommt, habe ich es als Download zur Verfügung gestellt:
Erläuterung:
Die Zeile „PIR_PIN = 25“ legt den abzufragenden GPIO-Port fest. Diesmal zählt die originale Nummerierung des Raspberry Pi gemäß folgender Tabelle.
Die Zeile
os.system("echo "Bewegung erkannt"")
legt die Aktion fest, die bei einer erkannten Bewegung des PIR-Sensor ausgeführt werden soll. In diesem Fall gibt das Script mittels „echo“ auf die Befehlszeile den Text „Bewegung erkannt“ aus. Ebenso könnte eine Aktion in openHAB ausgelöst werden, wenn via curl eine URL der openHAB-REST API abgerufen wird. Dies könnte etwa so aussehen:
os.system("/usr/bin/curl --header "Content-Type: text/plain" --request POST --data "ON" http://IP_DES_OPENHAB_SERVERS:8080/rest/items/ITEMNAME")
Die Tastenkombination „STRG + C“ beendet das Script. Nun könnte das Script bei einem Systemstart automatisch mitgeladen werden und im Hintergrund als Dienst laufen.
Python-Script als Systemdienst einrichten
Um das oben gezeigte Script automatisch beim Systemstart als Hintergrunddiest zu starten, wird ein sogenanntes init-Script benötigt. Diese liegen grundsätzlich im Verzeichnis „/etc/init.d/„. Daher erstellen wir mit einem Editor des höchsten Wohlfühlgrades ein neues Script:
cd /etc/init.d/ nano pirsensor
Das Inhalt könnte beispielhaft so aussehen:
#!/bin/sh # chkconfig: 123456 90 10 # Startscript fuer den PIR-Sensor # verzeichnis=/scripte scriptname=pirsensor.py start() { cd $verzeichnis /usr/bin/python $verzeichnis/$scriptname & echo "PIR-Sensor gestartet." } stop() { pid=`ps -ef | grep '[p]ython $verzeichnis/$scriptname' | awk '{ print $2 }'` echo $pid kill $pid sleep 2 echo "PIR-Sensor beendet." } case "$1" in start) start;; stop) stop ;; restart) stop start ;; *) echo "Benutzung: /etc/init.d/pirsensor {start|stop|restart}" exit 1 esac exit 0
Die Variablen „verzeichnis“ und „scriptname“ müssen natürlich an die eigenen Bedingungen angepasst werden. Die Tastenkombination „STRG + X“ beendet nano und fragt, ob die Änderungen gespeichert werden sollen. Das Script wird nun durch
chmod +x /etc/init.d/pirsensor
mit dem Attribut „ausführbar“ versehen und
/sbin/chkconfig pirsensor on
aktiviert das neue init-Script beim Systemstart. Wie die Daten an openHAB übergeben und dort weiter verarbeitet werden, zeigt dieser Artikel.
Daniel Wenzel
27.01.2016 @ 17:41
Ich habe das Script als Download in den Beitrag eingefügt. Ich hoffe, das hilft bei deinem Problem.
Christian
27.01.2016 @ 09:00
Hallo Daniel,
vielen Dank für deine tollen Erklärungen. Gerade als Einsteiger wirklich mehr als wertvoll!
Eine Frage habe ich bzgl. des Python-scripts. Könntest du deines zur Verfügung stellen? Ich hab das Problem, dass der Item nicht mehr auf OFF geht. Ich vermute ein Problem mit den Einrückungen (bin blutiger python Noob). Mit folgender Zeile funktioniert das ON aber das OFF nicht.
def MOTION(PIR_PIN):
os.system(„/usr/bin/curl –header \“Content-Type: text/plain\“ –request POST –data \“ON\“ http://192.168.1.233:8080/rest/items/PIR„)
time.sleep(5)
os.system(„/usr/bin/curl –header \“Content-Type: text/plain\“ –request POST –data \“OFF\“ http://192.168.1.233:8080/rest/items/PIR„)
time.sleep(2)
Wenn ich die OFF Zeile einrücke bekomme ich einen „IndentationError: unexpected indent“. Befehl „python -tt /scripts/pirsensor.py“ zeigt keinen Leerzeichen/Tab Fehler an…
Dank für deine Hilfe
Daniel Wenzel
10.01.2016 @ 17:36
Ich habs so gelöst:
import RPi.GPIO as GPIO
import time
import os
GPIO.setmode(GPIO.BCM)
PIR_PIN = 25
GPIO.setup(PIR_PIN, GPIO.IN)
def MOTION(PIR_PIN):
os.system("/usr/bin/curl --header \"Content-Type: text/plain\" --request POST --data \"ON\" http://IP_ADRESSE_OPENHAB/rest/items/PIR_WZ")
time.sleep(5)
os.system("/usr/bin/curl --header \"Content-Type: text/plain\" --request POST --data \"OFF\" http://IP_ADRESSE_OPENHAB/rest/items/PIR_WZ")
time.sleep(2)
try:
GPIO.add_event_detect(PIR_PIN, GPIO.RISING, callback=MOTION)
while 1:
time.sleep(100)
except KeyboardInterrupt:
GPIO.cleanup()
Matthias
10.01.2016 @ 17:30
Hallo,
eine Frage habe ich: Wie setzt man um, den Bewegungssensor in Openhab einzubinden?
Mit der curl kann ich das Dummy-Item bei „Bewegung“ updaten. Doch wann wir der Status wieder zu abwesend geändert? Das ITem würde doch immer auf „ON“ bleiben?
Kannst du mir weiterhelfen?
Viele Grüße
Matthias
Daniel Wenzel
04.01.2016 @ 05:56
Hallo Simon,
solche Probleme sind bei mir bisher nicht aufgetreten, ich werde das mal beobachten und ggf. deine Version des Scripts testen.
Danke und viele Grüße
Daniel
Simon Schürmann
01.01.2016 @ 22:57
Hallo zusammen,
Vielen Dank Erstmal für den super Artikel.
Bei mir verursachte das Python Script leider zu Beginn auch Probleme. Ich musste die Doppelhochkommas bei os.system anpassen. Zusätzlich musste ich alle Ausgaben – print in Klammern setzen.
So hat es dann funktioniert:
import RPi.GPIO as GPIO
import time
import os
GPIO.setmode(GPIO.BCM)
PIR_PIN = 25
GPIO.setup(PIR_PIN, GPIO.IN)
def MOTION(PIR_PIN):
os.system('echo "Bewegung erkannt"')
print ("PIR Testscript (STRG+C zum beenden)")
time.sleep(2)
print ("Bereit")
try:
GPIO.add_event_detect(PIR_PIN, GPIO.RISING, callback=MOTION)
while 1:
time.sleep(100)
except KeyboardInterrupt:
print ("Beenden")
GPIO.cleanup()
Ich weiss nicht ob vielleicht mein Python in einem strikteren Modus läuft? Da kenne ich mich zu wenig aus. Vielleicht hilft es jemand anderem auch mit meiner Version.
Michael Bodden
14.12.2015 @ 15:45
Hallo zusammen,
ich möchte auch den Bewegungsmelder nutzen. Hat jemand hier ein Gehäuse gefunden, in dem man den Melder unterbringen kann? Am besten eins für Innen- und Aussenbereich?
Danke für Eure Antworten
Daniel Wenzel
03.12.2015 @ 13:32
Hallo Matthias,
leider reichen meine Python-Kenntnisse dafür nicht aus. Da ich die Sensoren nur im Zusammenhang mit openHAB nutze, würde ich hier eine Regel erstellen, so dass openHAB das Relais nach 5 Minuten wieder ausschaltet. Aber ich denke, das wird nicht in deinem Sinne sein.
Matthias Lutter
03.12.2015 @ 13:13
Hallo Daniel,
ich verwende dein skript, um ein Relais anzusteuern.Dieses schaltet einen Bildschirm an, wenn man davor sitzt. Leider bekomme ich es nicht hin, eine Relayverzögerung mittels time.sleep() einzubauen. Ziel ist, dass ein Ausgabe GPIO Pin nach aktivierung des Sensors 5 min gesetzt wir und dann wieder abfällt. Bisher habe ich die Zeile:
GPIO.output(RELAY_PIN, not GPIO.input(PIR_PIN))
unter …os.system()
eingefügt und das Relay schaltet auch, sowie der Sensor eine Bewegung erkennt.
Vielleicht kannst du mir weiterhelfen?
Viele Grüße
Matthias
Daniel Wenzel
30.05.2015 @ 18:49
Hallo,
danke für deine Hinweise. Zwei Zeilen im Python-Script waren in der Tat nicht korrekt eingerückt. Das habe ich nun im Beitrag korrigiert. Der PIR-Sensor muss jedoch nicht zwingend am GPIO-Port 7 angeschlossen sein. Im zweiten Beispiel des Beitrags hängt der Sensor am GPIO-Port 25 und dieser muss auch so im Script angegeben werden.
Sascha
29.05.2015 @ 19:37
Das Script funktioniert so leider nicht, da PIR_PIN 7 sein muss (GPIO, nicht physikalischer Pin) und die while-Einrückung fehlerhaft ist.
So funktioniert’s:
import RPi.GPIO as GPIO
import time
import os
GPIO.setmode(GPIO.BCM)
PIR_PIN = 7
GPIO.setup(PIR_PIN, GPIO.IN)
def MOTION(PIR_PIN):
os.system(„echo „Bewegung erkannt““)
print „PIR Testscript (STRG+C zum beenden)“
time.sleep(2)
print „Bereit“
try:
GPIO.add_event_detect(PIR_PIN, GPIO.RISING, callback=MOTION)
while 1:
time.sleep(100)
except KeyboardInterrupt:
print „Beenden“
GPIO.cleanup()