Raspberry-Pi-Projekte: PIR Bewegungsmelder HC-SR501

Prof. Jürgen Plate

Raspberry Pi: PIR Bewegungsmelder HC-SR501

Allgemeines

Mittlerweile gibt es passive Bewegungsmelder für einige Euro bei Ebay, Amazon und anderen Händlern. Solche Bewegungsmelder laufen unter der Bezeichnung "PIR-Sensor", wobei "PIR" für "Passive Infra Red" (passiver Infrarot-Sensor) steht. Solche Infarot-Bewegungsmelder (meist nur "Bewegungsmelder" genannt) dienen oft zum automatischen Einschalten von Beleuchtung etc. Sie erkennen eine bestimmte Menge an Infarotstrahlen/Wärme) und werten diese Information aus. Schlicht gesagt, reagieren Sie auf die Wärme des menschlichen Körpers (oder auch von Hund, Katze usw.). Damit aber nicht staische Wärmequellen (besonntes Fenster, Heizkörper etc.) den Senso triggern, regieren solche Sensoren auf bewegliche Wärmequellen. Damit das funktioniert, ist eine Fesnel-Linse vor dem Sensor augeordnet. Das folgende Bild zeigt den PIR-Sensor auf der Platine und daneben die abgenommene Fresnel-Linse.

Die im Handel erhältliche Sensorplatine HC-SR501 hat eine Reichweite von etwa 7 Metern und einen Erfassungswinkel von ca. 120 Grad. Die Platine trägt die Auswerte-Elekronik mit dem IC BISS0001 und einen 3,3-V-Spannungsregler, was den Vorteil hat, dass sich das Ausgsangssignal direkt in den Raspberry Pi einspeisen lässt. Auf der Platine befinden sich neben den Anschlusspins noch zwei Potenziometer und ein Jumper. Die folgenden Bilder zeigen die Vorder- und Rückseite der Platine. Das Bild darunter einen Schaltplan, der aber nicht ganz der HC-SR501 entspricht. Hier ist noch ein Fotowiderstand (Cos2) enhalten, der die Ausgabe deaktiviert, wenn es hell genug ist - die Schaltung ist speziell für Beleuchtungszwecke gedacht.

       

Anschluss und Einstellung

Der Anschluss ist denkbar einfach. Es gibt nur drei Anschlusspins, +5 V, GND und Ausgang. Der Ausgang liefert 0 V, wenn keine Bewegung erkannt wird und +3,3 V, wenn sich vor dem Sensor eine Wärmequelle bewegt. Zm Verdrahten mit dem RasPi nimmt man am Besten drei Jumperkabel mit jeweils einer Buchse am Ende. Die Pins auf der Platine des Moduls sind mit "GND", "OUT" und "+5V" bezeichnet.

