![]() |
Raspberry-Pi-Projekte: Auto-ShutdownProf. Jürgen Plate |
Der Microcomputer Raspberry Pi ist so klein und minimalistisch, dass er keinen Ein-/Ausschalter hat. Wie schaltet man ihn dann aus oder führt einen Reset aus, wenn das mal nötig ist?
Das Einschalten des Raspberry Pi erfolgt einfach durch Anschließen an das Netzteil via USB-Kabel. Dann startet der Winzling automatisch und bootet sein Betriebssystem von der SD-Karte. Man kann ihn auch auf umgekehrtem Wege ausschalten: Einfach den Netz- bzw. USB-Stecker ziehen. Das machen die meisten und in den seltensten Fällen führt das zu Problemen -- manchmal aber doch. Ein Schaden an der Hardware des RasPi sind dabei zwar ausgeschlossen, aber die auf der SD-Karte gespeicherten Daten können Schaden nehmen. War das System beim brutalen Stromentzug gerade beim Schreiben auf der SD-Karte, wird das eventuell nicht korrekt abgeschlossen. Wenn es halbwegs gut ging, ist vielleicht nur eine Logdatei beschädigt, wenn das Pech zuschlägt, ist das gesamte Dateisystem kaputt und der kleine bootet nicht mehr. Da hilft dann nur noch eine Neuformatierung der SD-Karte unter komplettem Datenverlust. Das ist – wie gesagt – selten der Fall, aber es kann jederzeit vorkommen.
Um den Raspberry Pi etwas sanfter auszuschalten, wird das System vollständig heruntergefahren, bevor man den Stecker zieht. Dies ist nur per Kommando möglich. Sind am RasPi Bildschirm und Tastatur angeschlossen, gibt es damit keine Probleme: Entweder man klickt in der grafischen Oberfläche den entsprechenden Menüeintrag an oder man verwendet die Kommandozeile, wie auch beim Headless-Betrieb. Kurz und schmerzlos geht es beispielsweise mit
sudo shutdown -h now # Shutdown - Halt sudo shutdown -r now # RebootDas sudo ist nur erforderlich, wenn man nicht als root-Benutzer angemeldet ist, da diese Befehle nur mit Superuser-Rechten ausgeführt werden dürfen. Als Synonyme gibt es auch noch die entsprechend funktionierenden Programme halt und reboot. Zum erneuten Starten des RasPi nach einem Shutdown muss auf jeden Fall die Stromzufuhr kurz unterbrochen werden.
Ist der Raspberry Pi so "headless", dass er nur gelegentlich per SSH-Login administriert wird (etwa bei einer Steuerungs- oder Messwerterfassungs-Aufgabe) und soll er auch lokal ohne extra Login heruntergefahren werden, kann man das auch mittels einer Taste am GPIO-Port erledigen. Dazu ist aber ein Programm notwendig, das die Taste überwacht und daher ständig laufen muss. Das weiter unten beschriebene Programm erlaubt sogar zwei Alternativen, das Herunterfahren oder den Reboot - ja nachdem, wie lange die Taste gedrückt wird. Für diesen Betrieb muss eine Taste am GPIO-Port angeschlossen werden und das Programm bereits beim Hochfahren des Systems starten.

