Der Desktop-Benutzer ist es gewohnt, dass sein Betriebssystem gewissermaßen aus der Dose kommt. Für IoT-Geräte wird es aber frisch zubereitet – und zwar streng nach Rezept.
Eine Schlüsselentscheidung bei der Entwicklung eines jeden IoT-Projekts betrifft die Software und damit verbunden die Wahl des Betriebssystems. Letzteres kann dabei einer von drei Kategorien angehören, von denen jede ihre Vor- und Nachteile hat.
Die erste Kategorie nennt sich Bare Metal. Dafür braucht es kein besonderes Betriebssystem. Stattdessen obliegt es dem Entwickler der Anwendung, in diese solche Fähigkeiten einzubauen, die sonst das Betriebssystem beisteuern würde, also etwa das Scheduling oder den Zugriff auf die Hardware. Entscheiden sich die Entwickler für eine Bare-Metal-Applikation, implementieren sie gewöhnlich einen Hardware Abstraction Layer (HAL), der es der eigentlichen Anwendung erspart, direkt auf Register der Hardware zuzugreifen.
Die zweite Kategorie bilden die Real-Time Operating Systems (RTOS). Die Verarbeitung von Daten und Events ist bei ihnen immer zeitkritisch, und es wird stets ein Limit für die Bearbeitungszeit zugesichert. Das ist etwa im Bereich der Robotik notwendig, wo es katastrophale Folgen haben kann, wenn bestimmte Reaktionszeiten überschritten werden. Ähnlich wie bei Bare-Metal-Systemen werden Betriebssystem und Anwendung auch bei RTOS oft integriert.
Die dritte Kategorie sind IoT-Projekte mit Linux als Betriebssystem. Im Unterschied zu Bare Metal oder RTOS benötigt der Applikationsentwickler in diesem Fall nicht den Quellcode des Betriebssystems. Stattdessen greift die Anwendung hier über Syscalls oder Gerätetreiber auf darunterliegende Ressourcen zu.
Abbildung 1 zeigt den unterschiedlichen Ressourcenverbrauch der drei Alternativen. Den geringsten Ressourcenbedarf haben Bare-Metal-Systeme gefolgt von RTOS. Der Mehrbedarf bei RTOS erscheint gering, kann aber den Ausschlag geben. So ist möglicherweise ein RTOS keine valide Option für einen Mikrocontroller, wenn der, wie zum Beispiel der NXP KL03, nur 2 KByte SRAM adressieren kann, wogegen etwa Amazons FreeRTOS mindestens 16 KByte braucht. Dann wäre also ein Bare-Metal-System zwingend erforderlich.

