Schreiben Sie mir kurz, worum es geht. Ich antworte per E-Mail.
Einen Projektor einzuschalten ist einfach. Eine kleine Installation angenehm bedienbar zu machen, braucht etwas mehr Überlegung. Mein gewünschter Ablauf: einschalten, Bild und Ton in Schleife laufen lassen und zum Schluss genau eine gut erkennbare Taste drücken.
Daraus wurde ein Aufbau aus Raspberry Pi, Acer-Beamer und einem Konferenzsouvenir mit neuer Aufgabe: meinem DEF CON 29 Badge. Eine seiner beleuchteten Tasten dient als Knopf zum Herunterfahren. Rot bedeutet hier: „Pi stoppen.“

Auf dem Pi läuft 64-Bit-Debian (aarch64) ohne Desktop-Umgebung. Einen Bildschirm hat er trotzdem: Über HDMI geht das Bild an den Projektor. Das Badge ist per USB angeschlossen und bildet die Mensch-Maschine-Schnittstelle, kurz HMI.
Die Aufgaben sind überschaubar:
Das Badge steuert den Pi, nicht die Stromversorgung des Beamers. Der Projektor wird weiterhin über seine eigenen Bedienelemente ausgeschaltet.
Die Beispiele verwenden ein Benutzerkonto namens video. Die Dateien movie.mov und audio.wav liegen unter /home/video/video/; die Pfade müssen zum eigenen Aufbau passen. Auf dem Pi werden diese Werkzeuge benötigt:
sudo apt install mpv evtest cron
Zunächst sollte ein einzelnes Video von der lokalen Konsole des Pi aus laufen. Das folgende Skript wählt mit --vo=gpu --gpu-context=drm das DRM/EGL-Backend von mpv. Dafür müssen der installierte Build, der Grafiktreiber und die Geräteberechtigungen des Wiedergabebenutzers passen. mpv --gpu-context=help und mpv --audio-device=help zeigen die verfügbaren Optionen.
Dieses Skript wird als /home/video/video.sh gespeichert:
#!/usr/bin/env bash
set -euo pipefail
media_dir=/home/video/video
movie="$media_dir/movie.mov"
mpv --no-video --input-terminal=no --loop-file=inf \
"$media_dir/audio.wav" &
audio_pid=$!
trap 'kill "$audio_pid" 2>/dev/null || true' EXIT
mpv --vo=gpu --gpu-context=drm \
--no-audio --input-terminal=no --loop-playlist=inf \
'--{' --video-pan-x=-0.7 "$movie" '--}' \
'--{' --video-pan-x=0.7 "$movie" '--}'
Der Ton läuft im Hintergrund. Der Exit-Trap beendet ihn, sobald der Videoplayer endet. Beide Prozesse wiederholen ihre Dateien unabhängig voneinander; das passt zu einer separaten Hintergrundtonspur. Muss der Ton synchron zum Bild bleiben, sollte derselbe mpv-Prozess auch die Audiospur des Videos abspielen.
Die Argumente --{ und --} begrenzen dateispezifische Optionen. Derselbe Film läuft erst an der einen Position, dann an der anderen; anschließend beginnt die Playlist erneut. Die Position wechselt zwischen den Durchläufen, nicht durch eine fließende Schwenkbewegung. Die Werte beziehen sich auf die skalierte Videogröße. 0.7 ist ein großer Versatz und kann erhebliche Bildteile aus dem sichtbaren Bereich schieben. Den Wert deshalb an der tatsächlichen Projektion einstellen. Details zu Wiedergabe und Ausgabebackends stehen im mpv-Handbuch.
Für diesen Aufbau muss das Badge als USB-Tastatur ein S-Tastenereignis senden. Die dafür vorgesehene Taste leuchtet rot. Die Beleuchtung hilft beim Finden; Linux reagiert auf das Tastaturereignis.
Ich habe die Taste über das eingebaute serielle Menü des Badge konfiguriert. Eine eigene Firmware war dafür nicht nötig. Das Badge mit einem USB-C-Datenkabel anschließen, seine serielle Schnittstelle in einem Terminal öffnen und im Menü die rote Taste so belegen, dass sie S sendet. Das serielle Menü dient der Konfiguration; der spätere Tastendruck erreicht den Pi über die USB-Tastaturschnittstelle. Wer tiefer einsteigen möchte, findet Firmware- und Hardwarequellen im Badge-Repository von MK Factor.
Bevor die Taste das Herunterfahren auslösen darf, lässt sich ihre Ausgabe prüfen:
ls -l /dev/input/by-id/
sudo evtest
In der Liste das Tastaturgerät des Badge auswählen und die gewünschte Taste drücken. Gesucht ist ein Ereignis mit diesem Inhalt:
(EV_KEY), code 31 (KEY_S), value 1
1 bedeutet Tastendruck, 0 Loslassen und 2 automatische Wiederholung. KEY_S bezeichnet eine Taste, keinen bereits übersetzten Großbuchstaben; Umschalt ist dafür nicht erforderlich. Die Bedeutung dieser Werte beschreibt das Linux-Eingabeprotokoll.
Für das Skript den passenden Symlink /dev/input/by-id/...-event-kbd verwenden. Ein Name wie /dev/input/event0 kann nach einem Neustart zu einem anderen Gerät gehören. Hat das Badge mehrere Eingabeschnittstellen, ist diejenige richtig, bei der der Test tatsächlich KEY_S meldet.
Das folgende Skript als shutdown-on-s.sh speichern. Es verlangt den Gerätepfad als Argument, wartet auf dessen Erscheinen und reserviert die Tastaturschnittstelle für die Dauer seiner Laufzeit.
#!/usr/bin/env bash
set -euo pipefail
badge_device=${1:?Pass the badge keyboard path from /dev/input/by-id/}
while [[ ! -e "$badge_device" ]]; do
sleep 1
done
LC_ALL=C stdbuf -oL evtest --grab "$badge_device" |
awk '
!requested && /\(EV_KEY\)/ && /\(KEY_S\)/ && /value 1$/ {
requested = (system("systemctl poweroff") == 0)
if (!requested)
print "Power-off request failed" > "/dev/stderr"
}
'
evtest --grab verhindert, dass die Eingaben des Badge zusätzlich in einer Konsole oder anderen Anwendung landen. stdbuf stellt eine zeilenweise Ausgabe sicher. Nach einer erfolgreichen Anforderung verhindert requested weitere Aufrufe. Schlägt die Anforderung fehl, erscheint eine Fehlermeldung; ein erneuter Tastendruck kann es noch einmal versuchen.
Der Listener läuft hier als root, damit er das Eingabegerät lesen und ohne interaktive Passwortabfrage herunterfahren kann. Das Skript wird deshalb mit root als Eigentümer installiert:
sudo install -o root -g root -m 0755 shutdown-on-s.sh /usr/local/sbin/shutdown-on-s
Für einen ersten Test in einer temporären Kopie system("systemctl poweroff") durch system("printf 'S pressed\\n'") ersetzen. Damit lässt sich der gesamte Eingabeweg prüfen, ohne den Pi auszuschalten. Der eigentliche Befehl systemctl poweroff fordert systemd zum Herunterfahren des Betriebssystems auf; den Projektor schaltet er nicht aus.
Der ursprüngliche Aufbau verwendete @reboot-Einträge in cron. Die Wiedergabe bleibt beim Benutzer video, der Listener läuft unter root.
In die Crontab des Benutzers video eintragen (crontab -e, als dieser Benutzer angemeldet):
@reboot /bin/bash /home/video/video.sh >>/home/video/video.log 2>&1
In die Crontab von root (sudo crontab -e) kommt der folgende Eintrag. usb-YOUR_BADGE-event-kbd muss durch den zuvor ermittelten Symlink ersetzt werden:
@reboot /usr/local/sbin/shutdown-on-s /dev/input/by-id/usb-YOUR_BADGE-event-kbd >>/var/log/projector-shutdown.log 2>&1
@reboot wird beim Start von cron ausgeführt. Es wartet weder auf Grafik und Audio noch erzeugt es eine Desktop-Sitzung oder vergibt Geräteberechtigungen. Deshalb zunächst die Wiedergabe unter dem vorgesehenen Benutzer prüfen und nach einem Kaltstart beide Logs lesen. Benötigt das Ausgabebackend eine aktive Konsolensitzung, stellt ein Cron-Eintrag allein diese nicht bereit.
Für einen längeren Einsatz würde ich Wiedergabe und Listener in separate systemd-Dienste überführen, mit automatischen Neustarts und passender Gerätekonfiguration. Cron startet einen Prozess einmal. Einen ausgefallenen Player oder einen nach dem Abziehen des Badge beendeten Listener startet es nicht erneut.
Nützlich wären außerdem ein kurzes Ausblenden des Tons vor dem Herunterfahren und eine LED, die den Empfang der Anforderung bestätigt. Das wären zusätzliche Funktionen: Die rote Taste kennzeichnet bisher die Bedienung und meldet nicht den Zustand des Betriebssystems.
Am meisten gefällt mir an dem Aufbau die Bedienung. Das Konferenzbadge bekommt eine neue Aufgabe, und das Ende der Show wird zu einem Handgriff: die rote Taste drücken und den Pi geordnet herunterfahren lassen.