Sd-Library in eigener Klasse verwenden

Hallo,
ich möchte mir einen kleinen Bordcomputer für mein Motorrad bauen. Das ganze besteht grob aus den folgenden Elementen:

  • Arduino
  • Display
  • SD-Karte, soll Daten abspeichern können
  • Momentan 3 Taster, wobei Taster 1 und 2 ein hoch- bzw. herunterschalten zwischen verschiedenen Menüs auf dem Bildschirm bewirken

Mein Ziel ist jetzt, das ganze möglich objektorientiert zu machen, aber leider fehlt mir hier wohl noch etwas Erfahrung. Die Probleme treten auf, wenn ich externe Libraries nutzen möchte. In dem Falle die "SD.h". Nachdem
ich über sd_init() die SD-Karte initialisiere, macht der Arduino komische Dinge:

Ohne die sd_init() (auskommentiert), kann ich über die Tasten zwischen den verschiedenen Bildschirmen hin- und her springen und auch im Seriellen Monitor wird mir der Name der aktuellen Seite angezeigt, z.B. "Seite 1". Wenn ich die sd-init() nicht auskommentiert habe, dann zeigt mir der Serielle Monitor nur noch "eite" an und mit den Tasten kann ich auch nicht mehr hin- und her springen zwischen den Bildschirmen. Auf dem Display wird allerdings immer "Seite 1" angezeigt. Fehlermeldungen gibt mir die IDE nicht aus.

Das sind meine Dateien:

Master.ino
Code:

//Funktionsbibliotheken einbinden
#include "Bordcomputer.h"

//Deklarationen
BC mybc;    //Objekt für Bordcomputer anlegen

void setup() {
 Serial.begin(9600);
 mybc.init();      //Initialisierung von Display, SD usw.
}

void loop() {
 mybc.check_button();
 mybc.show();
}

Bordcomputer.h
Code:

//Bordcomputer.h
//Klasse für den Bordcomputer

#ifndef _BORDCOMPUTER_        //verhindert mehrmaliges inkludieren der Klassendefinition
#define _BORDCOMPUTER_

#include "Arduino.h"
#include <U8g2lib.h>
#include <SPI.h>
#include <SD.h>

/*
Anschluss des SD-Moduls:
CS:    wie unter chipselect ausgewaehlt
SCK:   13
MOSI:  11
MISO:  12
VCC: + VCC (3,3 oder 5V)
GND: - GND
*/

class BC {
 private:
   //Variablen private
     int akt_seite = 1;
     int min_seite = 1;    
     int max_seite = 3;
     int pin_taste_1 = 8;
     int pin_taste_2 = 9;
     int pin_taste_3 = 10;
     U8G2_SSD1306_128X64_NONAME_1_HW_I2C* display;    // 0,96"- Display - bei Änderung auch init mit ändern
     //U8G2_SH1106_128X64_NONAME_1_HW_I2C* display;   // 1,3" - Display - bei Änderung auch Init mit ändern
     
     const int chipSelect = 4;                        //Pin, an dem ChipSelect hängt
     bool sd_ready = false;
     File* logFile;                                   //Logfile für alle Daten

   
   //Methoden private
     void show_seite1();    //zeigt Seite 1
     void show_seite2();    
     void show_seite3();
//      void show_seite4();
//      void show_seite5();
//      void show_seite6();
//      void show_seite7();
//      void show_seite8();
     void up();             //geht eine Seite nach oben
     void down();           //geht eine Seite nach unten
   
 public:
   //Variablen public
     
   //Methoden public
     BC();                  //Konstruktor
     ~BC();
     void init();           //Initialisierung von Display und anderen Objekten
     void display_init();
     void sd_init();
     void show();           //zeigt aktuellen Bildschirm an
     void check_button();   //kontrolliert, welche Buttons gedrückt werden
   
 };
#endif    // _BORDCOMPUTER_

Bordcomputer.cpp
Code:

#include "Bordcomputer.h"

//Konstruktor
BC::BC() {

 }
//##############################################################
//Destruktor
BC::~BC() {
 delete display;
 delete logFile;
 }
//##############################################################
void BC::init(){
 display_init();
 sd_init();
 Serial.println("Code geht in init weiter");  
}
//##############################################################