Abbildung 1: Die Betriebssystemalternativen beanspruchen die Ressourcen des Systems unterschiedlich stark.
Linux verbraucht im Vergleich mit Abstand die meisten Ressourcen und benötigt außerdem einen Prozessor, der über eine Memory Management Unit (MMU) verfügt. Bare-Metal- und RTOS-Systeme haben diesen Anspruch nicht. Auf der anderen Seite erlaubt es Linux als Betriebssystem, auf eine riesige Auswahl an fertigen Applikationen und Services zurückzugreifen, die es für Bare Metal und RTOS so nicht gibt. Dieser Artikel konzentriert sich auf ein komplettes, eingebettetes Linux-System für IoT-Geräte und zeigt, wie sich ein solches Image mithilfe des Frameworks The Yocto Project/OpenEmbedded oder kurz Yocto [1] herstellen lässt.
Image erstellen
Jedes Linux für IoT-Geräte besteht aus fünf Hauptkomponenten (Abbildung 2). Da wäre als Erstes der Cross-Compiler, der es ermöglicht, Quellcode auf dem PC des Entwicklers so zu übersetzen, dass er sich auf dem IoT-Gerät ausführen lässt. Die zweite Komponente ist der Bootloader, der von Code innerhalb des ROM der CPU ausgeführt wird, sobald der Strom eingeschaltet wird. Am Status bestimmter Pins der CPU erkennt das ROM, wo es den Bootloader findet, etwa auf einer eMMC- oder MicroSD-Karte.
Der Code im ROM konfiguriert dann das jeweilige Bootmedium und sucht den Bootloader an einer voreingestellten Adresse. Wird er gefunden, transferiert ihn der ROM in den RAM, und die CPU arbeitet ihn dort ab. Der Bootloader ist dafür verantwortlich, die Umgebung so einzurichten, dass die CPU den Kernel laden und ausführen kann. Das kann das Herunterladen des Kernels von einem TFTP-Server im Netzwerk einschließen. Der meistbenutzte Bootloader für IoT-Geräte heißt U-Boot.
Als dritte Komponente fungiert dann der Kernel selbst, der dazu dient, diverse Applikationen gleichzeitig auf dem Gerät auszuführen und ihnen Hardwarezugriffe zu vermitteln. Die vierte Komponente ist der Device Tree (DTS), ein Bestandteil der Kernel-Quellen, der die CPU darüber informiert, über welche Peripheriegeräte das jeweilige Board verfügt und wie sie konfiguriert sind. Die fünfte und letzte Komponente bilden die eigentlichen Applikationen, die sich normalerweise im Root-Dateisystem (RFS) finden.
Während Desktop-Linux-Benutzer daran gewöhnt sind, eine ISO-Datei auf ein USB-Flash-Laufwerk herunterzuladen und dann das Flash-Laufwerk zu verwenden, um Linux auf ihrem PC zu installieren, funktioniert der Prozess für IoT-Systeme anders. Um alle fünf der oben genannten Komponenten zu erstellen, ist ein Framework erforderlich.
Das Framework wird vom Hersteller des System-on-Module (SoM) ausgewählt, das die CPU und alle erforderlichen Schaltkreise für die Energieverwaltung und Signalaufbereitung enthält. Wenn ein Unternehmen ein bestimmtes SoM für sein IoT-Produkt bestimmt hat, fährt dessen Anwender am besten, wenn er das vom SoM bereitgestellte Framework in Form eines Board Support Package (BSP) verwendet. Ein solches Framework für das Generieren kompletter Linux-Abbilder für IoT-Geräte ist Yocto.
Das Board Support Package besteht aus verschiedenen Layern, die jeweils ihre eigenen Repositories haben. Ein SoM-Provider gewährt zumeist Dritten Zugriff auf sein BSP in Form eines XML-Files, das dann Manifest heißt und pro Layer das passende Repository auflistet. Ein Beispiel zeigt Abbildung 3 – hier handelt es sich um ein BSP von Digi für das SoM CC6.
Die Datei aus Abbildung 3 verwendet das Repo-Tool von Android, um alle Layer herunterzuladen. Jeder Layer wird von einer bestimmten Organisation erstellt und gepflegt. In der Datei beziehen sich beispielsweise die Bezeichnungen »yocto« und »oe« auf Repositories, die die Organisation The Yocto Project verwaltet. Diese Repos dienen als Basis für das gesamte BSP, stellen kritische Anwendungen wie Bitbake zur Verfügung und bieten Referenzimplementierungen, auf denen andere Schichten aufbauen.
In Abbildung 3 lässt sich erkennen, dass tatsächlich auch der Meta-Freescale-Layer zum Yocto-Layer gehört, obwohl man ihn eigentlich in einem Repository erwarten würde, das Freescale/NXP verwaltet. Wegen der großen Popularität und häufigen Verwendung der Freescale-CPUs hat aber das Yocto-Projekt diesen Layer übernommen. Allerdings gibt es auch ein von NXP bereitgestelltes Repository, das NXP heißt und den Layer »meta-imx« enthält. Diese Schicht enthält Konfigurationen, Ressourcen und Anwendungen für die von NXP hergestellten CPUs der iMX-Serie.
Daneben finden sich auch Schichten, die von Digi verwaltet werden, dem Hersteller des SoM, wie »meta-digi« und »meta-digi-dualboot«. Schließlich kann man sehen, dass Digi auch andere Repositories in das Referenz-BSP aufnimmt, zum Beispiel »qt5« und »swu«, was letztendlich zu Schichten namens »meta-qt5« und »meta-swupdate« führt. Digi fügt diese Layer in das BSP ein, um es zu ermöglichen, Frameworks, Dienste und Anwendungen einzubinden, die von den Organisationen stammen, die diese Repositories verwalten. Abbildung 4 zeigt die typische Struktur der einzelnen Schichten.

