Placement-New auf dem Arduino

Gestern kam ja das Thema auf, wie man Arduino-Peripherie per IO Pins mit Strom versorgt.

Aber einen wichtigen Software-Aspekt habe ich dort nicht erwähnt:

Was muss alles passieren, wenn der Strom nach der Unterbrechung wieder eingeschaltet wird?


Die meisten Arduino Bibliotheken nutzen globale C++ Objekte, um die Kommunikation mit Sensoren und Geräten abzuwickeln.
Und leider nutzen viele den Objekt-Konstruktor für das Initialisieren dieser Verbindung.

Wenn wir nun einfach den Strom unterbrechen, stören wir in den meisten Fällen auch diese Kommunikation, da beim erneuten Bestromen eine weitere Initialisierung notwendig ist.

Doch wie lösen wir das mit globalen Objekten, die ohne einen Heap auskommen müssen? (Mit Heap wäre es einfach ein: delete ptr; ptr = new TYPE(...);)

Meine Antwort ist: C++ Placement new

Zuerst müssen wir die placement-new Funktion manuell schreiben, da sie auf den Arduino-Plattformen nicht im Header <new> mit dabei ist. Sie wird aber vom Compiler dennoch unterstützt.

1inline void * operator new(size_t sz, void * dest_ptr)
2{
3  return dest_ptr;
4}

Und anstatt eines globalen Objektes (wie in meinem Beispielfall mit der U8GLIB)

1U8GLIB_SSD1306_128X32 u8g(U8G_I2C_OPT_NONE|U8G_I2C_OPT_DEV_0);

allokieren wir nur die nötigen Bytes für das Objekt, und setzen eine C++ Referenz auf den nicht initialisierten Speicher.

1char u8g_buffer[sizeof(U8GLIB_SSD1306_128X32)];
2
3U8GLIB_SSD1306_128X32& u8g =
4    *((U8GLIB_SSD1306_128X32*)&u8g_buffer[0]);

Und in der setup() Funktion wie auch nach dem Wiedereinschalten der Strom-Pins kommt einfach die Zeile:

1new (&u8g) U8GLIB_SSD1306_128X32(U8G_I2C_OPT_NONE|U8G_I2C_OPT_DEV_0);

Und schon haben wir ein C++ Problem mit ein paar weiteren C++ Zeilen gelöst.

Korrekterweise müssten wir vor dem Abschalten des Stroms den Destruktor des Objekts manuell aufrufen, aber nachdem gerade Arduino-Bibliotheken keinen Heap und andere Systemressourcen dauerhaft in ihren Objekten halten, kann man den Schritt auch der Einfachheit halber überspringen und darauf vertrauen, dass die Initialisierung auf einem nicht-deinitialisierten Speicherblock keine Nebeneffekte auslösen wird.

… zumindest keine von denen ich wüsste. 🤔

Handelt es sich nicht um eine C++ Bibliothek sondern um reinen C Code, so gibt es bestimmt eine adäquate lib_xyz_init() Funktion, die man anstatt dessen aufrufen kann.


Nachtrag: Ich ärgere mich gerade etwas darüber, dass meine Dauertests mit dem OLED Display dazu geführt haben, dass einige Pixel schon mehr ausgebrannt sind als ihre Nachbarn. Damit ist der Test-Text bei voller Beleuchtung weiter sichtbar.

Also noch ein Grund mehr, dass man Displays nie länger bestromen und leuchten lassen sollte, als es unbedingt nötig ist!

Arduino-Pin-OLED