Arduinos programmieren und mit dem Speicher haushalten

Der Speicher auf den Mikrocontrollerboards ist oft knapp. So vermeidet man Speicherprobleme zur Laufzeit, die der Compiler im Vorfeld gar nicht bemerkt.

In Pocket speichern vorlesen Druckansicht 4 Kommentare lesen
Lesezeit: 4 Min.
Von
  • Ulrich Schmerold

Als ich die erweiterte Software für mein Taupunkt-Lüftungssystem mit Datenlogging-Funktion (siehe Make 2/22, S. 82) fertig programmiert und auf den Arduino Nano übertragen hatte, kam die Enttäuschung: Das Programm stürzte ständig ab. Auch stundenlanges Suchen und endlose Tests brachten keinen Erfolg. Der Fehler trat auch immer wieder an verschiedenen Stellen des Programms auf. Ich war mir irgendwann zu hundert Prozent sicher, dass der Code fehlerfrei war. Nach langen Recherchen in Arduino-Foren brachte ein Beitrag über die Speichernutzung endlich den Erfolg.

Make 2/22

Mehr zum Thema gibt es in Ausgabe 2/22 der Make.

Arduino: Mikrocontroller für Quereinsteiger

Nach dem Kompilieren erhält man in der Arduino-IDE unten einen Hinweis auf die Speichernutzung (siehe Bild). Aber: Auch wenn der Code ohne Fehler kompiliert wurde und danach noch Speicher übrig ist – wie in unserem Beispiel bei einer Speichernutzung von 84% –, bedeutet das nicht unbedingt, dass das Programm auf dem Arduino auch problemlos läuft!

Der Compiler weiß nämlich nur, wieviel Speicher der Programmcode und die globalen Variablen benötigen. Variablen oder Speicher, die erst zur Laufzeit angefordert werden, kann der Compiler zu diesem Zeitpunkt nicht berücksichtigen. So gibt er auch nur eine Warnung aus, dass der Arbeitsspeicher eventuell nicht ausreichen könnte. Treten dann beim Betrieb undefinierbare Probleme auf, so lohnt sich ein kritischer Blick auf den Umgang des eigenen Codes mit dem Speicher. Dabei ist zu beachten, dass der Arduino zwischen dem (persistenten) Programmspeicher (flash memory, program space) und dem (flüchtigen) dynamischen oder Variablenspeicher (SRAM, static random access memory) unterscheidet. Bei einem aktuellen Arduino Nano stehen dem Flash-Speicher von 32KB (wovon 2KB für den Bootloader abgehen) lediglich 2KB SRAM gegenüber.

Was kann man also tun, um Speicher zu sparen? Hier ein paar Tipps:

  • Verwenden Sie anstelle von Variablen mit speicherintensiven Datentypen wie String solche, deren Speicher kalkulierbar begrenzt ist, etwa char[10] für eine Zeichenkette mit maximal 10 Zeichen.
  • Konstante Zeichenfolgen wie Serial.println("Hallo Welt"); werden standardmäßig im SRAM abgelegt, können aber auch leicht in den Flash-Speicher verschoben werden: Serial.println(F("Hallo Welt"));
  • Für andere Variablentypen leistet der Variablenmodifikator PROGMEM aus der Library pgmspace.h ähnliches.
  • Werden vielleicht Libraries eingebunden, aber am Ende doch nicht verwendet? Einfach die #include-Anweisung entfernen. Das spart zwar keinen SRAM, sondern „nur“ Flash, aber auch mit dem muss man haushalten, gerade wenn man wie gezeigt Strings und andere Variablen dorthin auslagert.
  • Ausgaben auf dem seriellen Monitor, die bei der Programmierung zur Fehlersuche nützlich waren, sollten am Ende wieder gelöscht oder auskommentiert werden. Das gilt auch für Serial.begin (9600), das schon beim reinen Aufruf Speicher verschwendet, auch wenn danach nichts mehr über die serielle Schnittstelle ausgegeben wird.
  • Globale Variablen, die am Anfang des Programms definiert werden, belegen ihren Speicher immer. Lokale Variablen, die in einer Funktion angelegt werden, schlagen aber nur zu Buche, wenn die Funktion wirklich aufgerufen wird. (Leider sind das aber genau die Variablen, deren Platzbedarf beim Kompilieren unbekannt ist und deshalb höchstens eine Warnung erscheint, keine Fehlermeldung.)
  • Es sollte immer der kleinstmögliche Datentyp verwendet werden, also z.B. statt einem int (=2 Byte) genügt auch ein byte (=1 Byte), wenn garantiert nur Zahlen von 0 bis 255 abgespeichert werden. Das ist besonders bei Arrays wichtig, da man sonst schnell mal mit einer einzigen Programmzeile den kompletten Speicher des Arduino belegen kann …

Dies sind aber nur ein paar Möglichkeiten zur Speicheroptimierung. Auf den gängigen Arduino-Plattformen und in Foren dazu finden sich sicher noch einige mehr. Bei meinem Code waren das Problem tatsächlich die Strings, als die der Timestamp und die Logdaten zunächst implementiert waren – das sah erst einmal schöner aus und war auch bequemer im Umgang. Jetzt kommen stattdessen char-Arrays zum Einsatz und alles funktioniert einwandfrei.

Übrigens: Die beschriebenen SRAM-Speicherprobleme können auch dann auftreten, wenn der Compiler keine Warnmeldung ausgibt …

(pek)