void BC::display_init() {
 //bei anderem Display ggfs. umkommentieren und auch bei privaten Variablen umkommentieren nicht vergessen
 display  = new U8G2_SSD1306_128X64_NONAME_1_HW_I2C(U8G2_R0, /* reset=*/ U8X8_PIN_NONE, /* clock=*/ 16, /* data=*/ 17);   // 0,96"-Display  
 //display = new U8G2_SH1106_128X64_NONAME_1_HW_I2C display(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);    // 1,3"-Display
 display->begin();  
 }

//##############################################################
       
void BC::sd_init() {
 //SD-Modul initialisieren
 //Dateien anlegen
 logFile = new File;
 if(SD.begin(chipSelect)) {
     Serial.println("card initialized.");
     sd_ready = true;
   } else {
     Serial.println("Card failed, or not present");
     //Fehler - Flag setzen
     sd_ready = false;
   }
   Serial.println("Code geht in sd_init weiter");
 }

//##############################################################
void BC::show() {
 switch(this->akt_seite) {
   case 1:
     show_seite1();
     break;
   case 2:
     show_seite2();
     break;
   case 3:
     show_seite3();
     break;
   default:
     show_seite1();
   }
 }
//##############################################################
void BC::show_seite1() {
 Serial.println("Seite" + this->akt_seite);
 display->firstPage();
 do {
   display->setFont(u8g2_font_profont11_mr);
   display->setCursor(0, 10);
   display->print("Seite ");
   display->print(this->akt_seite);  
 } while ( display->nextPage() );
}

//##############################################################
void BC::show_seite2() {
 ////Serial.println("Seite" + this->akt_seite);
 display->firstPage();
 do {
   display->setFont(u8g2_font_profont11_mr);
   display->setCursor(0, 10);
   display->print("Seite ");
   display->print(this->akt_seite);  
 } while ( display->nextPage() );
}

//##############################################################
void BC::show_seite3() {
 //Serial.println("Seite" + this->akt_seite);
 display->firstPage();
 do {
   display->setFont(u8g2_font_profont11_mr);
   display->setCursor(0, 10);
   display->print("Seite ");
   display->print(this->akt_seite);  
 } while ( display->nextPage() );
}


//##############################################################
void BC::up() {
//geht eine Seite nach oben
 this->akt_seite = this->akt_seite + 1;
 if(this->akt_seite > this->max_seite) {
   this->akt_seite = this->min_seite;
   }
 delay(200);
}

//##############################################################
void BC::down() {
//geht eine Seite nach oben
this->akt_seite = this->akt_seite - 1;
  if(this->akt_seite < this->min_seite) {
   this->akt_seite = this->max_seite;
   }
 delay(200);
}

//##############################################################
void BC::check_button() {
//wertet die Aktionen auf den Tasten aus (welche Tasten werden gedrückt, lang oder kurz usw.)
 int taster = 0;
 if (digitalRead(this->pin_taste_1) == HIGH) {
   taster = 1;
   //Serial.println(taster);
   }
 if (digitalRead(this->pin_taste_2) == HIGH) {
   taster = 2;
   //Serial.println(taster);
   }
 if (digitalRead(this->pin_taste_3) == HIGH) {
   taster = 3;
   //Serial.println(taster);
   }
   
 switch(taster) {
 case 1:
   BC::up();
   break;
 case 2:
   BC::down();
   break;
 }
}

Ich vermute, dass ich falsch auf die Methode SD.begin() zugreife, bzw. dass die Initialisierung/Deklaration nicht passt.

Die Ausgaben "Code geht in init weiter" bzw. "Code geht in sd_nit weiter" werden übrigens beide ordentlich ausgegeben. Also scheint sich der Arduino zumindest nicht aufzuhängen :-). Aber warum geht dann mein Menü nicht mehr weiter zu betätigen und der Serielle Monitor gibt so unvollständige Strings zurück?

Würde mich sehr freuen, wenn ihr mir helfen könnt.
VG Stefan

Was an den Antworten nebenan hat Dir nicht gefallen, ohne darauf eingegangen zu sein?

Gruß Tommy

Bin doch darauf eingegangen. Und es gab ja leider auch nur die eine Antwort von dir. Daher habe ich die Hoffnung, dass es hier mehr Leute lesen und mir vllt. jemand helfen kann. Mir geht es wie gesagt darum, das ganze in einer Klasse zu verstehen. Und ich habe auch ein Buch über C++ zuhause und andere Artikel gelesen. Nur bringt mich das in dem Fall leider nicht weiter.

Habe es vorhin auch nochmal mit SdFat.h probiert. Leider mit dem gleichen Ergebnis, dass die Ausgabe über Serial und die Steuerung über die Taster nicht mehr funktionieren.

Mit dem Display hatte ich am Anfang das gleiche Problem und habe es später zum laufen bekommen. Leider funktioniert das mit der Sd-Library nicht (erst Pointer definieren und später außerhalb vom Konstruktor initialisieren).

Hallo,

ist schon seltsam in 2 Foren parallel, musste selbst zugeben, hoffe ich. Merke, ein Forum ist kein Chat. Die Antwort auf dein Problem lautet. Man muss von jeder Klasse erstmal ein Objekt erstellen mit dem man dann arbeitet.

Das mit dem this-Zeiger kannst du dir übrigens sparen. Braucht man eigentlich nur wenn eine lokale Variable eine globale Variable gleichen Namens überdeckt

Ok, danke für den Hinweis mit dem this Zeiger, ich dachte es ist sauberer, wenn ich es immer mit dazu schreibe. Dann kann ich das in Zukunft weglassen.

Die Frage ist ja dann wie ich das Objekt anlege. Beim Display hatte ich ja schon das gleiche Problem. Dort habe ich dann in der *.h erst einen Zeiger erstellt und das Objekt dann in der display_init() erstellt.

display = new U8G2_SSD1306_128X64_NONAME_1_HW_I2C(U8G2_R0, /* reset=*/ U8X8_PIN_NONE, /* clock=*/ 16, /* data=*/ 17);

Dort hatte das ganze dann gut funktioniert.

Wenn ich das normale ReadWrite-Beispiel der SD-Library anschaue, dann wird ja auch nicht erst ein Objekt erstellt. Geschieht das dann schon direkt in der Header-Datei? Im Beispiel greift man dann ja auch auf das SD-Objekt zu.

Also ich habs wie gesagt auch nochmal mit der SdFat probiert, analog zu meinem Display:

in der *.h unter private:

SdFat* mySD; //Zeiger erstellen

in der *.cpp sieht meine sd_init() jetzt so aus:

void BC::sd_init() {
 //SD-Modul initialisieren
 //Dateien anlegen
 mySD = new SdFat;
 //logFile = new File;
 
 if(mySD->begin(chipSelect)) {
     sd_ready = true;
   } else {
     Serial.println("Card failed, or not present");
     //Fehler - Flag setzen
     sd_ready = false;
   }   
 }

und im Destruktor dann:

delete mySD;

Erstelle ich damit nicht mein Objekt, mit dem ich dann arbeiten kann? Leider funktioniert das immer noch nicht.

@TO: Setze Deinen Code bitte in Codetags (</>-Button oben links im Forumseditor oder [code] davor und [/code] dahinter ohne *).
Dann ist er auch auf mobilen Geräten besser lesbar.
Das kannst Du auch noch nachträglich ändern.

Gruß Tommy

Die Frage ist ja dann wie ich das Objekt anlege

Da die Instanz ja ein einzelnes Objekt beschreibt, das während der gesamten Existenz des Codes existiert,
macht es in der Regel keinen Sinn, es mit new/delete dynamisch zu erzeugen.

Ob es nun bereits in der .cpp definiert wird ( wie z.B.Serial) oder ob der Benutzer einen Namen frei vergeben kann ( Servo myServo; ), wenn er ein globales Objekt ( oder gar mehrere ) definiert, bleibt dem Ersteller der Bibliothek überlassen. Wenn es (unter Umständen) Sinn macht, in einem Sketch mehrere Instanzen zu haben, ist natürlich nur die zweite Variante sinnvoll.

Ich weis jetzt nicht genau was die SD-Klasse da macht, aber ich hatte es schon einige Male
das eine Standard-Library-Klasse es einfach nicht gerne hat wenn man sie via NEW inkarniert.
Die wollen einfach global sein. D.h der Konstruktor wird schon ausgeführt bevor
setup() ausgeführt wird. Ich habe das im einzelnen mal analysiert aber vergessen woran
es genau lag. Was verschenkst du wenn du wie in den Beispielen gezeigt SD global deklarierst ?
Viele von diesen Klassen haben auch keinen gescheiten Destruktor. D.h. wenn du sie dann
mehrmals inkarnierst und wieder deletest, gerade wenn da auch noch Hardwarklassen mitbenutzt
werden geht das in die Hose.

Ulli

Ich weis jetzt nicht genau was die SD-Klasse da macht

Ich hatte gehofft, der Fragesteller würde es sich ansehen (entweder im Code, oder zumindest in den Beispielen):
Sobald man