Die Einstellung des PIR-Moduls erfolgt über zwei Potenziometer und einen Jumper (siehe Bild unten). Mit dem linken Potenziometer ("Time") wird die Haltezeit des Moduls nach dem Erkennen einer Wärmequelle eingestellt. Mit dem rechten Potenziometer ("Sensitive" stellt man die Empfindlichkeit und damit auch die Reichweite des Sensors ein. Der Jumper dient der Einstellung der Arbeitsweise: In der unteren Stellung wird bei jeder erkannten Bewegung ein Impuls erzeugt. Wenn also jemand beisielsweise am Sensor vorbei geht, gibt es im Sekundenabstand Impulse, solange sich die Person innerhalb der Reichweite des Sensors befindet. In der oberen Stellung des Jumpers liefert der Sensor nur einen Impuls, abhängig von der Dauer der Haltezeit. Dies ist auch meist der Dfault-Wert.

Programmierung

Für die folgenden Programme wird das PIR-Modul auf "ein Impuls pro Erkennung" (Default) eingestellt. Die Haltezeit stellen Sie auf den minimalen Wert (Potenziometer auf Linksanschlag). Denken Sie auch daran, dass wegen des Zugriffs auf den GPIO-Pin eventuell Root-Rechte nötig sind.

Das erste Programm demonstriert die Bearbeitung der Impulse von PIR-Modul. Dazu muss eine kleine State Machine mit zwei Zuständen programmiert werden, da die Einschaltzeit relativ lang ist. Würde man nur den Zustand des GPIO-Pins abfragen, bekäme man im Ruhezustand immer eine "0" und bei aktivem PIR für längere Zeit dauernd eine "1". Es gilt also:

  1. Nach dem Start warten, bis der PIR-Sensor in Ruhe ist (warten auf "0"). Die Zustandsvariable wird auf "0" gesetzt.
  2. Ist die Zustandsvariable == 0 warten, bis der Sensor eine "1" liefert und dann die Zustandsvariable auf "1" setzen.
  3. Ist die Zustandsvariable == 1 warten, bis der Sensor eine "0" liefert und dann die Zustandsvariable auf "0" setzen.
Das Ganze läuft in einer Endlosschleife ab, wobei bei jedem Schleifendurchlauf eine Pause (mittels time.sllep()) eingelegt wird, damit das Programm nicht zu viel Rechenzeit aufnimmt. Das Python-Programm liefert beim Erkennen der Bewegung nur die Meldung "Bewegung erkannt!". Hier kann man natürlich noch weitere Aktionen programmieren, etwa ein BIld mit der Kamera aufnehmen, das Licht einschalten etc.
#!/usr/bin/python
import RPi.GPIO as GPIO
import time

# BCM GPIO-Referenen verwenden (anstelle der Pin-Nummern)
# und GPIO-Eingang definieren
GPIO.setmode(GPIO.BCM)
GPIO_PIR = 4

print "PIR-Modul gestartet (CTRL-C to exit)"

# Set pin as input
GPIO.setup(GPIO_PIR,GPIO.IN)

# Initialisierung
Read  = 0
State = 0

try:
  print "Warten, bis PIR im Ruhezustand ist ..."

  # Schleife, bis PIR == 0 ist
  while GPIO.input(GPIO_PIR) != 0:
    time.sleep(0.1)
  print "Bereit..."

  # Endlosschleife, Ende mit STRG-C
  while True:
    # PIR-Status lesen
    Read = GPIO.input(GPIO_PIR)

    if Read == 1 and State == 0:
      # PIR wurde getriggert
      print "Bewegung erkannt!"
      # Zustand merken
      State = 1
    elif Read == 0 and State == 1:
      # PIR wieder im Ruhezustand
      print "Bereit..."
      # Zustand merken
      State = 0

    # kleine Pause
    time.sleep(0.1)

except KeyboardInterrupt:
  # Programm beenden
  print "Ende..."
  GPIO.cleanup()

Statt des Wartens in einer Schleife besteht die Möglichkeit, den Daten-Pin so zu initialisieren, dass eine Veränderung, genauer ein Wechsel von "0" auf "1", an das Skript übergeben wird. Dazu wird dann eine Callback-Funktion Motion() zum Auslesen des Pinzutands definiert, die beim Auftreten einer positiven Flanke automatisch aufgerufen wird. Der Vorteil dabei ist, das der Code nicht ständig den Pin-Status überprüft, sondern schläft, bis etwas passiert.

#!/usr/bin/python

import RPi.GPIO as GPIO
import time
import datetime

# BCM GPIO-Referenen verwenden (anstelle der Pin-Nummern)
# und GPIO-Eingang definieren
GPIO.setmode(GPIO.BCM)
GPIO_PIR = 4

print "PIR-Modul gestartet (CTRL-C to exit)"

# Set pin as input
GPIO.setup(GPIO_PIR,GPIO.IN)

# Initialisierung
Read  = 0
State = 0

print "Warten, bis PIR im Ruhezustand ist ..."

# Schleife, bis PIR == 0 ist
while GPIO.input(GPIO_PIR) != 0:
  time.sleep(0.1)
print "Bereit..."

# Callback-Funktion
def MOTION(PIR_GPIO):
  print "%s - Bewegung erkannt!" % datetime.datetime.now()

print "%s - Warten auf Bewegung" % datetime.datetime.now()

try:
  # Ereignis definieren: steigende Flanke
  GPIO.add_event_detect(GPIO_PIR, GPIO.RISING, callback=MOTION)
  # laenger schlafen - Callback wird durch die Flanke aktiviert
  while True:
    time.sleep(60)

except KeyboardInterrupt:
  # Programm beenden
  print "Ende..."
  GPIO.cleanup()
Das Programm ist zudem kürzer als das erste.

Das dritte Programm ist eine C-Implementierung des ersten. Als Bibliothek wird hier wiringPi verwendet. Si könnten aber genausogut die auf der Seite über die GPIO-Programmierung in C vorgestellte Bibliothek oder die die bcm2835-Library verwenden.

/*
 * Program for PIR-Sensor.
 * Connect on board pin number 07 to Sensor
 *
 * gcc -Wall -o pir -l wiringPi pir.c
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <signal.h>
#include <wiringPi.h>

/* Sensor auf GPIO 4, Pin 7, WiringPi 7 */
#define SENSOR 7

unsigned int state = 0;
unsigned int data  = 0;
struct sigaction sig_struct;

/* Signalhandler fuer STRG-C */
void  finish(int sig)
  {
  printf("Signal %d empfangen. Programm wird beendet.\n", sig);
  exit(0);
  }

int main(int argc, char **argv)
  {
  /* Initialisierung der Bibliothek */
  if (wiringPiSetup() == -1)
    {
    printf("Please run this as root\n");
    return EXIT_FAILURE;
    }

  /* Signalhandler fuer STRG-C einrichten */
  sig_struct.sa_handler = finish;
  sigemptyset(&sig_struct.sa_mask);
  sig_struct.sa_flags = 0;
  sigaction(SIGINT,&sig_struct,NULL);

  /* Sensor als Input definieren */
  pinMode(SENSOR, INPUT);

  while((data = digitalRead(SENSOR)) != 0)
    {
    /* Pause 0,1 Sekunde */
    usleep(100000);
    }

  printf("Bereit...\n");
  while (1)
    {
    data = digitalRead(SENSOR);
    if(data == 1 && state == 0)
      {
      printf("Bewegung erkannt - %d\n",(int)time(NULL));
      state = 1;
      }
    else if (data == 0 && state == 1)
      {
      printf("Bereit...\n");
      state = 0;
      }
    /* Pause 0,1 Sekunde */
    usleep(100000);
    }
  return EXIT_SUCCESS;
  }
Auch bei C kann man mit Flankenerkennung arbeiten, siehe oben genannte Webseite.

Links


Copyright © Hochschule München, FK 04, Prof. Jürgen Plate und die Autoren
Letzte Aktualisierung: