Einbindung von Sensoren und Aktoren mit Industrial-IO in Linux

Autor / Redakteur: Andreas Klinger * / Johann Wiesböck

Für das Industrial-Input-Output-Subsystem IIO im Linux-Kernel gibt es mittlerweile an die 250 Treiber für Sensoren und Aktoren. Wie Sie eine solche Applikation schnell und richtig realisieren, zeigt der Autor am Beispiel einer Bienenwaage.

Firma zum Thema

In dem für dieses Thema gewählten Projekt soll das Gewicht von Bienenstöcken gemessen werden. Dafür gibt es mehrere Use-Cases.
In dem für dieses Thema gewählten Projekt soll das Gewicht von Bienenstöcken gemessen werden. Dafür gibt es mehrere Use-Cases.
(Bild: Pixabay)

Seit 2011 gibt es im Linux-Kernel das Industrial-Input-Output-Subsystem, kurz IIO. Inzwischen wurden von der recht eifrigen Community rund um die Mailingliste linux-iio beinahe 248 IIO-Treiber (stable v4.13, ohne Variationen und Staging) mainline gebracht. Beispiele sind A/D- und D/A-Wandler, Beschleunigungssensoren, Licht-, Feuchte-, Luftdruck-, Temperaturmessung usw.

Was sind die Besonderheiten von IIO-Treibern und wie kann ich diese in meinem Projekt verwenden? Genau davon handelt dieser Artikel. Damit es nicht zu theoretisch sondern anschaulich wird, wurde ein Anschauungsobjekt in etwas vereinfachter Darstellung gewählt.

Bildergalerie

Wie funktioniert Industrial-IO in Linux?

Das Industrial-IO-Subsystem im Linux-Kernel dient dazu, den Zugriff auf Sensoren und Aktoren zu vereinheitlichen. Bei klassischen Character Devices wird für jedes Gerät ein eigener Treiber mit seinen eigenen Funktionen und seiner oft recht individuellen Schnittstelle zum Userspace (z.B. ioctl(), sysfs-Attribute, Verwendung von Timern) implementiert.

Dies bedeutet oftmals, dass der Zugriff auf unterschiedliche Treiber eigens implementiert werden muss. Selbst bei Treibern der gleichen Art, wie beispielsweise Temperatursensoren ist es unwahrscheinlich, dass zwei Treiber das gleiche Interface anbieten.

Wird nun ein Sensor durch einen anderen ersetzt ist die Anwendung an den neuen Treiber anzupassen. Hier setzt Industrial-IO an mit dem Ziel sowohl die Schnittstelle für Treiber, welche Daten liefern als auch für die Verwendung der Daten im Userspace zu standardisieren.

Ergänzendes zum Thema
Embedded-Linux-Woche

Von Montag bis Freitag, 14. bis 18. Mai sowie 8. bis 12. Oktober 2018, finden die nächsten beiden Embedded-Linux-Wochen in Würzburg statt. Unser erfahrener Referent und Autor des Artikels zu „Sensoren und Aktoren mit Industrial-IO in Linux“ Andreas Klinger ist natürlich auch wieder dabei – wie schon seit Beginn der Linux-Wochen.

Die Embedded-Linux-Woche bietet verschiedene Hands-on-Kurse für Entwickler von Embedded-Systemen an. Im Programm stehen Kursmodule zu den Themen Grundlagen, Embedded Linux, Systemprogrammierung, Yocto, Realtime, Gerätetreiber, Security und Qt.

Alle Teilnehmer erhalten eine externe Festplatte mit geprüftem Linux-Image und ein industrietaugliches Board von Phytec mit aktuellem, echtzeitfähigem Linux-Kernel. Die Seminare können sowohl einzeln als auch als Woche (fünf Tage) gebucht werden: www.linux4embedded.de.

Die Bienenwaage – als Anwendungsbeispiel

In dem für dieses Thema gewählten Projekt soll das Gewicht von Bienenstöcken gemessen werden. Dafür gibt es mehrere Use-Cases. Die Masse wird erfasst, um folgende Fragen zu klären:

  • Messung des Futterverbrauches (Honig) bei der Überwinterung mit Erkennung von hungernden Völkern,
  • Verfolgung des Nektareintrages und damit des angesammelten Honigs, um ihn zu schleudern,
  • Erkennung von Schwärmen als natürliche Volksteilung mit der Möglichkeit diese rechtzeitig einzufangen und ein neues Volk zu gründen.

Auch Umweltdaten werden am Bienenstand aus folgenden Gründen erfasst:

  • Eine Umgebungstemperatur über 12°C führt zu nennenswerter Flugaktivität.
  • Bei Abfall des Luftdrucks werden die Bienen oftmals stechlustiger.

Die Skizze in Bild 1 erläutert den Informationsfluss von der Wägezelle, auf welcher der Bienenstock steht, über den A/D-Wandler (hx711) bis zum IIO-Sensor im Linux-Kernel, angebunden mittels GPIO-Bitbanging.

Temperatur, Luftdruck und Luftfeuchte können mit dem Sensor BME280 gemessen werden, wie in Bild 2 dargestellt. Dieser wiederum lässt sich mittels I²C oder SPI ansprechen und durch einen IIO-Sensor auswerten.

Verfügbare Hardwareschnittstellen

Direkt durch das Framework unterstützt werden I²C, SPI und GPIO. Für diese Schnittstellen ist die Implementierung von Treibern besonders einfach, da eine ganze Reihe an Hilfsfunktionen zur Verfügung steht. Andere Hardwareschnittstellen sind nicht ausgeschlossen und können ebenso genutzt werden. Gegebenenfalls ist etwas mehr an Implementierung vonnöten.

Ganz ohne Telegrammverkehr kann der Datenaustausch auch mittels DMA erfolgen. Hierbei werden die Daten vom Gerät direkt in einen Speicherbereich geschrieben, ohne dass ein expliziter Telegrammverkehr notwendig ist. Die Benachrichtigung über geschriebene Daten erfolgt dann mit einem Interrupt. Die Beziehungen sowohl nach unten zur Hardware, als auch nach oben zum Userspace sind in Bild 3 beispielhaft dargestellt.

Die Schnittstellen zum Userspace

Aus dem Userspace heraus können die Sensoren über ein vereinheitlichtes Interface abgefragt und eingestellt werden. Dazu existiert ein eigenes Bussystem namens iio. Dementsprechend sind die registrierten Devices in Unterverzeichnissen von /sys/bus/iio/devices auffindbar.

In Bild 4 ist ein Beispiel für einen eingelesenen Spannungswert eines A/D-Wandlers zu sehen. Die einzelnen Namensbestandteile werden systematisch zusammengebaut und sind daher gut durch einen Algorithmus verwendbar. Mit dem Tool lsiio aus dem Verzeichnis tools/iio/ der Kernelsourcen kann abgefragt werden, welche IIO-Devices erkannt wurden und welche Nummer diese bekommen haben:

root@waage: lsiio -v

Device 000: bme280
  in_temp_input
  in_humidityrelative_input
  in_pressure_input

Device 001: hx711
  in_voltage0_raw
  in_voltage1_raw

In diesem Beispiel existieren zwei IIO-Devices. Das IIO-Device 000 implementiert den Temperatur-, Luftdruck- und Luftfeuchtesensor BME280 mit seinen drei Messgrößen. Der A/D-Wandler hx711 liefert den eingelesenen Rohwert beider verfügbarer Kanäle jeweils als Spannungswert. Die Umrechnung in das Gewicht erfolgt durch die Userspace-Anwendung.

Triggerung durch hrtimer-Event

Das IIO-Framework bietet Unterstützung für das Einlesen von Sensordaten beim Auftreten eines Ereignisses, wie zum Beispiel eines Interrupts. Die dann eingelesenen Werte können in einen Speicherbereich eingestellt und asynchron mit einem Device-Node abgefragt werden.

Die am häufigsten eingesetzte Triggerung ist der hrtimer-Event. Hierbei wird der Hardware-Timer durch das hrtimer-Framework auf einen Zeitpunkt in der Zukunft programmiert. Tritt der Zeitpunkt ein, liefert der Hardware-Timer einen Interrupt, welcher als hrtimer-Event zur Verfügung steht. Eine Callback-Funktion im Industrial-IO-Treiber wird aufgerufen und der Sensor abgefragt.

Die Daten können in einen Puffer eingestellt werden. Dieser muss im Treiber eingerichtet werden und dient der Zwischenspeicherung der Daten, bis diese mithilfe des Device-Nodes (/dev/iio:device<N>) abgefragt werden. Das IIO-Framework kümmert sich darum, dass der Timer erneut programmiert wird. Dadurch entsteht ein zyklischer Timer mit konstanter Abtastfrequenz.

Abfrage der gepufferten Daten

Für die Abfrage der Daten existiert im Verzeichnis tools/iio der Linux-Sourcen ein Hilfsprogramm namens iio_generic_buffer. Diesem wird mitgeteilt, von welchem Device mit welchem Trigger welche Daten wie häufig abgefragt werden. Nachfolgendes Beispiel zeigt, wie man hrtimer-Events aus dem Userspace heraus verwendet:

root@waage: cd /sys/kernel/config/iio/triggers/hrtimer
root@waage: mkdir mytmr

root@waage: cd /sys/bus/iio/devices/trigger0
root@waage: echo 1 > sampling_frequency

root@waage: cd /sys/bus/iio/devices/iio_device1/scan_elements
root@waage: echo 1 > in_voltage0_en
root@waage: echo 1 > in_timestamp_en

root@waage: iio_generic_buffer -c 3 -N 1 -T 0

iio device number being used is 1
iio trigger number being used is 0
/sys/bus/iio/devices/iio:device1 mytmr
10.062591 1511821230327326320
11.024664 1511821231327324360
10.025594 1511821232327323800

Zunächst wird der hrtimer-Trigger generiert. Dies erfolgt im configfs durch Anlegen eines neuen Verzeichnisses. Als Name dafür wurde im Beispiel mytmr gewählt. Handelt es sich um den ersten angelegten Trigger, dann hat dieser die Nummer 0.

Im sysfs ist ein neues Device-Verzeichnis mit dem Namen trigger<T> entstanden, wobei T die Nummer des Triggers ist. In diesem Verzeichnis kann der Trigger parametrisiert werden. In obigem Beispiel wird die Frequenz auf 1 Hz eingestellt. Im Verzeichnis scan_elements können die einzelnen Kanäle sowie der Timestamp aktiviert werden.

Die Datenabfrage erfolgt mit dem Programm iio_generic_buffer. Es werden die voreingestellten Kanäle 3-mal (-c 3) vom Device mit der Nummer 1 (-N 1) unter Verwendung des Triggers mit der Nummer 0 (-T 0) abgefragt. Angezeigt werden in obigem Beispiel der Rohwert multipliziert mit dem Skalierungsfaktor (aus Datei involtagescale) in der ersten Spalte und der Timestamp in Nanosekunden in der zweiten Spalte.

Datenabfrage im Userspace

Sollen die durch einen Trigger generierten und gepufferten Daten durch eine Userspace-Anwendung abgefragt werden, kann dies durch Auslesen des Device-Nodes (/dev/iio:device<N>) erfolgen, wobei <N> wieder die Nummer des Devices ist.

Diese Daten stehen in dem Device-Node binär drinnen und können mithilfe eines kleinen C-Programmes in ASCII gewandelt und ausgegeben werden. Wie die Daten binär zu interpretieren sind, ist im Verzeichnis scanelements dokumentiert. Beispiel:

root@waage: cd /sys/bus/iio/devices/iio:device1/scan_elements

root@waage: cat in_voltage0_type
le:u24/32>>0

root@waage: cat in_voltage0_index
0

So besagt dieses Beispiel, dass der Spannungswert des Kanales 0 little-endian-codiert, unsigned 24 Bit groß ist und in einem 32-Bit-Feld ohne Bit-Shifting drinnen steht. Der Wert wird als erstes geliefert (in_voltage0_index == 0). Mit diesen Informationen ist es ein leichtes ein eigenes Programm zu schreiben, welches die gepufferten Daten ausliest und korrekt interpretiert.

Userspace-Libraries

Das IIO-Subsystem ist, wie oben ausgeführt, sehr systematisch nach bekannten und dokumentierten Regeln aufgebaut. Dass dies so bleibt, darüber wachen die Reviewer und der Maintainer. Bevor neue Definitionen verwendet werden, wird versucht, neue Treiber in die bestehenden Interfaces zu integrieren.

Am Framework angemeldete IIO-Geräte werden in der Reihenfolge der Anmeldung durchnummeriert. Dies erfordert vom Userspace-Entwickler, dass er den Zusammenhang zwischen Device-Nummer und dem gewünschten Treiber herstellen kann. Er muss die Devices im sysfs durchlaufen und anhand des Attributes name den betreffenden Treiber erkennen. Analog dazu funktioniert die Erkennung von Trigger-Events.

Dies ist mühsame Routine und kann aufgrund der Systematik von IIO gut auf eine Library ausgelagert werden. Die libiio ist genau dafür erschaffen worden. Mit ihr kann gut automatisiert werden:

  • Verbindung zu lokalem oder entferntem IIO-Subsystem, auf welchem der iiod-Dämon läuft
  • Abfrage der vorhanden Devices
  • Kanäle und Attribute durchiterieren
  • Lesen und Schreiben von Attributen

Auswertung der Bienenwaage

In Bild 5 ist der Temperatur- sowie Gewichtsverlauf im Zeitraum einer Woche aufgezeichnet. Zur Auswertung wurde GNUPlot verwendet. Aus dem Diagramm kann ausgelesen werden:

Bild 5: Auswertung von gemessenen Daten mittels GNU-Plot
Bild 5: Auswertung von gemessenen Daten mittels GNU-Plot
(Bild: Klinger)

[07.-09.05.] Bei Regenwetter und Temperaturen unter 12°C findet kein Ausflug statt
-> Honig wird verbraucht.

[10.05.] Um 12:00 Uhr fliegt Bienenschwarm von ca. 1000 g Gewicht innerhalb weniger Minuten ab.

[11.-12.05.] Bienen tragen Nektar bei Temperaturen über 12°C ein.

[13.05.] Manueller Eingriff des Imkers führt zu Gewichtsveränderungen.

Verweise und Link zum Autor

- Sourcen der in Artikel verwendeten Treiber finden sich im Kernel-Source-Tree in den Unterverzeichnissen:
drivers/iio/adc/hx711.c
drivers/iio/pressure/bmp280*.c

- Kernel-Mailing-Liste für das Industrial-IO-Subsystem: linux-iio

- Zur Homepage des Autors geht es hier: www.it-klinger.de.

* Andreas Klinger ist selbständiger Linux-Trainer und Referent der Embedded Linux Woche sowie Gewinner des Best Speaker Awards auf dem ESE Kongress. Er ist als Bio-Landwirt und Imker im Nebenerwerb tätig.

(ID:45153380)