#include <SD.h>

eingefügt hat, ist ein einziges Objekt namens SD bekannt und definiert, mit dem man
SD.begin(cs_pin); ausführen kann.

Mehr als ein SD Adapter an einem Arduino ist mit dieser Library nicht vorgesehen und macht auch in der Regel keinen Sinn.

FrüherTM gab es mal Probleme, dass die IDE nicht gemerkt hat, dass du die SD library verwenden willst, weil in der .ino nichts davon drin steht. Und ein makefile-Äquivalent im geheimen erzeugt wird um Arduino-Künstler nicht zu verwirren.

Abhilfe: in deinem Master.ino

#include <SD.h>
#include "BC.h"

einbauen.

Mein Test mit

#include "test.h"
void setup() {
  Serial.begin(9600);
  Serial << F("Start: ") << __FILE__ << endl; 
  if (!SD.begin(10)) Serial << "Keine " ;
  Serial << "SD Karte gefunden" << endl;
}

void loop() { }
//include Streaming out
template<class T> inline Print& operator<< (Print &obj, T arg) { obj.print(arg); return obj; }
const char endl {'\n'};

#include <SD.h>

zeigt nun aber beim Compilieren (IDE 1.8.9) die Meldungen

Sketch wird kompiliert...
"C:\\Users\\Micha\\AppData\\Local\\Arduino15\\packages\\arduino\\tools\\avr-gcc\\5.4.0-atmel3.6.1-arduino2/bin/avr-g++" -c -g -Os -Wall -Wextra -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -MMD -flto -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10809 -DARDUINO_AVR_NANO -DARDUINO_ARCH_AVR "-IC:\\Users\\Micha\\AppData\\Local\\Arduino15\\packages\\arduino\\hardware\\avr\\1.6.23\\cores\\arduino" "-IC:\\Users\\Micha\\AppData\\Local\\Arduino15\\packages\\arduino\\hardware\\avr\\1.6.23\\variants\\eightanaloginputs" "-IC:\\Users\\Micha\\Arduino-1.8.9\\libraries\\SD\\src" "-IC:\\Users\\Micha\\AppData\\Local\\Arduino15\\packages\\arduino\\hardware\\avr\\1.6.23\\libraries\\SPI\\src" "C:\\Users\\Micha\\AppData\\Local\\Temp\\arduino_build_246457\\sketch\\sketch_apr01a.ino.cpp" -o "C:\\Users\\Micha\\AppData\\Local\\Temp\\arduino_build_246457\\sketch\\sketch_apr01a.ino.cpp.o"
Compiling libraries...
Compiling library "SD"

und alles ist gut:

Start: C:\Users\Micha\Documents\Arduino\sketch_apr01a\sketch_apr01a.ino
Keine SD Karte gefunden

Wenn du direkt SDFat verwenden willst, schau nach wie die SD Library das macht. new brauchst du nie.

So, habe mich jetzt nochmal ans Programm gesetzt. Einige der Fehler, die ich oben beschrieben hatte, hatten wohl doch eine andere Ursache. Bis zum SD.begin funktioniert es jetzt und es wird auch richtig erkannt, ob eine SD-Karte eingesteckt ist oder nicht. Ein Grund, das SD.begin in die Klasse zu nehmen ist, dass ich später die Möglichkeit haben möchte während des Betriebes eine Karte einzustecken. Ich vermute, dass das Probleme gibt, wenn ich einmal in der ino den SD.begin() durchführe und anschließend eine Karte einstecke. Also will ich einen Menüeintrag, wo man auswählen kann, dass eine SD-Karte eingesteckt wurde. Dann wird einfach die sd_init() aus der Klasse aufgerufen und ich kann üerprüfen, ob diese Karte auch wirklich gelesen werden kann. So meine Gedanken dazu.

Die Probleme, dass mir mein String nicht vollständig angezeigt wurde, lag wohl daran dass ich Serial.println("Text" + var); genutzt habe. Mit

Serial.print("Text"); 
Serial.println(var);

klappt das ohne Probleme. Und mein Bildschirm hat sich wohl nicht weiter bewegt, weil ich den PinMode nirgendwo ordentlich definiert hatte, damit ging in der Methode show() der Weg immer ins default der switch-Abfrage und es wurde immer der selbe Standardbildschirm dargestellt.

Bis zum initialisieren der SD-Karte funktioniert jetzt also alles reibungslos. Wenn ich allerdings auf die Karte schreibend zugreifen will, dann hängt sich der Arduino auf.

