Moin zusammen,
für das RFID-Masterterminal, das jetzt im Lab hängt, musste ich mit einem Hardwareschalter das Touch-Interface deaktivieren und aktivieren können. Die Bildschirm-Funktionalität sollte dabei natürlich erhalten bleiben. Ich habe das mit einem kleinen Python Skript bewerkstelligt, den Vorgang und das fertige Skript möchte ich hier kurz vorstellen – vielleicht hat der ein oder andere ja noch einen Verbesserungsvorschlag .
Allgemeines
Verwendet wurden in diesem Projekt ein Raspberry Pi 3B mit original 7" Touchscreen und Gehäuse.
Tastatur- und Mauseingaben in Linux werden vom sogenannten X.Org-Server (oder auch Xorg) entgegengenommen und an die jeweils laufende Anwendung weitergereicht. Xorg bietet hierbei die Möglichkeit, einzelne Eingabegeräte gezielt zu (de-)aktivieren.
Zunächst müssen wir also herausfinden, welches Xorg-Eingabegerät dem Touchscreen zugeordnet ist. Hierfür kann man den folgenden Befehl verwenden:
$ xinput --list
Dieser Befehl muss dabei mit Root-Rechten ausgeführt werden.
Ist man mit dem Pi per SSH verbunden, schlägt der Befehl fehl. Man muss hier xinput zuerst mitteilen welcher Nutzer zurzeit eingeloggt ist.
Das geht zum Beispiel so, wobei man pi
durch den Namen des eingeloggten Benutzers ersetzen muss:
$ XAUTHORITY=/home/pi/.Xauthority DISPLAY=:0 xinput --list
Das Ergebnis sollte dann ungefähr so aussehen:
$ XAUTHORITY=/home/pi/.Xauthority DISPLAY=:0 xinput --list
⎡ Virtual core pointer id=2 [master pointer (3)]
⎜ ↳ Virtual core XTEST pointer id=4 [slave pointer (2)]
⎜ ↳ FT5406 memory based driver id=6 [slave pointer (2)]
⎣ Virtual core keyboard id=3 [master keyboard (2)]
↳ Virtual core XTEST keyboard id=5 [slave keyboard (3)]
die hier dargestellten IDs können verwendet werden, um einzelne Geräte zu (de-)aktivieren. Um das richtige Gerät zu finden, kann man die einzelnen IDs mit dem folgenden Befehl durchprobieren:
$ xinput --disable 2
Hierbei ist der Parameter 2 die ID des Geräts. Manche Geräte können mit diesem Befehl nicht deaktiviert werden und geben eine Fehlermeldung zurück. Mit
$ xinput --enable 2
kann man das Gerät wieder aktivieren.
Sobald das richtige Gerät gefunden wurde (in meinem Fall FT5406 memory based driver
mit der ID 6
) kann es mit der Umsetzung weitergehen.
Hardware
Zur Aktivierung des Touchscreens habe ich einen 3-Pin Schiebeschalter verwendet, mit leichten Anpassungen kann man aber auch einen Taster oder 2-Pin Schalter verwenden.
Der Vorteil des 3-Pin Schalters ist, dass kein Pullup-Widerstand verwendet werden muss, bei einem 2-Pin Schalter kann aber auch der interne Pullup des Raspberry Pis verwendet werden.
Hier seht ihr den Aufbau:
Der GPIO-Pin zum Anschluss des Schalters ist natürlich frei wählbar, er muss nur im Programm angepasst werden.
Python-Skript zum Finden der Geräte-ID
Nun zum eigentlichen Skript. Da sich unter Umständen beim Neustart die ID des Geräts ändern kann, kann diese nicht fest einprogrammiert werden. Aus diesem Grund muss man zunächst die aktuelle ID des Geräts herausfinden. Ich verwende hierfür den Namen des Geräts, der bei jedem Neustart identisch bleibt:
import os
getInputsCmd = 'xinput --list'
def getTouchInput():
inputs = os.popen(getInputsCmd).read()
inputList = inputs.split("\n")
for inputLine in inputList:
if "FT5406" in inputLine:
inputIdMatch = re.search('id\=[0-9]+', inputLine)
return inputIdMatch.group(0).split("=")[1]
Diese Funktion liest die Geräte zeilenweise ein und sucht dann nach der Zeile die den Gerätnamen enthält. FT5406
muss hierbei durch einen Namensbestandteil des eigenen, vorher ermittelten Geräts ersetzt werden.
Anschließend durchsucht die Funtion die Zeile nach der ID des jeweiligen Geräts und gibt diese zurück.
Python Skript zum (De-)Aktivieren des Touch-Interfaces
Nun müssen wir nurnoch im Dauerdurchlauf den GPIO-Pin auslesen um auf Änderungen reagieren zu können:
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(4, GPIO.IN)
prev_read = False
prev_value = False
try:
while True:
read_value = GPIO.input(4)
if read_value != prev_value or not prev_read:
inputId = getTouchInput()
prev_read = True
prev_value = read_value
if read_value:
# enable touchscreen
os.system("xinput --disable "+str(inputId))
else:
# disable touchscreen
os.system("xinput --enable "+str(inputId))
time.sleep(0.1)
finally:
GPIO.cleanup()
Um nicht in jedem Schleifendurchlauf erneut einen Systembefehl absetzen zu müssen speichern wir uns in prev_value
den vorigen GPIO-Wert ab. Nur bei einer Änderung wird die Umschaltlogik ausgeführt.
Abhängig von dem aktuellen Wert schaltet das Skript außerdem die Toucheingabe an oder aus.
Um im Fehlerfall den GPIO-Zugriff wieder ordnungsgemäß freizugeben verwenden wir die finally
-Klausel.
Sollte es zu einem Fehler beim einlesen kommen wird das Skript also beendet – ein Problem um das wir uns später kümmern .
Zum Testen können wir das ganze mit
$ python3 enable_touch.py
ausführen, auch hier müssen bei Zugriff per SSH die Variablen wie zuvor gesetzt werden! Ihr solltet jetzt sehen, dass innerhalb einer Sekunde nach Betätigung des Schalters der Touchscreen ein- oder ausgeschaltet wird.
Hier nochmal das fertige Skript (525 Bytes) zum Download.
Einrichtung des Autostart-Services
Wenn also alles geklappt hat wollen wir nun den Pi so einrichten, dass das Skript auch beim Booten automatisch ausgeführt wird. Ich verwende für solche Aufgaben gern das Init-System systemd. Damit können auf einfache Weise Dienste definiert werden, die beim Systemstart laufen sollen. Außerdem kümmert sich systemd um den Neustart im Fehlerfall und viele weitere Details.
Um in systemd einen Dienst anzulegen müssen wir eine Datei erstellen:
# /etc/systemd/system/touch-enable.service
[Unit]
Description=Touchscreen toggle service
After=network.target
StartLimitIntervalSec=0
[Service]
Type=simple
Restart=always
RestartSec=1
User=root
EnvironmentFile=/etc/systemd/system/touch-enable.env
ExecStart=/usr/bin/python3 /home/pi/masterterminal/enable_touch.py
[Install]
WantedBy=multi-user.target
Im [Unit]
Block definieren wir allgemeine Eigenschaften des Services, wie den Namen und eine Beschreibung. Außerdem legen wir fest dass der Service erst nach dem Starten des Netzwerks ausgeführt werden soll.
StartLimitIntervalSec
wird für den Neustartzyklus benötigt.
Der [Service]
Block beschreibt den eigentlichen Service. Mit Restart=always
und RestartSec=1
definieren wir dass nach einem Fehler der Service mit einem Delay von einer Sekunde erneut gestartet werden soll.
User=root
legt den ausführenden Nutzer fest und ExecStart
legt das auszuführende Kommando fest.
EnvironmentFile
zeigt auf eine Datei die Variablen enthält die beim Start des Programms verwendeten Variablen. Da die Ausführung des Services ebenfalls nicht direkt vom eingeloggten Nutzer gestartet wird, müssen wir auch hier die oben aufgeführten Variablen verwenden.
Im [Install]
Block können wir Details zur Verwendung des Services angeben. In diesem Fall also die Eigenschaft, dass der Dienst auch im Mehrnutzerbetrieb ausgeführt werden soll.
Achtet darauf, dass euer Python-Skript jetzt mit Root-Berechtigung ausgeführt wird. Ihr solltet also auf jeden Fall die Dateiberechtigungen anpassen. Wenn Ihr nicht genau wisst wie das geht oder was das bedeutet könnt ihr einfach die Befehle
$ sudo chown pi:pi enable_touch.py
$ sudo chmod 700 enable_touch.py
ausführen, dann könnt nurnoch ihr selbst das Programm lesen, bearbeiten und ausführen (mit Ausnahme von Nutzern mit Root-Rechten).
Jetzt fehlt nur noch die EnvironmentFile:
# /etc/systemd/system/touch-enable.env
XAUTHORITY=/home/pi/.Xauthority
DISPLAY=:0
Zum Testen starten wir den Service mit dem Befehl
$ service touch-enable start
Wenn jetzt alles klappt können wir mit
$ systemctl enable touch-enable.service
auch den Autostart aktivieren, Neustarten und alles in Aktion testen