Struktur aus EEPROM auslesen



  • Hallo ich habe ein Project mit einem Arduino Nano in PlatformIO.
    Da habe ich bestimmet Konfigurationen im EEPROM gespeichert. D.h. mehrere gleiche.
    Da möchte ich über ein Klasse/Struktur zugreifen.
    z.b. ähnlich

    class haus {
    public:
     int zimmer;  // anzahl qm
    int bad;
    int flur;
    ..
    }
    
    // 
    haus haus1;
    haus haus2;
    ...
    
    test = haus1.zimmer;
    test1 = haus2.zimmer;
    

    also so ähnlich, möchte jedoch keinen weitern RAM brauchen, die Datenquelle soll direct der EEPROM sein.
    Geht sowas überhaupt oder wie realisiere ich das ?
    Wäre super wenn mir da wer einen Tip/Beispiel geben könnte.
    Dank un Gruß
    Rainer



    1. Programmierst du den mit C oder C++ direkt, oder hast du die Arduino Bibliotheken zur Verfügung?

    Hier ist die https://www.arduino.cc/en/Reference/EEPROM referenz für arduino.



  • Edit: Das ist natürlich nur der C++-Teil der Antwort. Wie du dein Objekt aufs EEPROM schreibst und wie du an die Speicheraddresse weiss vielleicht jemand anderes oder du findest es selbst heraus.

    Ja, ich denke sowas geht:

    • Stell sicher, dass deine Klasse TriviallyCopyable ist.
    • Stell sicher, dass das Speicher-Layout der Klasse exakt den Daten im EEPROM entspricht. Auch bezüglich Padding und Alignment (#pragma pack)
    • Bevor du darauf zugreifst, erzeuge dein Objekt mit Placement New:
      new(eeprom_object_pointer) haus;
      Du kannst auch gleich eine Referenz für bequemen Zugriff initialisieren. Z.B. so:
      const haus& haus1= *(new(eeprom_object_pointer1) haus);
      Ich vermute mal, dass EEPROM-Adresssen ganz normale Pointer sind (?).
    • Wenn du mit dem Objekt fertig bist, dann zerstöre es mit manuellem Destruktor-Aufruf: haus1.~haus();

    Etwa so:

    #include <new>
    #include <type_traits>
    #include <iostream>
    
    #pragma pack(push, 1)
    
    class Haus
    {
      public:
        int zimmer;
        int bad;
        int flur;
    };
    
    #pragma pack(pop)
    
    static_assert(std::is_trivially_copyable<Haus>::value);
    
    char memory[sizeof(Haus)] = { 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0 };
    
    auto main() -> int
    {
      const Haus& haus = *(new(memory) Haus);
      std::cout
        << haus.zimmer << "\n"
        << haus.bad << "\n"
        << haus.flur << "\n";
      haus.~Haus();
    }
    

    https://ideone.com/EScJmK



  • @Finnegan sagte in Struktur aus EEPROM auslesen:

    char memory[sizeof(Haus)] = { 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0 };

    Die ATmega328 integer sind 2 byte breit.



  • Ohne MMU:

    int *eeprom = reinterpret_cast<int*>(0x12345678);
    

    Wie Du noch die MMU umgehen kanst, kann ich Dir jetzt nicht sagen.



  • @mgaeckler
    Der TE nutzt einen Arduino Nano und dieser hat 32 kbyte Flash, 1 kByte EEPROM, 2 kByte SRAM.

    Daher bezweifele ich mal dass das Board irgenteine MMU drauf hat.

    Also so ein Teil ist das.

    Edit: Rechtschreibung



  • Also was @Finnegan gesagt hat, habe ich noch nie gemacht, aber das hängt vermutlich damit zusammen, dass ich kaum Ahnung davon habe 😃

    Ich nehme mal an, dass du keine Arduino Libs nutzt, sondern nur Standard libs. Für eeprom gibt es dafür avr/eeprom.h in der avr-libc.
    https://www.nongnu.org/avr-libc/user-manual/group__avr__eeprom.html

    Das ganze ich recht selbsterklärend zu nutzen mit dem EEPROM Schlüsselwort:

    uint8_t myVariable EEPROM; 
    eeprom_write_byte (&myVariable, 42)
    uint8_t myRealVariable = eeprom_read_byte(&myVariable)
    

    Entsprechend kann man mit zum Beispiel auch arrays und structs und arrays von structs mit eeprom_write_block super einfach schreiben und lesen.

    Siehe auch: ttps://www.mikrocontroller.net/articles/Kategorie:AVR-GCC-Tutorial#EEPROM

    Kniffliger wird es meistens, wenn es darum geht wie der EEPROM seinen Initalwert bekommt. Eine gängige Praxis ist das lesen des EEPROMS und checken auf 0xFF, der default Wert. Wenn der noch drin steht, den richtigen default Wert setzen.
    Das ist natürlich nur so mittelmäßig robust, insbesondere dann wenn 0xFF in deinem Code ein legitimer Wert ist (in solchen Fällen hat man dann eine neue EEPROM Variable eingeführt, die sagt, ob deine richtige Variable schon initalisiert wurde)

    Einfacher geht es mit Compiler Features, dazu einfach so tun als wäre deine EEPROM eine richtige Variable (Wichtig: Das ist sie nicht, im normalen Code musst du die eeprom methoden nutzen)

    uint8_t myCoolVariable EEPROM = 42
    

    In deinem Makefile (oder ähnliches) kannst du dann eine .eep Datei erzeugen lassen, ungefähr so:

    $(BINDIR)/$(TARGET).eep: $(BINDIR)/$(TARGET).elf
    	avr-objcopy -O ihex -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma .eeprom=0 $(BINDIR)/$(TARGET).elf $(BINDIR)/$(TARGET).eep
    

    Der Compiler nimmt dann die 42 und schreibt sie in die .eep Datei

    Beim upload Skript das eeprom file mit angeben, z.B. so:

    upload:
    	avrdude -p $(MCU_AVRDUDE) $(PORT) $(BAUD) -c $(PROGRAMMER) -U flash:w:$(BINDIR)/$(TARGET).hex:i -U eeprom:w:$(BINDIR)/$(TARGET).eep
    

    Edit: Und kleiner Hinweis, überdenke dein Programm ... das ist ein Arduino Nano, der hat nicht grade Ressourcen im Überfluss. Brauchst du wirklich 32 bit ints etc. .... OOP auf dem MU geht super, aber man muss wissen, was man verwenden darf und was eher nicht.



  • @5cript sagte in Struktur aus EEPROM auslesen:

    Die ATmega328 integer sind 2 byte breit.

    War nur ein ideone-lauffähiges Beispiel, dass das noch an das konkrete Problem angepasst werden muss. Ob die Little-Endian-Annahme für die Chips überhaupt zutrifft, weiss ich auch nicht.

    Ansonsten funktioniert das auch nur, wenn man über reguläre C++-Pointer auf den EEPROM-Speicher zugreifen kann (wenn die Daten irgendwie in den CPU-Addressraum "gemappt" sind, entweder mit MMU oder irgendwie hartverdrahtet). Nach kurzem Überfliegen der EEPROM-Doku scheint mir das hier nicht der Fall zu sein (dachte zuerst das wäre so). Du musst also wohl eher mit Gettern/Settern arbeiten.

    Die Klasse würde dann z.B. nur die EEPROM-Adresse des Objekts halten und sich die eigentlichen Daten, immer dann wenn sie z.B. via Getter angefordert werden, aus dem EEPROM lesen.



  • Danke erst mal, da sind gute Ideen dabei. Das brauchbarste ist nach dem ersten überfliegen wohl die vorletzte. Ich arbeite zwar mit PlatformIO habe aber auch die Arduino IDE installiert. Logisch verwende ich für die Adressen in den EEPROM 16 bit und sonst fast nur uint8_t. Der RAM ist nicht gerade übermäßig bei 328p. Ich brauche das für ein Smart Home System. Noch unvollständige Doku hier https://zen-works.de/index.php?id=13.
    Im EEPROM stehen im wesentlichen Konfigurationsdaten die via CAN Bus an den Knoten gesendet werden, der sie dann in den EEPROM schreibt. Nun sind die Datenstrukturen dort etwas komplexer als im Beispiel und der Zugriff somit nicht ganz trivial, insbesondere wenn sich etwas ändert muss ich viel anpassen. Daher eben die Suche nach einer geschickteren Möglichkeit.
    Danke erst mal für die Hilfe und Ideen.
    Gruß
    Rainer



  • @muekno Ich nutze da persönlich einen normalen Texteditor z.B. Visual Studio Code und Standard C/C++. Arduino Code ist nett für kleine Spielereien und testen, aber letzlich viel zu ineffizient und es passiert zu viel im Hintergrund. Ich brauche da mehr Transparenz.

    Für ein Projekt vor kurzem musste ich tatsächlich auch mal eine etwas komplexere Datenstruktur in den Eeprom schreiben, das ging mit den gennanten Methoden ganz gut.