Im Internet gibt es bereits einige Tutorials, wie man den Raspberry Pi sauber herunterfahren und danach auch wieder hochfahren kann. Diese verwenden jedoch alle zwei getrennte Knöpfe zum Herunterfahren und zum Hochfahren.

Dies ist grundsätzlich einfacher, da beide Vorgänge komplett unterschiedlich funktionieren, jedoch nicht sehr intuitiv. Will man das Board nur zum Entwickeln nutzen, ist diese Variante möglicherweise sinnvoll, bei einem Endprodukt jedoch nicht (z.B. mein Internetradio).

Hoch- und Runterfahren

Das Herunterfahren des Raspberry Pi muss aus dem Betriebssystem heraus durch das shutdown-Kommando erfolgen. D.h. man muss mit einem GPIO-Pin auf eingehende Signale lauschen und prüfen, ob ein Taster gedrückt wurde oder nicht. Sobald der Taster gedrückt wurde, kann man shutdown -h now aufrufen.

Um den Pi anschließend wieder hochzufahren, muss man die RESET-Pins des Raspberry Pi verwenden. Verbindet man diese, führt der Raspberry Pi ein Reset durch. Da er bereits heruntergefahren ist, entspricht dies einem Hochfahren.

Anstatt die beiden Pins zu verbinden, kann man jedoch auch nur den weiter am Rand des Boards liegenden Pin (ich vermute Pin 1) auf 0V ziehen.

Vorbereitung

Standardmäßig existieren für diese beiden Pins jedoch noch keine Steckstifte. Man muss sich selbst eine 2-Pin-Stiftleiste auf die entsprechenden Anschlüsse des P6 löten.

IMG_6080

Hierzu führt man die Stiftleiste erst durch die Löcher hindurch und lötet sie dann von unten her fest.

Schaltung

Wie oben geschrieben, sollen beide Kommandos durch nur einen Knopf möglich sein.

  • Ist der Pi hochgefahren: Herunterfahren
  • Ist der Pi heruntergefahren: Hochfahren

Hierzu wird von irgendwoher ein Signal benötigt, in welchem Zustand sich der Raspberry Pi im Moment befindet. Da ich nicht herausfinden konnte, ob der Pi von sich aus so ein Signal anbietet, habe ich einen GPIO-Ausgang eingeplant. Dieser steht auf HIGH, wenn der Raspberry Pi läuft. Beim Herunterfahren schaltet er dann auf LOW um.

Wie in den Beschreibungen zum Hoch- und Runterfahren erläutert, erfolgt das Herunterfahren durch Signal an einem GPIO-Pin. Das Hochfahren durch Signal am P6-Anschluss. Zusammengefasst gibt es also zwei Pins, die Signale empfangen sollen - einer bei laufendem Pi, einer bei ruhendem Pi.

IMG_6083

Für die Eingabe des Shutdown-Signals habe ich GPIO-Pin 9 gewählt. Die Ausgabe, ob der Pi hoch- oder runtergefahren ist, wird auf GPIO-Pin 10 gesendet.

Der Shutdown-Pin kann jedoch auch im heruntergefahrenen Zustand Signale erhalten - er kann sowieso noch nicht reagieren. Insofern ist nur wichtig, dass RESET nicht ausgeführt wird, wenn der Pi läuft.

Ob RESET ein Signal erhalten soll, kann dann durch einen Transistor gesteuert werden (ich habe den BS170 verwendet).

Raspberry-Pi-Ein-Aus-Schalter

Grundsätzlich liegt bei dieser Schaltung am GPIO-9 über einen Pulldown-Widerstand Ground an. Wird der Taster betätigt, liegt am GPIO-9 HIGH an.

Die beiden Transistoren kümmern sich um die korrekte Beschaltung von RESET. Solange GPIO-10 auf HIGH steht, schaltet der linke Transistor durch. Dadurch liegen im gesamten Bereich unten rechts 0V an. Das Gate des rechten Transistors steht also auch auf 0V und schaltet nicht durch.

Interessant wird es, wenn GPIO-10 auf LOW steht - d.h. der Pi heruntergefahren ist. Dann sperrt der linke Transistor. Solange der Taster nicht gedrückt ist, gelangt über zwei 220kOhm-Widerstände jedoch immer noch GND an das Gate des rechten Transistors. Erst wenn der Taster betätigt wird, überwiegt die Spannung von 3,3V und der rechte Transistor schaltet durch. RESET wird auf GND gezogen und der Pi fährt hoch.

(Wie Ulrich in den Kommentaren korrekt anmerkt, hatte ich hier ursprünglich 10kOhm statt 220kOhm geschrieben. Im ursprünglichen Vorshlag auf mikrocontroller.net wurde wesentlich kleinere Widerstände verwendet.)

Software

Die Ein- und Ausgaben müssen natürlich auch noch in Software gesteuert werden. Dazu habe ich ein kleines Script erstellt, welches beim Booten als Daemon gestartet wird.

Als Programmiersprache verwende ich Python. Im Grunde setze ich beim Starten des Skripts nur GPIO-10 auf HIGH, um anzuzeigen, dass der Pi nun läuft.

Dann warte ich auf Signale am GPIO-9. Wichtig ist hierbei auf die fallende Flanke zu achten, also wenn der Knopf losgelassen wird. Ansonsten fährt der Raspberry Pi beim Herunterdrücken schon runter und setzt GPIO-10 auf LOW. Der Benutzer hält den Knopf noch gedrückt, der RESET-Transistor schaltet durch (da GPIO-10 ja auf LOW steht) und der Pi führt einen Reset durch statt eines sauberen Shutdowns.

Um zusätzlich noch Reset-Fehler durch Bouncing (also mehrmaliges Springen des Signals, wenn man einen Knopf drückt oder loslässt) zu verhindern, warte ich beim Herunterfahren noch eine Sekunde ab, bevor GPIO-10 auf LOW gesetzt wird.

#!/usr/bin/python2

import RPIO
import subprocess
import time

RPIO.setup(9, RPIO.IN)
RPIO.setup(10, RPIO.OUT, initial=RPIO.HIGH)

def shutdown(gpio_id, val):
        time.sleep(1) # wait a while to avoid resetting due to signal bouncing
        RPIO.output(10, False)
        subprocess.call(['shutdown', '-h', 'now'])

RPIO.add_interrupt_callback(9, shutdown, edge='falling')

RPIO.wait_for_interrupts()

Dieses Skript kann man unter ArchlinuxARM mittels systemd automatisch starten. Hierzu muss man sich eine Datei unter /etc/systemd/system erstellen, z.B. /etc/systemd/system/piboot.service.

[Unit]
Description=Control and listen to boot and shutdown

[Service]
ExecStart=/path/to/python/script.py

[Install]
WantedBy=multi-user.target

Unter ExecStart muss man als absoluten Pfad eintragen, wo die obige Python-Datei liegt. Diese Datei muss ausführbar sein (also muss man ggfs. chmod +x ausführen).

Dann kann man den Dienst mittels systemctl start piboot starten und mittels systemctl enable piboot für jeden Start aktivieren.

Anmerkung

Gestern traten bei mir selten Signalsprünge auf, die zum Herunterfahren führten, obwohl ich den Knopf nicht betätigt hatten. Diese lassen sich vermutlich durch einene Kondensator im Schaltkreis oder aber durch eine Zeitprüfung in der Software verhindern.

I do not maintain a comments section. If you have any questions or comments regarding my posts, please do not hesitate to send me an e-mail to blog@stefan-koch.name.