Sensoren am Raspberry PI: Taster

taster3Über die GPIO-Pins lassen sich viele schöne Sachen mit dem Raspberry PI machen. In diesem Beispiel soll über einen Taster eine LED abwechselnd ein- und wieder ausgeschaltet werden. Dabei versuche ich die gängigsten Fallstricke zu umgehen, die Schaltung möglichst fehlertolerant zu bauen und den Programmcode möglichst effizient zu gestalten.


(Der Nachbau der Schaltung und des Programmes geschehen auf eigene Gefahr)

1) Der Schaltplan

In diesem Beispiel wird ein Taster und eine LED an den Raspberry PI angeschlossen. Durch Drücken des Tasters soll die LED abwechselnd aus- und wieder eingeschaltet werden.

Eine passende Schaltung dazu habe ich hier gefunden.

taster1Als Input wird der GPIO-PIN 23 verwendet. Über einen pull-up Widerstand von 10 kΩ wird der Input an 3.3V angeschlossen und damit auf HIGH gesetzt. Theoretisch kann man auf den pull-up-Widerstand auch verzichten, da der Raspberry PI an den GPIOS eingebaute pull-up-Widerstände hat, die man per Software dazuschalten kann. Das Wort „kann“ ist hier aber der Knackpunkt. Wird das vergessen, so ist der Zustand des Inputs undefiniert und das Verhalten der Software ist zufällig. Mal geht es, mal geht es nicht. Viel Spaß bei der Fehlersuche… Deshalb soll die Schaltung möglichst fehlertolerant sein, so dass ich lieber einen eigenen pull-up-Widerstand verwende. So hat man schon einen Punkt weniger, der bei der Programmierung schiefgehen kann.

taster2 Der Taster verbindet den Pin 23 mit Ground und schaltet ihn damit auf LOW. Und schon kommen wir zu einem weiteren Punkt, der schiefgehen kann (und nach Murphys Gesetzt schiefgehen wird): Konfiguriert man den Pin versehentlich als Output und drückt den Taster, so kann es zu einem Kurzschluss kommen, welcher den Rauchgenerator des Raspberry PI aktiviert. Und bekanntlich geht das nur einmal. Aus diesem Grund ist ein weiterer Widerstand (1kΩ) eingebaut, der den Strom in diesem Fall begrenzt. Die Schaltung ist nun einigermaßen sicher und verzeiht auch Software-Fehler.

An Pin 24 ist zusätzlich noch eine LED angeschlossen. Auch LEDs müssen zwingend über einen Vorwiderstand betrieben werden (Stichwort: Rauchgenerator). In diesem Fall kommt ein 470 Ω Widerstand zum Einsatz.

Damit ist der Hardware-Teil der Schaltung abgeschlossen.

Programmierung

Mit Hilfe eines Python-Programmes soll das oben beschriebene Verhalten umgesetzt werden: Wird der Taster gedrückt geht die LED abwechselnd an und aus. Oft liest man in Büchern und Anleitungen Code wie diesen:

while True:
  input = GPIO.input(17)
  ...
  sleep(100)

Oder auch:

while True:
    GPIO.wait_for_edge(23, GPIO.FALLING)
    ...

In beiden Fällen geschieht das Auslesen des Tasters in der Hauptschleife des Programms. Das hat diverse Nachteile. Zum Einen belastet es den Prozessor unnötig und zum anderen ist das Auslesen des Tasters entweder langsam (nur 10x / Sekunde bei Fall 1) oder man kann im Programm nichts anderes machen, solange man auf den Tastendruck wartet (Fall 2). Beides ist nicht wirklich schön und zum Glück kann man es mit Hilfe von Interrupts besser machen:

toggle_led.py

import RPi.GPIO as GPIO

BUTTON = 23
LED = 24

led_on = False

GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)

GPIO.setup(BUTTON, GPIO.IN)
GPIO.setup(LED, GPIO.OUT)

def toggle_led(channel):         
    global led_on
    led_on = not led_on
    GPIO.output(LED,GPIO.HIGH if led_on else GPIO.LOW)
    print "LED: ", led_on

GPIO.add_event_detect(BUTTON, GPIO.FALLING, callback=toggle_led, bouncetime=200)     

try:
    while True:
        pass

except KeyboardInterrupt:
    print "Ctrl-C - quit"

finally:
    GPIO.cleanup() 

Die Erklärung im Einzelnen:
Zunächst die GPIO-Library importieren:

import RPi.GPIO as GPIO

Nun werden die Pins für Button und LED in Konstanten gespeichert:

BUTTON = 23
LED = 24

In dieser Variablen wird der aktuelle Zustand der LED gespeichert:

led_on = False

Es gibt verschiedene Beschriftungen für die Pins. In diesem Fall wird die BCM-Beschriftung benutzt. Außerdem werden Warnungen unterdrückt, falls die GPIO-Schnittstelle zuvor nicht richtig beendet wurde:

GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)

Nun werden die GPIO-Pins auf Input (für den Taster) und Output (für die LED) gesetzt:

GPIO.setup(BUTTON, GPIO.IN)
GPIO.setup(LED, GPIO.OUT)

Hier wird die Callback-Funktion definiert, die durch den Interrupt gerufen werden soll. Diese greift auf die globale Variable „led_on“ zu. Bei jedem Aufruf wird der Zustand der LED negiert und entsprechend geschaltet. Außerdem erfolgt eine Ausgabe auf stdout

def toggle_led(channel):         
    global led_on
    led_on = not led_on
    GPIO.output(LED,GPIO.HIGH if led_on else GPIO.LOW)
    print "LED: ", led_on

Hier wird der Interrupt definiert und die Callback-Funktion gesetzt. Ein Bouncen des Tasters wird über „bounctime=200“ verhindert.

GPIO.add_event_detect(BUTTON, GPIO.FALLING, callback=toggle_led, bouncetime=200)     

In der Hauptschleife passiert in diesem Fall gar nichts („pass“). Hier wäre Platz für jeden beliebigen Code. Die Abfrage der Taste erfolgt unabhängig davon und wird durch den Interrupt sofort ausgelöst. Um die GPIO-Schnittstelle vernünftig zu beenden, wird „Ctrl-C“ hier gefangen und auf jeden Fall vor Beendigung des Programmes ein GPIO.cleanup() ausgeführt.

try:
    while True:
        pass

except KeyboardInterrupt:
    print "Ctrl-C - quit"

finally:
    GPIO.cleanup() 

Das ganze nun starten mit:

sudo python toggle_led.py

Und schon sollte beim Drücken des Tasters die LED an- und ausgehen. Außerdem sollte in der Konsole so etwas zu sehen sein:

pi@raspberry ~/toggle_led $ sudo python toggle_led.py 
LED:  True
LED:  False
LED:  True
LED:  False
LED:  True
Ctrl-C - quit

9 Gedanken zu „Sensoren am Raspberry PI: Taster

  1. Tom

    Tolles Tutorial, das habe ich so nachgebaut als Einstieg in kleinere Schaltungen mit dem Raspi.
    Vielen Dank dafür.

    Antworten
    1. martin Beitragsautor

      Auf dem Schaltplan ist sie richtig. Eigentlich müsste sie dann auf dem Bild auch richtig herum sein, sonst hat(te) Fritzing nen Bug, als ich den Beitrag erstellt habe.

      Antworten
  2. samke

    Hi, tolles TuT :-).
    Eine Frage dazu, ich möchte mittels Taster kein Gpio steuern, ich möchte ein Udp Paket senden (Taster „ein“ soll per Udp „Taster=1“ senden).
    Wo, bzw. wie müsste ich das Script abändern? Danke für einen Tipp!

    Antworten
    1. martin Beitragsautor

      Moin,

      klar geht das.

      Einfach die Callback-Funktion von toggle_led auf send_udp ändern:
      aus
      GPIO.add_event_detect(BUTTON, GPIO.FALLING, callback=toggle_led, bouncetime=200)
      wird
      GPIO.add_event_detect(BUTTON, GPIO.FALLING, callback=send_udp, bouncetime=200)

      Und dann eine Funktion send_udp schreiben:

      def send_udp(channel):         
          UDP_IP = "192.168.xxx.xxx"
          UDP_PORT = 7777
          MESSAGE = "Taster=1"
          sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 
          sock.sendto(MESSAGE, (UDP_IP, UDP_PORT))
      

      Das Paket socket muss importiert werden. Hier ist ein Beispiel:
      https://wiki.python.org/moin/UdpCommunication
      (ich hab das selbst nicht getestet, sieht aber gut aus)

      Antworten
  3. samke

    Super, Danke für die Info – klappt 1a.
    Jetzt muss ich die ganze Funktion nur noch irgendwie in den Autostart bekommen.

    Antworten
  4. Tato

    Hi, schön erklärt, Eine frage zu dem Thema, ist es auch möglich eine LED folgendermaßen zusteuern? – Taster S1 schaltet die LED an
    – Taster S2 schaltet die LED aus

    Antworten
    1. martin Beitragsautor

      Moin Tato,

      klar ist das möglich. Der Status der LED wird ja in der variablen led_on gespeichert. Anstatt der Funktion toggle_led kann man natürlich auch je auch eine Funktion switch_led(on) schreiben und dann kannst Du mit switch_led(True) oder (False) die LED an- oder ausschalten. Entweder durch einen 2. Taster, oder irgendeinen Sensor oder wie auch immer Du möchtest. Der Phantasie sind da keine Grenzen gesetzt.

      Antworten

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.