Die Taste kann an jeden beliebigen (freien) GPIO angschlossen werden. Empfehlenswert ist es, "Allerwelts"-GPIOs zu nehmen, also nicht unbedingt die I2C- oder Seriell-Schnittstelle. Als Beispiel verwende ich den GPIO 5, Pin 29. Der Taster soll gegen GND schließen (das erleichtert die Verdrahtung, wenn etliche GPIOs in Betrieb sind) und beim GPIO 5 ist GND gleich daneben auf Pin 30.
Damit das Programm beim Boot-Vorgang automatisch im Hintergrund startet, gibt es zwei Möglichkeiten der Einrichtung:
Bei allen Systeme, insbesondere Raspbian Wheezy und davor, geht das über einen Eintrag in der Datei /etc/rc.local (als root-User). Vor der letzen Zeile mit dem Befehl "exit 0" fügt man den Aufruf des Shutdown-Programms ein und speichert die Änderung ab:
... /home/pi/bin/shutdown.py & ... exit 0Das "&" am Ende der Zeile nicht vergessen! Das Programm soll ja im Hintergrund laufen.
Ab Raspbian "Jessie" kann man das Programm auch als richtigen Dienst einrichten. Dazu erstellt man eine Konfigurations-Datei für den neuen Dienst im Verzeichnis von /lib/systemd/system/ (auch alles wieder als root-User) mit dem Namen "switchoff.service" und folgendem Inhalt:
[Unit] Description=Raspi per Taste herunterfahren After=multi-user.target [Service] Type=idle ExecStart=/home/pi/bin/shutdown.py & [Install] WantedBy=multi-user.targetDie Datei wird dann mittels chmod 644 /lib/systemd/system/switchoff.service ausführbar gemacht. Nun kann man den Dienst eintragen und aktivieren:
sudo systemctl daemon-reload sudo systemctl enable switchoff.service sudo systemctl start switchoff.serviceNach einem späteren Neustart, kann man nachsehen, ob auch alles ordentlich funktioniert:
sudo systemctl status switchoff.serviceMit "disable" kann der Dienst auch wieder abgeschaltet werden. Nun nur noch probieren, ob der Shutdown per Taste funktioniert.
Das Programm erlaubt sowohl das komplette Anhalten des RasPi als auch das Rebooten. Was getan wird, hängt von der Zeitdauer des Tastendrucks ab. Wird die Taste weniger als 3 Sekunden gedrückt gehalten, erfolgt ein Reboot. Bei längerer Betätigung wird der Raspberry heruntergefahren. Zum erneuten Starten des Raspberry Pi muss im Shutdown-Fall die Stromzufuhr kurz unterbrochen werden.
Am Programmanfang werden alle wichtigen Konstanten festgelegt: GPIO-Port und Schaltzeiten. Danach überzeugt sich das Programm, ob der Aufruf auch mit Root-Berechtigung erfolgt und beendet sich gegebenenfalls mit einer Fehlermeldung. Danach wird der GPIO für die Taste initialisiert und auch der interne Pullup-Winderstand eingeschaltet, so dass bei offenem Tastenpin ein definierter Logikpegel anliegt und auch keine Störungen eingefangen werden.
Anschließend wird eine Interrupt-Serviceroutine (ISR) definiert, die auf die fallende Flanke am Tastenpin reagiert (nicht verwirrren lassen: "not GPIO.input(pin)" bedeutet ja gerade, dass die Taste gedrückt ist → negative Logik). Zu Beginn der Flanke wird die Variable für die Zeitmessung, duration, auf 0 gesetzt. Jetzt müssen Sie sich vorstellen, dass ja nach wenigen Mikrosekunden die ISR abgearbeitet ist. Bleibt die Taste gedrückt, ist natürlich die Interruptleitung immer noch aktiv und es passiert erst wieder etwas, wenn die Taste losgelassen wurde. Auch bei der steigenden Flanke wird die ISR wieder angesprungen. Ist duration ungleich 0, wird die Dauer des Tastendrucks berechnet und entsprechend reagiert: War die Taste lange genug betätigt, erfolgt ein Shutdown, bei kürzerer Dauer wird der Pi neu gestartet und eine zu kurze Zeit wird ignoriert.
Im Hauptprogramm wird dann nur noch die ISR aktiviert und in einer Endlosschleife "geschlafen". Der Aufruf von sleep() sorgt auch dafür, dass das Programm nahezu keine aktive Rechenzeit aufnimmt und die anderen Prozesse nicht behindert. Da das Programm als Dienst läuft werden alle Meldungen ins System-Logfile /var/log/syslog geschrieben.
#!/usr/bin/python
# shutdown/reboot Raspberry Pi mittels Taste
import RPi.GPIO as GPIO
import subprocess, time, sys, syslog, os
# GPIO-Port, an dem die Taste gegen GND angeschlossen ist
# GPIO 5, Pin 29 (GND waere daneben auf Pin 30)
PORT = 5
# Schwelle fuer Shutdown (in Sekunden), wird die Taste kuerzer
# gedruckt, erfolgt ein Reboot
T_SHUT = 3
# Entprellzeit fuer die Taste
T_PRELL = 0.05
uid = os.getuid()
if uid > 0:
print ("Programm benoetigt root-Rechte!")
sys.exit(0)
# GPIO initialisieren, BMC-Pinnummer, Pullup-Widerstand
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(PORT, GPIO.IN, pull_up_down=GPIO.PUD_UP)
# Zeitdauer des Tastendrucks
duration = 0
# Interrupt-Routine fuer die Taste
def buttonISR(pin):
global duration
if not (GPIO.input(pin)):
# Taste gedrueckt
if duration == 0:
duration = time.time()
else:
# Taste losgelassen
if duration > 0:
elapsed = (time.time() - duration)
duration = 0
if elapsed >= T_SHUT:
syslog.syslog('Shutdown: System halted');
subprocess.call(['shutdown', '-h', 'now'], shell=False)
elif elapsed >= T_PRELL:
syslog.syslog('Shutdown: System rebooted');
subprocess.call(['shutdown', '-r', 'now'], shell=False)
# Interrupt fuer die Taste einschalten
GPIO.add_event_detect(PORT, GPIO.BOTH, callback=buttonISR)
syslog.syslog('Shutdown.py started');
while True:
try:
time.sleep(300)
except KeyboardInterrupt:
syslog.syslog('Shutdown terminated (Keyboard)');
print ("Bye")
sys.exit(0)