//Master.ino
//Funktionsbibliotheken einbinden
#include "Bordcomputer.h"

BC mybc;    //Objekt für Bordcomputer anlegen

void setup() {
  mybc.init();      //Initialisierung von Display, SD usw.
}

void loop() {
 mybc.check_button();
 mybc.show();

}

in der *.h deklariere ich jetzt also in der Klasse unter private:

File myFile;

im init() wird dann

 if(SD.begin(chipSelect)) {
     Serial << "card initialized.";
     sd_ready = true;
   } else {
     Serial << "Card failed, or not present";
     //Fehler - Flag setzen
     sd_ready = false;
   }

durchlaufen, was mir richtige Ergebnisse bringt (SD-Karte eingesteckt oder nicht bringt verschiedene Ausgaben)

später rufe ich in einer anderen Methode folgenden Code auf:

 myFile = SD.open("myTest.txt", FILE_WRITE);              //hier soll myFile initiiert, werden, was aber schon nicht klappt

/* if (SD.exists("test.txt")) {
    Serial << "test.txt exists." << endl;
  } else {
    Serial << "test.txt doesn't exist." << endl;
  }  
*/

 /*if(myFile) {
    myFile.println(akt_seite);
  } else {
    Serial << "Problem, die Datei zu öffnen" << endl;
    }
 myFile.close();
 */

Sowohl SD und myFile sollten doch jetzt in der ganze Klasse verwendbar sein, oder? SD wird im Kopf der *.h inkludiert und direkt in der Library wird das SD-Objekt erstellt. myFile wird von mir als private "Variable"/Objekt deklariert und später instanziert. Habe auch die beiden auskommentierten Varianten probiert, das führt alles zum Absturz vom Programm...

Damit habe ich jetzt schonmal das new/delete vermieden. Aber es macht m.M. keinen Sinn, das File oder die SD im Hauptprogramm, der *.ino zu definieren. Es ist doch gerade Sinn und Zweck einer Klasse, dass dort alles intern abgehandelt wird und mal von außen nur über die Methoden auf die privaten Eigenschaften zugreifen kann, oder sehe ich das falsch? Bestimmte Parameter kann man später immer noch übergeben, um diese zu initialisieren.

Bestimmte Parameter kann man später immer noch übergeben, um diese zu initialisieren.

Das wird beim Konstruktor gemacht. Stichwort: Initialisierungsliste / initializer list

Wenn du sowas hast:

class Test
{ 
   OtherClass obj;
};

Kann man Parameter nur über die Initialisierungsliste des Konstruktors der Test Klasse übergeben. Selbst im Konstruktor-Körper selbst existiert schon ein Objekt und es ist zu spät. Oder es schlägt generell fehl wenn kein entsprechender Konstruktor von OtherClass vorhanden ist.

Manche Arduino Klassen wie SD haben aber wie gesagt die Eigenheit dass sie sich selbst instantiieren ohne dass man irgendwas macht:

extern SDClass SD;
SDClass SD;

es macht m.M. keinen Sinn, das File oder die SD im Hauptprogramm, der *.ino zu definieren. Es ist doch gerade Sinn und Zweck einer Klasse, dass dort alles intern abgehandelt wird und mal von außen nur über die Methoden auf die privaten Eigenschaften zugreifen kann

Völlig richtig.

Überall wo #include <SD.h> am Dateianfang steht, ist die globale Variable SD bekannt.

das führt alles zum Absturz vom Programm...

Absturz ist ein sehr unklarer Begriff. Wieviel RAM ist denn vor dem SD.open frei ?
Probiere es mal in einer Dummy - Umgebung, die zwar die Verschachtelung in einer separaten Klasse verwendet, aber keine wesentlichen RAM-Anforderungen hinzufügt. (Wenn ein Standard-Demo-Sketch mit deinen Dateien problemlos funktioniert.)

Ich habe an verschiedenen Stellen mit folgender Funktion den freien RAM geprüft:

int BC::freeRam ()
{
extern int __heap_start, *__brkval;
int v;
return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}

Das Ergebnis:

freier RAM vor SD.begin() 255 Bytes
card initialized.
freier RAM nach SD.begin() 255 Bytes
freier RAM vor SD.open() 255 Bytes

Theoretisch sollte nach SD.open auch noch ein Hinweis kommen, aber da hat sich der ATMega wahrscheinlich schon aufgehangen. Wie viel Speicher braucht man denn dafür schätzungsweise?