Abbildung 4: Die Struktur eines typischen Layers: Neben einer Konfigurationsdatei sind vor allem Rezepte enthalten, die für das Build-Werkzeug Bitbake bestimmt sind.
Jeder Layer enthält ein Konfigurationsverzeichnis, das »conf« heißt. Es enthält seinerseits mindestens eine Konfigurationsdatei, die dem Yocto-Framework mitteilt, wie die vom Layer mitgebrachten Rezepte zu behandeln sind und mit welcher Yocto-Version der Layer kompatibel ist. Darüber hinaus können weitere Dateien enthalten sein, die dem Framework sagen, wie es bestimmte Konfigurationsoptionen für den Zugriff auf die Hardware handhaben soll.
Daneben umfasst jeder Layer einen Satz von Verzeichnissen mit den schon erwähnten Rezepten. Ein Rezept besteht aus einer Menge von Anweisungen für das Make-ähnliche Build-Werkzeug Bitbake zum Bau eines bestimmten Packages. Ein Package kann eine Applikation sein, der Linux-Kernel oder das ganze Linux-Image. Es resultiert immer in einem einzigen File, das auf das IoT-Gerät geflasht werden kann.
Rezepte mit ähnlicher Funktionalität oder solche, die eine bestimmte Komponente des Linux-Systems ansprechen, finden sich im selben Verzeichnis. So gehören etwa alle Rezepte, die ein Kernel-Modul erzeugen, das mit kundenspezifischer Hardware interagiert, in ein Verzeichnis »recipes-kernel«. Strenge Regeln für die Organisation der Rezepte gibt es nicht. Stattdessen folgen Community und Hersteller einer Reihe Best Practices, die das Auffinden des passenden Rezepts vereinfachen zu sollen.
In Abbildung 4 finden sich auch Dateien mit der Namenserweiterung ».bbappend«. Sie sind dafür gedacht, bestehende Rezepte zu erweitern oder zu modifizieren. Der Name des Erweiterungsrezepts muss mit dem Namen des zu erweiternden Rezepts übereinstimmen.
Praktische Schritte
Bis zu diesem Punkt wurde deutlich, wie ein von einem SoM-Hersteller angebotenes BSP aufgebaut sein muss, wenn es auf dem Yocto-Framework basiert. In den nächsten Schritten geht es daran, ein Image zu bauen, es auf ein IoT-Device zu flashen, es anzupassen und eine kleine Hello-World-Applikation auszurollen. Dazu nehmen wir an, dass als Hardware ein Digi-CC6N-SBC zur Verfügung steht.
Der PC des Entwicklers muss seinerseits den Anforderungen aus der Tabelle “Entwicklungsrechner” genügen. Außerdem müssen auf der Maschine bestimmte Softwarepakete installiert sein. Sie lassen sich mit dem Kommando aus Listing 1 einrichten. Zusätzlich benötigen wir das Repo-Tool von Google. Das lässt sich wie in Listing 2 gezeigt beschaffen.
|
Prozessor |
64 Bit, 8 Cores |
|
Betriebssystem |
Ubuntu 18.04 LTS |
|
Arbeitsspeicher |
8 GByte |
|
Massenspeicher |
250 GByte frei |
Listing 1
Software installieren
$ sudo apt-get install gawk wget git-core diffstat unzip texinfo gcc-multilib g++-multilib build-essential chrpath socat libsdl1.2-dev xterm minicom
Listing 2
Repo-Tool einrichten
$ sudo curl -o /usr/local/bin/repo http://commondatastorage.googleapis.com/git-repo-downloads/repo $ sudo chmod a+x /usr/local/bin/repo
Mithilfe des Tools Repo kommen wir jetzt an das Manifest-File von Digi (Listing 3, erste zwei Zeilen). Im Anschluss daran gilt es, das BSP für den Einplatinenrechner CC6 zu konfigurieren (dritte Zeile). Die Verzeichnisstruktur sieht nun so aus, wie es Abbildung 5 zeigt. Deutlich sieht man die verschiedenen Layer des BSP. Das Image lässt sich nun mit nur einem Aufruf erzeugen (letzte Zeile).
Listing 3
Manifest verwenden
$ repo init -u https://github.com/digi-embedded/dey-manifest.git -b gatesgarth $ repo sync -j8 --no-repo-verify $ source ./mkproject.sh -p ccimx6sbc $ bitbake dey-image-qt
Das File findet sich schließlich unter »tmp/deploy/images/ccim6sbc/«. Es lässt sich auf eine MicroSD-Card flashen, die wir dann in das CC6N-Board einsetzen. Danach verbinden wir den Rechner über ein serielles Kabel mit dem Board und schalten den Strom ein. Auf dem Bildschirm lässt sich der Bootprozess mitverfolgen (Abbildung 6).
Eine Applikation
Was bleibt jetzt noch zu tun, um eine kundenspezifische Applikation auszuführen oder den Kernel so zu modifizieren, dass er kundenspezifische Hardware unterstützt? Zuerst gilt es, zwei neue Repositories anzulegen. Eines nimmt unseren eigenen Layer für dieses Beispiel, das andere das Manifest, das dazu dient, alle Layer einschließlich des unsrigen per Repo-Tool herunterzuladen. Weil der Hersteller (in diesem Beispiel Digi) das Manifest kontrolliert, müssen wir es kopieren, das Repository mit unserem Layer ergänzen und das modifizierte Manifest schließlich in unser eigenes Repo hochladen.
Zum Zweiten brauchen wir die Files unseres Layers. Die Konfigurationsdatei können wir einfach aus einem anderen Layer kopieren und unter Verwendung der bereits gezeigten Struktur eigene Rezepte erzeugen. Für eine Hello-World-Applikation in C ergäbe sich so eine Verzeichnisstruktur wie in Listing 4.
Listing 4
Verzeichnisstruktur
meta-custom |-- apps | |-- files | | |-- hello-world.c | |-- hello-world.bb
Die Datei »hello-world.bb« muss speziell formatiert sein, Listing 5 zeigt den Inhalt. Die ersten vier Zeilen beschreiben das Rezept und dessen Lizenz. Stammt diese von der GPL ab, muss ihre Checksumme angegeben werden. Wenn man das Rezept nicht frei lizenzieren will, kann man in das License-Feld den Wert »CLOSED« eingeben.
Listing 5
hello-world.bb
DESCRIPTION = "Simple helloworld application"
SECTION = "examples"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"
SRC_URI = "file://helloworld.c"
S = "${WORKDIR}"
do_compile() {
${CC} hello-world.c -o hello-world
}
do_install() {
install -d ${D}${bindir}
install -m 0755 hello-world ${D}${bindir}
}
Die »SRC_URI« in Zeile 5 informiert Bitbake darüber, wo es die zum Rezept gehörenden Dateien findet. In Beispiel handelt es sich nur um ein einzelnes C-File. Man kann dort aber auch komplette Repositories angeben, die von Git oder Subversion verwaltet werden.
Am Ende enthält das Rezept noch zwei Funktionen (ab Zeile 7 und Zeile 10), die das Source-File der Applikationen kompilieren und das Resultat installieren. Das Yocto-Framework kommt mit Basisversionen dieser Funktionen, die der Anwender überschreiben kann. Auch für andere oft gebrauchte Funktionen enthält das Framework bereits Vorlagen, etwa für das Herunterladen der Quellen.
Die eigentliche Applikation steckt in einem sehr einfachen C-File (Listing 6). Um das Rezept zu erstellen, genügt das kurze Kommando »bitbake hello-world«.
Listing 6
hello-world.c
#include <stdio.h>
int main(void)
{
printf("Hello world!\n");
return 0;
}
Damit unser IoT-Device die Hello-World-Applikation ausführen kann, müssen wir sie in das endgültige Image integrieren. Bisher hat das Kommando »bitbake dey-image-qt« Bitbake dazu aufgefordert, das Rezept »dey-image-qt« zu verwenden, um daraus unser Image zu bauen. Um das Package anzufügen, das das Rezept »hello-world.bb« erzeugt, genügt ein Append-File namens »dey-image-qt.bbappend« mit dem Inhalt aus Listing 7.
Listing 7
Append-File
IMAGE_INSTALL_append = " hello-world"
Wenn wir nun »bitbake dey-image-qt« aufrufen, wird auch das Hello-World-Binary erzeugt und im endgültigen Image abgelegt, das wir nun auf das IoT-Device laden können.
Fazit
Eingangs haben wir verschiedenen Arten von Betriebssystemen kennengelernt, die auf einem IoT-Gerät zum Einsatz kommen können, und die verschiedenen Elemente eines vollständigen Linux-Images identifiziert. Schließlich haben wir gesehen, wie wir ein von einem SoM-Anbieter bereitgestelltes BSPerstellen, das auf dem Yocto-Framework basiert, und an unsere Bedürfnisse anpassen können.
Es gibt auch alternative Frameworks, beispielsweise Buildroot [2]. Der Vorteil von The Yocto Project/OpenEmbedded liegt darin, dass es unter den SoM-Anbietern sehr beliebt ist und sich zum vorherrschenden Framework für die Erstellung von Linux-Builds entwickelt hat. Der Nachteil: Es erfordert erheblich mehr Ressourcen als Buildroot. (jcb/jlu)
Infos
- Yocto-Project/OpenEmbedded-Framework: https://www.yoctoproject.org
- Buildroot: https://buildroot.org