Wird der Rasperry Pi heruntergefahren, schaltet er leider nicht komplett ab, was am Leuchten der Power-LED auf dem Board deutlich wird. Auch eventuell angesteckte USB_Peripherie wird weiter versorgt. Um ihn dann wieder neu zu starten, muss er kurz vom Netzteil getrennt werden - oder man lötet nachträglich zwei Drähtchen für eine Reset-Taste an. Das komplette Abschalten der Stromversorgung und das Starten per Tastendruck soll nun auch automatisiert werden.
Festzustellen, ob der Raspberry Pi vollständig heruntergefahren ist (nur noch Power-LED und die Spannungsversorgung aktiv), ist schwierig. Nach einm Shutdown ist der Rechner leider nicht komplett aus. Um ihn wieder zu starten, muss die Stromversorgung unterbrochen oder ein Reset ausgelöst werden. Ausserdemm verbrauchen der RasPi und eventuelle Peripherie weiterhin Energie. Das vollständige Abschalten müsste also Teil der Shutdown-Sequenz des Raspberry Pi sein und so spät wie möglich erfolgen, am sichersten wäre es erst nach dem Aushängen der Dateisysteme. Da aber seit den letzten Raspbian-Versionen nicht mehr das deterministische SysV-System für das Booten und Herunterfahren zuständig ist, sondern der neue Systemd, kann nicht mehr genau festgelegt werden, wann welches Script oder Programm ausgeführt wird (das ist ja gerade das Konzept des Systemd: vieles möglichst parallel ausführen). Insofern wäre ein Selbst-Abschalten ein klein wenig unsicher.
Eine zweite Überlegung führt daher dahin, das Abschalten per GPIO auszulösen, aber den eigentlichen Abschaltvorgang zeitlich zu verzögern. Das ist eine praktikable Lösung, wenn man die Zeitverzögerung genügend groß macht. Sie wäre auch recht einfach zu realisieren. Nach dem Einschalten muss sich der RasPi selbst aktiv halten und beim Shutdown wird dann nach einer gewissen Wartezeit der Saft abgedreht. Für das Detektieren, ob der Raspberry Pi aktiv oder heruntergefahren ist, bietet sich der serielle Ausgang an (TX, GPIO 14, Pin 8). Kurz nach dem Hochfahren geht er auf HIGH und erst nach dem Shutdown wieder auf LOW. Werden serielle Daten gesendet, wechselt der Pegel im Rhythmus der Daten zwischen HIGH und LOW, was aber nicht weiter stört, da ja sowieso eine Zeitverzögerung notwendig ist. Beim Modell 3 ist das leider nicht mehr ganz so. Da hängt der LOW-Pegel nach dem Herunterfahren leider davon ab, ob die serielle Schnittstelle per Konfiguration aktiviert wurde. Da muss also beim Herunterfahren per Programm nachgeholfen werden - was aber nicht problematisch ist, weil ja sowieso vor dem echten Abschalten etwas gewartet wird. Wichtig ist nur, dass die Selbsthaltung automatisch und ohne irgend eine Software auf dem Pi aktiviert wird.
Die Schaltung ist recht einfach, sie besteht nur aus zwei Transistoren, einem Relais, fünf Stiftleisten und einigen weiteren Bauteilen. Ein Relais anstelle einen P-MOSFET zur An- und Abschaltung wurde gewählt, um im ausgeschalteten Zustand eine wirkliche galvanische Trennung des RasPi vom Netzteil zu haben. Viel wichtiger ist jedoch, dass beim Schalten des Relaus der Strom komplett ein- oder ausgeschaltet wird. Die Stiftleisten haben folgende Aufgaben:
| Stift- leiste | Aufgabe | JP1 | Stromversorgung vom Netzteil (+5 V) | JP2 | Stromversorgung zum Raspberry Pi | JP3 | Einschalt-Taste - die Taste schaltet gegen +5 V. | JP4 | Ausschalt-Taste - die Taste schaltet gegen GND. | JP5 | GPIO-Anschlüsse des RaspPi GPIO 5 (IN) für Shutdown, GPIO 14 (OUT) für Selbsthaltung |
|---|
Für JP1 und JP2 trennt man einfach das Kabel vom Netzeil und verbindet die Netzteil-Seite mit JP1 sowie das Ende mit dem Mikro-USB-Stecker mit JP2. An JP3 und JP4 können beliebige Taster angeschlossen werden. Für den Einschalttaster an JP3 könnte man auch eine Taste mit LED-Beleuchtung verwenden und die LED im Taster anstelle der LED1 anschließen. Er schaltet die 5 V vom Netzteil über die Diode D2 auf die Basis von T2, der darauf hin durchschaltet. Das Relais zieht an und versorgt über seinen Kontakt K1 den Raspberry mit Energie. Gleichzeitig wird der Kondensator C1 aufgeladen. Er sorgt einerseits dafür, dass T2 für etliche Sekunden weiter durchgeschaltet bleibt, auch wenn der Taster losgelassen wird und gibt dem Rechner Zeit zum Booten. Der Kondensator endlädt sich über R3 und die Basis von T2 sowie R4. Bedingt durch die langsame Entladung von C1 sinkt die Spannung an der Basis von T2 ebenfalls langsam ab und beginnt er zu sperren. Würde anstelle des Relais ein P-MOSFET angesteuert, würde die Stromversorgung des RasPi auch nur "schleichend" getrennt, was nicht optimal ist. Wenn das Schalten des Raspberry elektronisch erfolgen soll, müsste man die einfache Schaltung mit T2 durch einen Schmitt-Trigger ersetzen.

