Hardware-in-the-Loop-Testing: CI/CD mit echter Embedded-Hardware
Warum Hardware-in-the-Loop-Testing das fehlende Stück für echtes CI/CD auf Embedded-Geräten ist: Treiber-, Timing- und Integrationsfehler auf echter Hardware.
Kurz gesagt: Host-seitige Unit-Tests und gemockte Schnittstellen prüfen Ihre Logik, sind aber blind für genau die Treiber-, Timing- und Integrationsfehler, die erst auf echtem Silizium auftreten – und manuelle Tests am Gerät sind zu selten und zu spät, um diese Lücke zu schließen. Hardware-in-the-Loop-Testing (HIL) integriert das reale Gerät in ein automatisiertes Rack, stimuliert seine echten Eingänge, vermisst seine echten Ausgänge und fährt diesen Kreislauf bei jedem Commit rund um die Uhr. Das macht aus “wir haben CI” echtes CI/CD für Embedded-Produkte.
Der blinde Fleck in der Embedded-Testautomatisierung
Die meisten Embedded-Teams pflegen eine solide Testpyramide auf dem Host: Unit-Tests für die Fachlogik, Integrationstests gegen gemockte Treiber, vielleicht einen QEMU-Lauf in der Pipeline. Das ist alles wertvoll – und alles teilt dieselbe Grenze: Es läuft gegen ein Modell der Hardware, nicht gegen die Hardware selbst.
In genau diesem Modell wohnt eine große Klasse von Embedded-Fehlern. Ein Display-Controller, der ein Register einen Frame zu spät übernimmt; ein CAN-Treiber, der unter Buslast eine Nachricht verliert; ein Touch-Panel, dessen Kalibrierung nach einem Firmware-Update driftet; eine I2C-Transaktion, die mit dem Power-Management des Kernels kollidiert – nichts davon hat eine Entsprechung in Ihren Mocks, also kann nichts davon einen host-seitigen Test rot färben. Solche Fehler zeigen sich auf dem Labortisch oder, schlimmer, im Feld.
Die übliche Antwort ist der manuelle Test am Gerät: Jemand flasht einen Build, klickt durch die Oberfläche, drückt ein paar Tasten, gibt frei. Das funktioniert, ist aber selten (an Meilensteinen, nicht bei jedem Commit), spät (die Regression steckt schon mehrere Merges tief) und nie vollständig (niemand bedient bei jedem Build von Hand jeden Screen und jede Busnachricht). Das Ergebnis ist eine Teststrategie, die bis exakt zu der Stelle vollautomatisiert ist, an der die interessanten Fehler passieren – und dort auf einen Menschen zurückfällt.
| Testebene | Läuft gegen | Findet | Blind für |
|---|---|---|---|
| Host-Unit-Tests | Reine Logik | Algorithmen, Zustandsautomaten | Treiber, Timing, echtes I/O |
| Gemockte Integration | Ein Modell des Treibers | Schnittstellen-Verträge | Hardware-Verhalten, Races |
| QEMU / Simulation | Ein emulierter SoC | Boot, grobe Regressionen | Peripherie-Eigenheiten, echtes Timing |
| Manuell am Gerät | Das reale Gerät, von Hand | Was geklickt wird, wann es geklickt wird | Alles andere; läuft selten |
| Hardware-in-the-Loop | Das reale Gerät, automatisiert | Treiber, Timing, Integration, UI wie ausgeliefert | Sehr wenig |
Was Hardware-in-the-Loop-Testing wirklich bedeutet
Hardware-in-the-Loop-Testing stellt das reale Device-under-Test (DUT) in den Mittelpunkt eines automatisierten Racks. Das Gerät führt exakt das Firmware-Image aus, das Sie ausliefern würden. Darum herum sitzt eine Instrumentierung, die die Rolle des Anwenders und der umgebenden Systeme übernimmt: Sie stimuliert die echten Eingabeschnittstellen und erfasst die echten Ausgabeschnittstellen – ohne eine Emulationsschicht dazwischen.
Auf der Eingangsseite heißt das, die tatsächlichen Busse und Bedienelemente anzusteuern: CAN- und CAN-FD-Frames einspeisen, seriellen Verkehr über UART/RS-232/RS-485 senden, Netzwerkanfragen erzeugen, GPIOs schalten und sogar ein kapazitives Touch-Panel so ansteuern, dass das Gerät eine echte Berührung registriert. Auf der Ausgangsseite heißt es, zu messen, was das Gerät wirklich tut: das Display über seine Videoschnittstelle aufnehmen und Frames mit einer Referenz vergleichen, CAN- und Serial-Antworten zurücklesen, LEDs und Statusleitungen beobachten, die Netzwerkausgabe prüfen.
Der Unterschied zum Mock ist nicht akademisch. Wenn das Rack eine Schaltfläche auf dem Touchscreen antippt und anschließend den resultierenden Frame von der Displayschnittstelle liest, wurde jede Schicht dazwischen – Touch-Treiber, Input-Stack, Anwendung, Qt-Szenengraph, GPU, Display-Controller – genau so durchlaufen, wie sie es beim Kunden tun wird. Es bleibt nichts mehr zu simulieren.
Der Kreislauf: Commit, Build, Flashen, Stimulieren, Messen, Urteil
Zu echtem CI/CD wird HIL erst, wenn das Ganze ein geschlossener, unbeaufsichtigter Kreislauf ist. Eine einzelne Iteration sieht so aus:
- Ein Entwickler pusht einen Commit.
- Die CI baut ein vollständiges, flashbares Firmware-Image.
- Das Image wird automatisch auf die reale Hardware im Rack deployed – geflasht und in einen definierten Ausgangszustand gebootet.
- Die Test-Suite stimuliert die echten Eingabeschnittstellen: Touch, CAN, Serial, Netzwerk, GPIO.
- Das Rack erfasst die echten Ausgabeschnittstellen: Display-Frames, LEDs, CAN, Serial, Netzwerk.
- Jede Beobachtung wird gegen eine Erwartung geprüft – daraus entsteht ein automatisches Pass/Fail-Urteil.
- Das Ergebnis wird an die CI zurückgemeldet, samt Artefakten – aufgenommenen Frames, Bus-Logs.
- Wiederholen, beim nächsten Commit, rund um die Uhr.
Der Gewinn ist dreifach. Regressionen fallen vor der Auslieferung auf statt danach, weil jeder Merge auf der Zielhardware geprüft wird. Der Kreislauf läuft unbeaufsichtigt rund um die Uhr, sodass die Abdeckung nicht länger davon abhängt, wie viel manuelles Testen ein Release verträgt – jedes Release bekommt die volle Suite. Und weil das Rack die realen Schnittstellen bedient, testen Sie genau das, was Ihre Anwender erleben, und nicht ein Modell davon.
Reproduzierbare Images: die Grundlage für deterministische Läufe
Ein HIL-Kreislauf ist nur so vertrauenswürdig wie die Images, die er flasht. Unterscheiden sich zwei Läufe “derselben” Firmware, weil sie auf verschiedenen Maschinen gebaut wurden oder unterschiedliche Abhängigkeitsversionen gezogen haben, sagt ein rotes Ergebnis nichts aus – Sie können eine echte Regression nicht von Build-Rauschen trennen. Deterministische, reproduzierbare Images sind daher kein Luxus, sondern das, was das Urteil überhaupt aussagekräftig macht.
Hier zahlt sich eine sauber strukturierte Embedded-Linux-Plattform und ein Yocto-Build unmittelbar aus. Mit gepinnten Layer-Revisionen und einem deklarativen Build ist das Image, das auf dem Rack bootet, bit-für-bit dasselbe, das die Pipeline erzeugt hat – auch Monate später rekonstruierbar. Jedes Rot oder Grün in der HIL-Stufe bildet dann auf einen konkreten, wieder baubaren Commit ab – genau die Eigenschaft, die Sie brauchen, wenn Sie eine Timing-Regression über eine Reihe von Merges hinweg per Bisect einkreisen.
HIL in die CI/CD-Pipeline einbinden
Mechanisch ist HIL nur eine weitere Pipeline-Stufe – eine, die zufällig auf einem Runner läuft, der physisch mit der Hardware verdrahtet ist. Die Build-Stufe erzeugt das Image als Artefakt; eine hardware-test-Stufe nimmt es auf, flasht es auf das DUT, fährt die Suite über die realen Schnittstellen und veröffentlicht einen Report. Eine minimale GitLab-CI-Form:
stages:
- build
- hardware-test
build-image:
stage: build
script:
- kas build kas-project.yml
artifacts:
paths:
- build/tmp/deploy/images/
hardware-test:
stage: hardware-test
tags: [embedded-testrack] # Runner physisch mit dem DUT verdrahtet
script:
- testrack flash --image build/tmp/deploy/images/*/*.wic.gz
- testrack run suites/ # Eingaben stimulieren, Ausgaben erfassen
artifacts:
when: always
paths:
- results/frames/ # aufgenommene Display-Frames für den Vergleich
reports:
junit: results/report.xml # Urteile erscheinen in der CI-Oberfläche
Weil die Urteile als Standard-JUnit-Ausgabe zurückkommen, erscheinen sie neben Ihren übrigen Testergebnissen, und aufgenommene Frames und Bus-Logs wandern zur Inspektion mit dem Job mit. Die Pipeline kümmert es nicht, dass diese Stufe Kupfer statt einen Container berührt – der Vertrag bleibt derselbe: ein Artefakt konsumieren, ein Pass/Fail liefern.
Praktische Aspekte: Flashen, Stabilität, Bildvergleich, Flakiness
Ein HIL-Rack zuverlässig zum Laufen zu bringen, ist zu großen Teilen die Übung, Nicht-Determinismus wegzukonstruieren.
Provisionierung und Flashen sollten das Gerät zu Beginn jedes Laufs in einen definierten, frischen Zustand versetzen – kontrollierte Stromversorgung, ein verlässlicher Flash-Pfad (Fastboot, ein SD-Mux, Netzwerk-Boot) und eine Boot-Prüfung, die schnell fehlschlägt, wenn das Image nicht hochkommt.
Ein stabiler Test-Harness ist am Anfang wichtiger als reine Abdeckung. Werden Touch-Koordinaten, Bus-Timing oder Startverzögerungen schlampig behandelt, fallen Tests aus Gründen aus, die nichts mit dem Code zu tun haben – und eine unzuverlässige Suite wird schnell ignoriert. Deterministisches Warten – auf einen bekannten UI-Zustand pollen statt eine feste Zeit schlafen – hilft enorm.
Bildaufnahme und -vergleich sind der subtilste Teil. Ein roher Pixel-für-Pixel-Vergleich ist viel zu brüchig: Antialiasing, ein blinkender Cursor oder ein Ein-Pixel-Layout-Versatz bringen ihn zu Fall. Praxistaugliche Racks vergleichen innerhalb von Toleranzen, maskieren dynamische Bereiche wie eine Uhr oder eine Animation und prüfen auf strukturelle Eigenschaften oder bestimmte Bildregionen statt auf den ganzen Frame.
Flakiness ist der Dauergegner. Isolieren und analysieren Sie sporadisch fehlschlagende Tests, statt sie blind zu wiederholen; ein Retry, der rot-dann-grün wird, verdeckt genau den Timing-Fehler, für den HIL existiert.
Das Embedded Testrack: HIL als schlüsselfertiges System
Diesen Kreislauf von Grund auf zu bauen – Stromsteuerung, Flashen, Touch-Ansteuerung, Frame-Capture, ein stabiler Harness – ist ein Projekt für sich. Unser Embedded Testrack ist ein produktisiertes HIL-System: Wir integrieren Ihr Gerät physisch in das Rack, verdrahten seine echten Ein- und Ausgabeschnittstellen und koppeln den gesamten Kreislauf an Ihre bestehende CI/CD-Pipeline und Ihre Testframeworks. Jeder Build wird automatisch auf die reale Hardware geflasht und über Touch, CAN, Serial und Netzwerk bedient, während das Display aufgenommen und verglichen, LEDs und Busse zurückgelesen werden und jedes Release ein vollständiges, automatisches Pass/Fail erhält – 24/7, ohne simulierte Mockups und ohne Menschen im Kreislauf. Die Ergebnisse landen dort, wo Ihr Team ohnehin hinschaut.
Fazit
Host-seitige Tests und Simulation bleiben die schnelle, günstige erste Verteidigungslinie – aber sie sehen die Treiber-, Timing- und Integrationsfehler nicht, die Embedded-Qualität ausmachen, und manuelles Testen am Gerät ist zu dünn, um die Lücke zu schließen. Hardware-in-the-Loop-Testing macht das reale Gerät zu einem vollwertigen Teil Ihrer Pipeline, sodass jeder Commit auf genau der Hardware geprüft wird, die Ihre Kunden nutzen. Wenn Sie echtes CI/CD mit echter Embedded-Hardware wollen, sprechen Sie mit uns über das Embedded Testrack.
Häufige Fragen
- Worin unterscheidet sich Hardware-in-the-Loop-Testing von Unit- oder Integrationstests?
- Unit- und Integrationstests laufen gegen ein Modell der Hardware und sehen daher keine Treiber-, Timing- oder Peripheriefehler. HIL führt die echte Firmware auf dem realen Gerät aus und bedient dessen physische Schnittstellen – und findet genau die Fehler, die ein Mock verdeckt.
- Ersetzt HIL-Testing die host-seitigen Tests?
- Nein, es ergänzt sie. Schnelle Host-Unit-Tests bleiben die erste Verteidigungslinie für die Logik; HIL fügt die Ebene hinzu, die sie nicht erreichen, und prüft das integrierte Gerät bei jedem Commit. Sie brauchen beides.
- Wie verhindert man, dass display-basierte Tests unzuverlässig werden?
- Niemals rohe Pixel Frame für Frame vergleichen. Innerhalb von Toleranzen vergleichen, dynamische Bereiche wie Uhren oder Animationen maskieren, auf einen bekannten UI-Zustand pollen statt fester Wartezeiten und sporadische Tests isolieren, statt sie blind zu wiederholen.
bitshift dynamics