Mit dem SD-Card-Lesemodul hatte ich übrigens auch Probleme im Beispiel-Sketch. Hatte zwischendurch ein Modul für die großen Speicherkarten genutzt. Manchmal funktioniert es, manchmal nicht. Brauchen die eine externe Spannungsversorgung (also mehr Strom, als das was vom Arduino kommt?). Habe jetzt mit 2 großen Modulen und mehreren Karten sowie 2 kleinen Modulen getestet. Bei den kleinen Modulen funktioniert es immer ohne Probleme, bei den großen Modulen nur ab und zu.

Wenn ich mal mein ganzes Display-Zeug auskommentiere, komme ich auf 740 Bytes jedoch bricht der Code immer noch bei SD.open ab. Habe mir aber vorsorglich trotzdem mal ein Arduino Mega 2560-Board bestellt. Nervt auch, wenn man immer an den Grenzen des Boards arbeiten muss ;-).

SD braucht zum open noch mindestens 512 Byte (die Sektorgröße).

Gruß Tommy

Wie gesagt, schau dir diese Werte mal in anderen Umgebungen an ...

Serenifly:
Das wird beim Konstruktor gemacht. Stichwort: Initialisierungsliste / initializer list

Wenn du sowas hast:

class Test

{
  OtherClass obj;
};



Kann man Parameter nur über die Initialisierungsliste des Konstruktors der Test Klasse übergeben. Selbst im Konstruktor-Körper selbst existiert schon ein Objekt und es ist zu spät. Oder es schlägt generell fehl wenn kein entsprechender Konstruktor von OtherClass vorhanden ist.


Manche Arduino Klassen wie SD haben aber wie gesagt die Eigenheit dass sie sich selbst instantiieren ohne dass man irgendwas macht:


extern SDClass SD;





SDClass SD;

Ich habe jetzt nochmal das SDClass SD; mit in meinen Code genommen. Hatte gehofft, dass dann klar ist, dass mein Objekt in der Klasse angelegt wird. Funktioniert aber dennoch nicht.

Mit der Initialisierungsliste das verstehe ich in meinem Fall immer noch nicht. Wenn sich das Objekt selbst anlegt bzw. keine Parameter benötigt (bei der SD-Klasse muss ich doch eigentlich das Objekt nur deklarieren und dann sollte es sich selbst über seinen Standardkonstruktor initialisiern, oder?), warum brauche ich dann die Initialisierungsliste, wenn gar nix zum initialisieren nötig ist?

Ich habe jetzt nochmal das SDClass SD

Wieso?! Sobald der Header inkludiert wird hast du das SD Objekt. Das nochmal zu machen ist schlichtweg falsch

warum brauche ich dann die Initialisierungsliste, wenn gar nix zum initialisieren nötig ist?

Deine Frage wirkte eher allgemein. Aber das betrifft z.B. deine Display Klassen die du mit new angelegt hast

Danke, habe ich auch schon wieder raus genommen. Ich hatte die Frage eher auf die SD-Lib gemeint, daher war ich etwas verwirrt. Also sollte mein Code theoretisch ja laufen, wenn genügend Arbeitsspeicher vorhanden ist, oder? Mal schauen, wann der Mega 2560 ankommt.

Bei den Displays kommt ja noch hinzu, dass es sich um 2 verschiedene Klassen handelt. Habe das jetztmal mit der Initialisierungsliste übernommen.

private:
U8G2_SSD1306_128X64_NONAME_1_HW_I2C display;    // 0,96"- Display - bei Änderung auch Konstruktor mit ändern
     //U8G2_SH1106_128X64_NONAME_1_HW_I2C display;   // 1,3" - Display - bei Änderung auch Konstruktor mit ändern

und im Konstruktor:

BC::BC() : display(U8G2_R0, /* reset=*/ U8X8_PIN_NONE, /* clock=*/ 16, /* data=*/ 17)     //0.96"-Display
//BC::BC() : display(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);                                 //1.3" -Display
{

}

Jetzt muss ich immer noch eine Variante auskommentieren. Lässt sich das nicht eleganter lösen?
Ich könnte mir z.B. vorstellen beim Aufruf der Klasse einen Integer zu übergeben (1 oder 2) je nach display-Art und dann mit einer #if -Direktive nur jeweils den relevanten Code verarbeiten zu lassen. Aber den Wert des Integers könnte ich ja erst innerhalb des Konstruktors auslese und da ist das Objekt displayschon erstellt...