Der Einzeiler in der Datei /etc/udev/rules.d/99-usbcopy.rules aus Listing 5 sagt dem Daemon, dass er für neue Geräte, deren Namen mit “sd” anfängt, das Skript /usr/local/sbin/copy_img ausführen soll. Den Gerätenamen (in "$kernel") bekommt das Skript per Parameter. Ein Neustart ist nach der Einrichtung in der Regel nicht nötig.
Listing 5
KERNEL=="sd?", SUBSYSTEMS=="usb", ACTION=="add", RUN+="/usr/local/sbin/copy_img $kernel"
Das Skript selbst finden Sie in Listing 6, reduziert auf das absolut Notwendige. Eine etwas ausgefeiltere Version mit Prüfungen und Logging erhalten Sie über die Projektseite des Autors [3]. Da der Name der als Datenspeicher genutzten Festplatte /dev/sda auch mit “sd” anfängt, filtert das Skript dieses Gerät heraus (Zeile 3). Das eigentliche Kopieren erledigt das Rsync-Kommando in Zeile 7. Interessant sind noch die Zeilen 5 und 9, deren Bedeutung die folgenden Abschnitte klären.
Listing 6
#!/bin/bash
[ "$device" = "sda" ] && exit 0
/usr/local/sbin/hatctl.py AS
mount "/dev/${device}1" /mnt
rsync -a --no-owner --modify-window=1 /mnt/DCIM/*/* "/data"
umount /mnt
/usr/local/sbin/hatctl.py AE
Pibrella
Der Pibrella gehört zur ersten Generation der RasPi-Erweiterungen und darf sich deshalb nicht offiziell als HAT bezeichnen (Abbildung 1). Das etwa 25 Euro teure Board funktioniert aber auch mit dem aktuellen RasPi 3 problemlos. Auf der Platine sitzen drei LEDs (rot, gelb, grün) sowie ein Summer und ein Taster. Über weitere sechs Anschlüsse lassen sich auch höhere Lasten schalten – dazu besitzt die Platine einen eigenen Micro-USB-Anschluss für die Stromversorgung, die dann auch den RasPi speist.
Die Platine wirkt insgesamt solide, es gibt aber einen mechanischen Nachteil: Der Taster sitzt gegenüber der Sockelleiste, sodass jeder Tastendruck eine Hebelwirkung auf die ganzen Platine ausübt. Abhilfe schafft etwas Material zwischen HDMI-Buchse und Pibrella. Bewährt hat sich etwas zusammengerollte dünne Pappe: Sie wirkt wie eine kleine Feder in die Gegenrichtung.
Für das Pibrella-Board hat der Hersteller Pimoroni eine Python-Bibliothek entwickelt, die im Quellcode auf Github [4] zum Herunterladen bereitsteht. Zu deren Installation genügen die Befehle aus Listing 7.
Listing 7
$ sudo apt-get update $ sudo apt-get install python-pip $ sudo pip install pibrella
Letztlich manipuliert diese Bibliothek auch nur die GPIO-Pins des RasPi – man könnte die Platine deshalb mit jeder beliebigen Programmiersprache nutzen. Die Funktionen der Bibliothek vereinfachen jedoch die Handhabung sehr. Die Funktionen umfassen das Ein- und Ausschalten der LEDs, diverse Möglichkeiten des Blinkens, die Reaktion auf den Taster und sogar das Abspielen von Melodien über den Summer. Mit dem Beispielcode auf Github gelingt der Einstieg auch für Programmierer mit wenig Python-Erfahrung.
Statusmaschine
Für unseren Foto-Tank nutzen wir nur einen Teil der verfügbaren Funktionen. Über die LEDs bilden wir den aktuellen Status des Systems ab. Am Anfang bootet das System (rot), danach bleibt es entweder idle (grün) oder führt eine Aktion aus (gelb). Am Ende fährt das System herunter (Halt, wieder rot). Ereignisse lösen den Übergang von einem Status zum anderen aus. Abbildung 2 kürzt diese Ereignisse mit zwei Buchstaben ab, etwa BS für “Boot-Start” und analog AS und AE für “Aktion-Start” und “Aktion-Ende”.
Softwaretechnisch setzt ein eigener Server dieses Konzept um. Listing 8 zeigt diesen HAT-Server. Er fragt in einer Endlosschleife (ab Zeile 88) einen Unix-Socket ab, liest die oben beschriebenen Buchstabencodes und ruft eine entsprechende Funktion auf. So aktiviert die Funktion boot_start() ab Zeile 21 die rote LED und lässt diese blinken. Analog zum Statusschema aus Abbildung 3 löscht die Funktion boot_end() ab Zeile 26 die rot blinkende Leuchtdiode und aktiviert die grüne LED.
Listing 8
#!/usr/bin/python
import sys
import socket
import os, os.path
import time
import pibrella
SOCKFILE = "/run/hat-server"
TIME_ON = 0.2
TIME_OFF = 0.2
FADE_ON = 0.2
FADE_OFF = 0.2
ts_start = 0
def boot_start():
pibrella.light.red.blink(TIME_ON, TIME_OFF)
def boot_end():
pibrella.light.red.off()
pibrella.light.green.on()
def action_start():
pibrella.light.green.off()
pibrella.light.yellow.pulse(FADE_ON, FADE_OFF, TIME_ON, TIME_OFF)
def action_end():
pibrella.light.green.on()
pibrella.light.yellow.off()
def halt_start():
pibrella.light.red.blink(TIME_ON, TIME_OFF)
pibrella.light.green.off()
pibrella.light.yellow.off()
def halt_end():
pibrella.light.red.off()
def btn_pressed(pin):
global ts_start
ts_start = time.time()
print "button pressed ..."
def btn_released(pin):
global ts_start
ts_end = time.time()
print "button released ..."
print "ts_start:", ts_start
print "duration:", ts_end-ts_start
if ts_end - ts_start > 2:
pibrella.buzzer.success()
os.system("halt -p &")
else:
pibrella.buzzer.fail()
ts_start = 0
def run_server():
if os.path.exists(SOCKFILE):
os.remove(SOCKFILE)
print "Opening socket..."
server = socket.socket( socket.AF_UNIX, socket.SOCK_STREAM )
server.bind(SOCKFILE)
server.listen(5)
print "Listening..."
while True:
conn, addr = server.accept()
print 'accepted connection'
while True:
data = conn.recv(2)
if not data:
break
elif data == "BS":
boot_start()
elif data == "BE":
boot_end()
elif data == "AS":
action_start()
elif data == "AE":
action_end()
elif data == "HS":
halt_start()
elif data == "HE":
halt_end()
print "Shutting down..."
server.close()
os.remove(SOCKFILE)
return
pibrella.button.pressed(btn_pressed)
pibrella.button.released(btn_released)
run_server()
Damit auch der Taster funktioniert, registriert der Programmcode beim Start zwei Callbacks für die beiden Events “Taster gedrückt” und “Taster losgelassen” (ab Zeile 114). Nur wenn zwischen beiden Events mindestens zwei Sekunden liegen, löst der HAT-Server einen Shutdown aus (Zeilen 63 bis 74).
Mit dem HAT-Server kommuniziert das Kontrollprogramm hatctl.py aus Listing 9. Das sehr einfach gestrickte Programm wartet nach dem Start, bis der Socket des HAT-Servers verfügbar wird (Zeilen 18 bis 22). Anschließend schreibt es “blind” das erste Argument in den Socket (Zeile 25). Ein hatctl.py AS löst dann das Event “Aktion-Start” aus; der HAT-Server schaltet im entsprechenden Callback die grüne LED aus und aktiviert die langsam blinkende gelbe.







