Zugegeben: Assertions sind ganz alter Kram. Und dennoch sind sie sehr nützlich in der Entwicklung mit Microcontrollern.

Fehler zu finden, ist eine nervige Angelegenheit. Wenn man mit Microcontrollern wie dem AVR/Arduino, ESP8266 oder anderen Chips arbeitet, verbringt man häufig sehr viel Zeit damit. Man schreibt fließig Ausgaben an die Konsole, testet seine Schaltung und liest parallel mit, was im Programm so abläuft. Den Luxus eines guten Debuggers mit Breakpoints und Watches- wie z.B. in Eclipse oder Visual Studio – kennt man so nicht – oder erfordert die Arbeit mit einem JTAG-Debugger. So wird der Befehl Serial.println(…) zum besten Freund der Fehlersuche. Dabei gibt es einen kleinen und sehr alten Helfer in C/C++: assert().

Direkt übersetzt bedeutet ‚assert‘ auf Deutsch ‚versichern / beteuern‚. Ich verwende eher den Begriff ‚Annahme‚. Mit Assert kann man Daten überprüfen, ob diese korrekte Werte enthalten. Der nachfolgende Code nimmt an, dass die Aufrufer sich an bestimmte Regeln halten. Beispielsweise geht eine Funktion, die mit einem String arbeitet, davon aus, dass dieser String gefälligst einen Wert hat (nicht Null oder nicht leer ist). Warum sollte schließlich jemand so blöd sein, eine Funktion zu nutzen und keine passenden Daten zu übergeben? Andererseits? Woher sollte der Aufrufer das auch wissen?

Wie könnte man das mit Bordmitteln von Arduino lösen?

Dieser Code funktioniert natürlich wunderbar und Du bekommst eine Fehlermeldung an der Konsole, dass etwas nicht stimmt. Der Haken aber ist, dass die Prüfung immer ausgeführt wird. Das kostet Prozessorzeit, die ja gerade auf Mikrocontrollern kostbar ist. Fürs Debugging ist das vielleicht noch ok, aber später im Betrieb? Eine Lösung wäre die bedingte Kompilierung. Also z.B. so:

Schon besser. Wenn wir für den späteren Betrieb das Symbol DEBUG_MODE auskommentieren, wird der Prüfungscode nicht übernommen. Verbraucht keinen Speicherplatz und auch keine Prozessortakte. Doch der Code wird aber immer länger und unübersichtlich.

Einfaches Debugging mit Assert.h

Assert hilft uns jetzt weiter: Man verwendet es, um Annahmen festzulegen und damit die Ausnahmen / Unregelmäßigkeiten im Programmablauf abzufangen. Wie funktioniert assert? Die Nutzung ist ganz einfach. Fügt zuerst den passenden Header ein:

Auf dem Arduino müsst ihr zusätzlich noch eine Funktion und ein #define zur Ausgabe der Assert-Informationen definieren.

Nun könnt ihr überall den Ausdruck: assert(boolscher Ausdruck); verwenden. Wenn der Ausdruck innerhalb der Klammer wahr ist, wird die Programmausführung fortgesetzt. Ist sie falsch, dann wird das Programm abgebrochen und an der Konsole der Ort der Ausnahme angezeigt.

Ein Beispiel: Nachfolgend seht ihr ein Progrämmchen, in dem ein Servo (oder sonstewas) um einen Winkel gedreht werden soll. Dieses Servo kann aber nur Winkel zwischen 0 und 360 Grad annehmen. Normalerweise wissen das auch die Benutzer Eurer Funktion. Mit Assert könnt ihr die Funktion gegen blödsinnige Aufrufe absichern:

Das Programm sieht auf den ersten Blick ganz ok aus, nicht? Wenn ihr das Programm laufen lässt, passiert auch erstmal wenig. Der Winkel wird kontinuierlich um 10 erhöht. Nur was passiert, wenn jemand 370 Grad ansteuern will? Würdet ihr Euren Code mit Assert nicht absichern, würde der Wert 370 eiskalt an das Servo übertragen werden. Wenn ihr Glück habt, ignoriert Eure Schaltung das. Was aber, wenn es kein Servo, sondern ein Heizelement ist? Ooopps! Assert kann also Leben retten. Die Assert-Anweisung hilft Euch dabei, dass in diesen Fällen Euer Programm einfach stoppt.

Funktioniert auch mit dem ESP8266

Für ESP8266-Benutzer: Erstaunlicherweise funktioniert der Code in Anhängigkeit von der verwendeten ArduinoESP8266-Bibliothek nicht immer korrekt. Ein Workaround wurde aber schon gefunden.

Ich verwende hier die C++-Notation anstelle einer INO-Datei. Es ist sowieso eine gute Empfehlung auf INO-Dateien in Arduino-Projekten zu verzichten. Da im Hintergrund ein ganz normaler C++-Compiler sein Werk verrichtet, kann man auf die wenige Magie von Arduino verzichten. Der Vorteil liegt darin, dass Euch später Assert die richtige Zeilennummer der Ausnahme anzeigt. Bei .INO-Dateien erhaltet ihr die Zeilennummern, die den internen Arduino-Präprozessor Code repräsentieren. Damit kann man wenig anfangen.

Zusammenfassend:

  • Nutzt Assert(), damit fremde Leser Euren Code besser verstehen können! Er wird deutlich lesbarer. Ihr dokumentiert, was Eure Funktion als Parameter erwartet bzw. welche Dinge eben nicht explizit geprüft werden
  • Fehlschlagende Asserts erzeugen lesbare und nützliche Informationen über den aufgetretenen Fehler. Auf dem ESP8266 erfolgt dies bei Verwendung der ESP8266_Arduino Basis bereits automatisch. Beim ihm springt – wie praktisch – zusätzlich der Chip in den Upload-Modus, so dass ihr schnell Euren Code korrigieren und neu flashen könnt
  • Asserts können ausgeschaltet werden! In Abhängigkeit von der Platform, reicht das Setzen der Eigenschaft NDEBUG, um alle Anweisungen innerhalb des Asserts zu ignorieren. Sie belasten damit im späteren Produktions-Modus nicht den Prozessor. Außerdem sinkt der Speicherbedarf. Wie genau hängt von der verwendeten Plattform ab und ist schnell zu googeln
Alt, aber nützlich: Assert.h
Markiert in:    

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.