JP5 führt zum GPIO-Port des RasPi. Die Ausschalttaste an JP4 ist über Pin 3 der Stiftleiste direkt mit dem GPIO 5 verbunden und triggert das Shutdown-Programm (siehe vorhergehendes Kapitel). Der GPIO 14 an Pin2 der Stiftleiste ist bei laufendem Rechner auf logisch HIGH (ca. 3 V) und steuert über den Widerstand R1 den Transister T1 an, der über D1 die Basis von T2 im durchgeschalteten Zustand hält. Dies ist die Selbsthaltung des RasPi im Normalbetrieb. Gleichzeitig wird auch C1 auf vollem Ladezustand gehalten, so dass Datenverkehr auf der TX-Leitung mit LOW-Impulsen keine Auswirkung auf die Selbsthaltung hat. Erst beim Herunterfahren muss diese Leitung dann automatisch oder manuell auf LOW geschatet werden.
Das Abschalten der TX-Leitung kann irgendwo im Shutdown-Prozess erfolgen, da ja über C1 der RasPi noch etliche Sekunden lang mit Strom versorgt wird. Ist die serielle Schnittstelle konfiguriert (bei den Modellen A, B, B+ und 2B ist das immer der Fall), muss man nichts weiter unternehmen. Beim Modell 3 hängt es von der aktuellen Konfiguration ab, ob TX beim Herunterfahren auf LOW schaltet. Notfalls wird hier nachgeholfen. Erstens wird ein Miniprogramm benötigt, das (mit Root-Rechten) den TX-Pin auf LOW schaltet:
#!/usr/bin/env python # -*- coding: utf-8 -*- # Programm: TX_OFF.py import RPi.GPIO as GPIO TX_LED = 8 GPIO.setmode(GPIO.BOARD) GPIO.setwarnings(False) GPIO.setup(TX_LED, GPIO.OUT) GPIO.output(TX_LED, GPIO.LOW)Es fehlt nun noch die Steuerdatei, die dafür zuständig ist, dass TX_OFF.py bein Herunterfahren gestartet wird. Ab "Jessie" kann auch hier wieder der systemd verwendet werden. Diese Datei habe ich "txoff.service" genannt. Sie wird (mit Root-Berechtigung) im Verzeichnis /lib/systemd/system/ abgelegt:
[Unit] Description=GPIO-Port TX auf LOW setzen DefaultDependencies=no Before=shutdown.target [Service] Type=oneshot ExecStart=/home/pi/bin/TX_OFF.py [Install] WantedBy=reboot.target halt.target poweroff.targetUnter "Unit" werden allgemeine Bedingungen festgelegt, u. a. wann das Script gestartet werden soll (Before=shutdown.target). Unter "Service" steht dann der Aufruf des Scripts mit vollständiger Pfadangabe. Um die Steuerdatei einzubinden, gibt man (als Root) das folgende Kommando ein:
systemctl daemon-reload systemctl enable txoff.serviceMit systemctl disable txoff.service könnte man es wieder deaktivieren. Den Status erfragt man mit der "status"-Option:
root@pi:/home/pi# systemctl status txoff.service * txoff.service - GPIO-Port TX auf LOW setzen Loaded: loaded (/lib/systemd/system/txoff.service; enabled) Active: inactive (dead)Schließlich kann man die Steuerdatei mittels systemctl cat txoff.service auch noch auflisten lassen. Vergessen Sie auch nicht, das Programm mittels chmod +x TX_OFF.py ausführbar zu machen.
Bei den älteren Versionen von Raspbian kann man das entsprechende Halt-Script in der Datei /etc/init.d/halt modifizieren, indem man den Aufruf von TX_OFF.py einfügt. Das folgende Listing zeigt den entsprechenden Ausschnitt des Scripts. Die Einfügestelle ist fett gedruckt:
...
do_stop () {
...
log_action_msg "Will now halt"
/home/pi/bin/TX_OFF.py # <--- hier
halt -d -f $netdown $poweroff $hddown
}
case "$1" in
start)
# No-op
;;
...