Das Industrial-IO-Subsystem des Kernels standardisiert die softwaretechnische Ankopplung insbesondere von Sensoren. Ungeachtet der Bezeichnung profitieren davon nicht nur die Profis, sondern auch Maker und Hobbyisten.
Linux steuert Mähroboter, Satelliten und Autos. Dazu liest es über Gerätetreiber Sensorwerte ein, arbeitet Regelalgorithmen ab und gibt Parameter an Aktoren aus. Da jeder Sensor- und Aktortyp einen Treiber benötigt, entsteht ein verstreuter Mix an Treiber-Software.
Um mehr Ordnung in die Angelegenheit zu bringen, hob Jonathan Cameron 2009 das Industrial-IO-Subsystem (IIO [1]) aus der Taufe. Damit gab er nicht nur den unzähligen Sensoren und – partiell – auch Aktoren ein wohliges Zuhause im großen Linux-Quellcodebaum, sondern stellte für Anwender und Entwickler ein einheitliches Interface für den professionellen Umgang mit den Daten bereit.
Dazu gehören beispielsweise die Attribuierung mit Zeitstempeln und die Standardisierung von Sensorwerten. Was nutzt den Anwendern ein Temperaturwert, wenn nicht klar ist, ob es sich um Grad Celsius oder Fahrenheit handelt oder ob ein Zeitwert in Sekunden oder Millisekunden vorliegt? Der Name Industrial IO führt allerdings etwas in die Irre, assoziiert er doch eine industrielle Anwendung. Dabei handelt es sich eher um ein professionelles, standardisiertes Sensor-Interface, das in besonderem Maß für Maker oder Hobbyisten interessant und relevant ist.
Geschätzt haben sich mittlerweile rund 300 Sensoren und Aktoren im IIO-Bereich des Quellcodes eingefunden, wo sie sich in Kategorien wie Drucksensoren (pressure), Temperatursensoren (temperature), Beschleunigungsmesser (accel), Entfernungsmesser (proximity), Feuchtigkeitsmesser (humidity), Gesundheit (health) und chemische Sensoren (chemical) tummeln. Zusätzlich gibt es jeweils eine generalisierte Kategorie für Analog-Digital- und Digital-Analog-Wandler. Damit befindet sich der Code für eine große Komponentensammlung bereits im Linux-Archiv und wartet nur darauf, als Modul generiert, geladen und genutzt zu werden.
Prinzipienreiterei
Für den Zugriff auf das Subsystem und auf Daten sowie für das Management derselben setzt IIO auf das ureigenste Unix-Prinzip: Alles ist eine Datei. Eine hierarchische Strukturierung über Verzeichnisse garniert das Ganze. Im Sys-Filesystem findet sich das IIO primär unter »/sys/bus/iio/« (Abbildung 1).
Im Unterverzeichnis »devices/« tauchen nebst anderem alle zur Verfügung stehenden Geräte in Form von weiteren Unterverzeichnissen auf. Das IIO-Framework hält sich an dieser Stelle nicht mit einer ausgefeilten Namensgebung auf, sondern zählt die Geräte einfach durch. Dazu ergänzt es den Namensstamm »iio:device« um eine nachfolgende Nummer. Das erste Gerät wird über das Verzeichnis »iio:device0« repräsentiert, das zweite über »iio:device1« und so weiter.
Für den Zugriff auf das Gerät sammeln sich im zugehörigen Unterverzeichnis – im Folgenden nennen wir es Geräteverzeichnis – eine Fülle von Dateien, deren Namen Auskunft über die dahinter stehende Funktion geben (Abbildung 2). Die Datei »name« enthält die aussagekräftige Bezeichnung des Geräts, etwa in Form der Typenbezeichnung. In der Datei »dev« finden sich die Major- und Minor-Nummer einer zugehörigen Gerätedatei, die wie üblich im Ordner »/dev/« liegt.
Kanalarbeiter
Da moderne Peripheriekomponenten häufig nicht nur einen einzelnen Messwert liefern, führt das IIO-Subsystem das Konzept der Kanäle ein. Ein Beschleunigungsmesser beispielsweise stellt gleich drei Werte bereit, nämlich die Beschleunigung in X-, Y- und Z-Richtung; AD-Wandler verfügen meist nicht nur über einen Analogeingang, sondern häufig über vier oder acht. Jeder Messwert wird durch einen Kanal repräsentiert. Der Beschleunigungsmesser hat also drei Kanäle, der AD-Wandler gleich vier oder acht.
Jeder Kanal wird im Geräteverzeichnis des IIO-Subsystems wiederum über mehrere Dateien repräsentiert. Der Kanalname, zum Beispiel »accel_x«, bildet den Namensstamm einer Datei, die das Framework um ein Präfix und um ein Suffix erweitert. Das Präfix »in_« steht für einen Eingang, das Präfix »out_« für einen Ausgang. Das Suffix »_raw« steht für den unskalierten Rohwert, wie ihn beispielsweise ein AD-Wandler liefert. Weitere Suffixe sind »_offset« und »_scale«. Mithilfe der in den Dateien abgespeicherten Werte lässt sich ein Rohwert in einen gebrauchsfähigen Messwert umrechnen, beispielsweise eine Geschwindigkeit in Meter pro Sekunde. Die simple Formel dazu lautet value=(raw+offset)*scale.
Um einen ersten Eindruck vom IIO-Subsystem zu erhalten, hat Entwickler Cameron ein Dummy-Geräte-Template implementiert, das Ubuntu als fertiges Kernel-Modul mitliefert. Es lässt sich per »modprobe iio_dummy« laden. Damit es ein Dummy-Gerät instanziiert, müssen Sie unterhalb des Ordners »/sys/kernel/config/iio/devices/dummy/« ein Verzeichnis mit dem Namen des betreffenden Geräts anlegen. Rufen Sie also dort »mkdir foo« auf, erscheint unter »/sys/bus/iio/devices/« das neue Verzeichnis »iio:device0«.
Doppelter Punkt
Um auf der Kommandozeile in das neue Verzeichnis zu wechseln, müssen Sie dort den Doppelpunkt durch einen vorangestellten Rückstrich maskieren (»cd iio\:device0«); schließlich dient der Doppelpunkt in der Shell als Metazeichen.
Das neue Dummy-Foo-Gerät bietet eine Fülle von Kanälen an: Es stellt zum Beispiel eine Beschleunigung in X-Richtung zur Verfügung, eine Spannung, zwei Spannungsdifferenzen, einen Zeitstempel, einen Schrittzähler und einen Kanal, der die Aktivitäten des Laufens und Wanderns in Form einer prozentualen Wahrscheinlichkeit signalisiert. Eine Beschreibung dieser und auch der sonstigen im IIO-Subsystem verwendeten Attributdateien findet sich in der Linux-Kernel-Dokumentation [2]. Diese zentrale Dokumentation beschreibt unter anderem die Einheiten, in denen die Messwerte vorliegen (Abbildung 3).
Auf Shell-Ebene lesen Sie einen Wert per Cat-Kommando ein. So offenbart ein »cat« auf die Datei »name«, dass unser Gerät »foo« heißt. »in_voltage0_raw« liefert die Zahl 73, »in_voltage0_offset« 7 und »in_voltage0_scale« 0.00133. Daraus lässt sich nach der bereits vorgestellten Formel ein Spannungswert von (73 + 7) x 0.00133 = 0.10640 berechnen, also 106,4 Millivolt.
Kernel als Macher
Besonders spannend wird das IIO-Framework, wenn es um die automatische Erfassung von Messwerten geht. Dazu bietet es in separaten Kernel-Modulen implementierte Trigger und Buffer. Per »modprobe iio_trig_sysfs« bekommen Sie Zugriff auf die Trigger, »modprobe iio_buffer« stellt die Buffer zur Verfügung. Von Triggern gibt es unterschiedliche Ausprägungen; insbesondere der auf den High-Resolution-Timern basierende »iio_trig_hrtimer« ist sehr nützlich.
Die Idee hinter den Triggern und Buffern besteht darin, dass das IIO-Subsystem Messwerte automatisiert erfasst. Der Anwender definiert die Trigger-Ereignisse, etwa durch Angabe einer Abtastfrequenz, startet den Messvorgang und liest dann entspannt und zeitlich entkoppelt die Daten über eine Gerätedatei ein. Einziger Wermutstropfen dabei: Nicht alle IIO-Geräte unterstützen Trigger und Buffer.
Sowohl die Kanalauswahl als auch das automatisierte Erfassen und das Einlesen durch eine Applikation lassen sich über das Sys-Filesystem parametrieren. Sind die entsprechenden Kernel-Module geladen und unterstützt das IIO-Gerät das automatische Erfassen, finden sich im Sys-Filesystem-Verzeichnis zum Gerät die Unterordner »trigger/« und »buffer/«. Für die Auswahl und das automatisierte Erfassen von Messwerten ist der Trigger zuständig, das Einlesen durch die Applikation konfigurieren Sie über den Buffer.
An dieser Stelle schon einmal ein Hinweis: Auf einem Ubuntu mit dem Standard-Kernel-Modul für das Dummy-IIO-Device existiert keiner der genannten Ordner. Die Kernel-Macher von Canonical haben diese Funktionalität schlichtweg nicht aktiviert, sodass Sie zum Testen selbst Hand anlegen müssen (siehe Kasten “Dummy-IIO-Devices auf dem RasPi”).
Abbildung 4 zeigt die Schritte zum Etablieren der automatisierten Erfassung. Als Erstes instanziieren sie einen Trigger. Dazu legen Sie im Config-Filesystem an dedizierter Stelle (»/sys/kernel/config/iio/triggers/hrtimer/«) ein Unterverzeichnis mit dem Namen des neuen Triggers an (Listing 1).
Listing 1
Neuer Trigger
$ cd /sys/kernel/config/iio/triggers/hrtimer/ $ sudo mkdir foo_periodic
Dummy-IIO-Devices auf dem RasPi
Bedauerlicherweise haben die Ubuntu-Entwickler im Dummy-Treiber die Trigger- und Buffer-Unterstützung ausgelassen, sodass ein Test dieser Funktionen nicht ohne Weiteres gelingt. Vorher müssen Sie die Kernel-Konfiguration anpassen sowie die IIO-Dummy-Module neu generieren und einrichten.
Unter Raspberry Pi OS sieht es nicht viel besser aus. Da sich aber der Raspberry Pi besser für Experimente mit dem IIO-Subsystem eignet – schließlich lassen sich hier kinderleicht eigene Sensoren anschließen – experimentieren wir mit der Himbeere weiter. Dazu holen wir uns die Kernel-Quellen, konfigurieren diese und generieren und installieren den Kernel. Die Schritte zur Kernel-Generierung auf dem Raspberry Pi beschreibt eine eigene Dokumentation unter “Local building” bestens [3].
Bei der Wahl der richtigen RasPi-Version gilt es, Vorsicht walten zu lassen – klassischerweise kommt derzeit noch ein 32-Bit-Kernel zum Einsatz. Nach der Grundkonfiguration, also bei einem RasPi 4 durch Aufruf des Kommandos »make bcm2711_defconfig«, müssen Sie noch über »make menuconfig« die spezifische IIO-Konfiguration vornehmen. Der Kasten “Kernel-Konfigurationsoptionen des IIO-Subsystems” führt dazu die einzustellenden Optionen auf, die zusätzlich Abbildung 5 zeigt.
Mit der neuen Konfiguration lässt sich anschließend der Kernel generieren. Dazu benötigen Sie reichlich Geduld. Auf einem RasPi*4 dauert der Compilerlauf bei Verwendung sämtlicher Prozessorkerne knapp 100 Minuten. Wer weniger Geduld mitbringt oder – insbesondere mit einer etwas betagteren Version des Raspberry Pi – nicht die ganze Nacht Zeit hat, weicht besser auf den Weg des Cross-Generierens [3] aus.
Sämtliche im Artikel verwendeten Befehle fasst Listing 2 noch einmal zusammen. Nach der Installation und einem Neustart steht dem Test der beschriebenen Funktionen mit dem Dummy-Device nichts mehr im Weg.
Kernel-Konfigurationsoptionen des IIO-Subsystems
Device Drivers | Industrial I/O support | Enable IIO configuration via configfs
Device Drivers | Industrial I/O support | Enable software IIO device support
Device Drivers | Industrial I/O support | Enable software triggers device support
Device Drivers | Industrial I/O support | Enable triggered events device support
Device Drivers | Industrial I/O support | IIO dummy driver | An example driver with no hardware requirements
Device Drivers | Industrial I/O support | IIO dummy driver | Event generation support
Device Drivers | Industrial I/O support | IIO dummy driver | Buffered capture support
Device Drivers | Industrial I/O support | Triggers – standalone | High resolution timer trigger
Device Drivers | Industrial I/O support | Triggers – standalone | Generic interrupt trigger
Device Drivers | Industrial I/O support | Triggers – standalone | SYSFS trigger
Listing 2
Test mit dem Dummy-Device
# Kernel-Module laden modprobe iio_dummy modprobe iio_trig_sysfs modprobe iio_trig_hrtimer # # Dummy Device mit Namen foo anlegen cd /sys/kernel/config/iio/devices/dummy/ mkdir foo # # Trigger anlegen und konfigurieren cd /sys/kernel/config/iio/triggers/hrtimer/ mkdir foo_periodic cd /sys/bus/iio/devices/trigger0 cat name echo 2 >sampling_frequency # # Trigger mit Gerät verknüpfen # (nur bei selbstgeneriertem Modul möglich) cd /sys/bus/iio/devices/iio\:device0/trigger/ echo "foo_periodic" >current_trigger # # Einzulesende Kanäle auswählen cd ../scan_elements/ echo 1 >in_timestamp_en echo 1 >in_voltage0_en echo 1 >in_accel_x_en # # Auslesen konfigurieren cd ../buffer echo 10 >length echo 5 >watermark # # Messungen starten echo 1 >enable # # Daten hexadezimal auslesen hd /dev/iio\:device0
Konfiguration mit Bordwerkzeugen
Das unscheinbare »mkdir« bewirkt, dass im Ordner »/sys/bus/iio/devices/« das neue Unterverzeichnis mit dem Namen »trigger0/« aufploppt. Wie die Geräte zählt das IIO-Framework auch die Trigger durch. Hätten wir gleich zwei Trigger angelegt, entstünde ein zweites Verzeichnis mit dem Namen »trigger1/«.
Im Subdirectory »trigger0/« finden sich unter anderem die Dateien »name« und »sampling_frequency«. Die Datei »name« enthält wieder den aussagekräftigen Namen unseres Triggers, über »sampling_frequency« lässt sich die Abtastrate einstellen. In der Vorgabe beträgt sie 100 Hz, zum Ändern der Frequenz schreiben Sie einfach per »echo« die neue Abtastrate in die Datei. Der Trigger ist damit konfiguriert.
Im dritten Schritt verknüpfen Sie Trigger und Gerät. Dazu wechseln Sie wieder ins Geräteverzeichnis und hier in den Unterordner »trigger/«. Den Namen des Triggers, im Beispiel »foo_periodic«, tragen Sie per »echo« in die Datei »current_trigger« ein.
Im nächsten, vierten Schritt wählen Sie die zu überwachenden Kanäle aus. Im IIO-Verzeichnis des Geräts hat sich nach der Zuordnung des Triggers zum Gerät schon wieder ein neues Unterverzeichnis mit dem Namen »scan_elements/« aufgetan. Darin befinden sich diverse Dateien, die die Suffixe »_en«, »_index« und »_type« tragen. »_en« steht dabei für enable. Schreiben Sie eine 1 in die Datei, wird dieser Wert bei einem Triggerereignis erfasst.
Die Dateien mit dem Suffix »_type« sind etwas komplizierter zu interpretieren. Sie geben Auskunft über das Format des abgespeicherten Messwerts (Abbildung 6). Der später über die Gerätedatei auszulesende Messwert »voltage0« beispielsweise verwendet gemäß »in_voltage0_type« (»le:u13/16>>>0«) das Byte-Ablageformat Little Endian (»le«).
Das Byte-Ablageformat gibt vor, in welcher Reihenfolge die Bytes eines 16-, 32- oder 64-Bit-Worts im Speicher liegen und folglich zu interpretieren sind. Im Beispiel handelt es sich um einen vorzeichenlosen Wert (»u«), wobei die unteren »13« Bits des »16«-Bit-Worts relevant sind. Sie gilt es im konkreten Fall noch um »0« Bits nach rechts (»>>>«) zu schieben (Abbildung 7), sprich: Im Beispiel kann das Schieben entfallen.
Nach Auswahl der Kanäle fehlt im Wesentlichen noch die Konfiguration des Zwischenspeichers (Buffer). Tatsächlich lässt sich das IIO-Subsystem so konfigurieren, dass es eine Applikation erst dann aufweckt, wenn eine definierte Anzahl Messwerte erfasst wurden. Dazu spezifizieren Sie zum einen die Größe des Buffers und zum anderen den Füllstand, ab dem die über den Systemcall »read()« schlafende Applikation einen Wakeup-Stupser bekommt. Standardmäßig nimmt der Buffer zwei Datensätze auf. Sobald ein Datensatz vorliegt, wird die Applikation aufgeweckt. Um das System zu entlasten, spezifizieren Sie beispielsweise eine Puffergröße von 20 und lassen die Applikation jeweils nach 15 Datensätzen aufwecken. Sie hat dann 5 Datensätze Zeit, die abgelegten 15 Datensätze auszulesen.
Der Begriff Datensatz verdeutlicht, dass alle ausgewählten Kanäle als ein Datensatz über eine einzelne Gerätedatei ausgelesen werden. Die Reihenfolge der Ablage erschließt sich über den Inhalt der Dateien mit dem Suffix »_index«. Die bereits erwähnte Gerätedatei befindet sich übrigens erwartungsgemäß im Verzeichnis »/dev/« und trägt denselben Namen wie das Verzeichnis im Sys-Filesystem, also beispielsweise »/dev/iio:device0«. Die hexadezimale Ausgabe der Datensätze durch Lesen der Gerätedatei zeigt Abbildung 8.
Tools für Programmierer
Im professionellen Einsatz greift man wohl häufiger über selbst gestrickte Software auf die IIO-Geräte zu als direkt auf Shell-Ebene. Auch dabei helfen die Kernel-Entwickler, indem sie eine passende Bibliothek samt Dokumentation zur Verfügung stellen (Listing 3, erste Zeile). Des Weiteren gibt es einen Reigen von Tools (letzte Zeile), mit denen Sie bequem und ohne lange Pfadeingabe auf die IIO-Geräte zugreifen. Als Gratisbeigabe steckt auch noch ein kleiner Netzwerk-Daemon für den Remote-Zugriff im Paket.
Listing 3
IIO-Bibliothek und Tools
$ sudo apt install libiio0 libiio0-dev libiio0-doc $ sudo apt install libiio-utils
Fazit und Ausblick
Intuitiv und gut unterstützt, so präsentiert sich das IIO-Subsystem dem Anwender. Ob das auch für den Treiberentwickler gilt, der sein Gerät per Industrial IO zugreifbar machen möchte, klären wir in der nächsten Folge der Kern-Technik. (jlu)
Die Autoren
Eva-Katharina Kunst begeistert sich seit den Anfängen von Linux für Open Source. Jürgen Quade ist Professor an der Hochschule Niederrhein und führt auch für Unternehmen Schulungen zu den Themen Treiberprogrammierung und Embedded Linux durch.
Infos
- Industrial IO: https://www.kernel.org/doc/html/latest/driver-api/iio/index.html
- IIO-Attribut-Dateien: https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-bus-iio
- Kernel-Building: https://www.raspberrypi.org/documentation/linux/kernel/building.md













