String in EEprom schreiben/lesen

Servus,

habe gerade folgendes Problem:
Ich möchte ins EEprom die Konfiguration einer kleinen Steuerung schreiben.
Darin befindet sich auch ein StringObject mit max 32 Zeichen, den ich über HTML einlese.

Ich habe jetzt gesehen, dass es da die EEpromAnything Routine gibt:

#include <EEPROM.h>
#include "EEPROMAnything.h"
struct config_t
{
    long alarm;
    int mode;
} configuration;

void setup()
{
    EEPROM_readAnything(0, configuration);
    // ...
}
void loop()
{
    // let the user adjust their alarm settings
    // let the user adjust their mode settings
    // ...

    // if they push the "Save" button, save their configuration
    if (digitalRead(13) == HIGH)
        EEPROM_writeAnything(0, configuration);
}

Das mit der Struktur und dem Lesen und Schreiben derselben in einem Rutsch wäre genau das was ich suche.
Allerdings ..... kann ich in der struct auch einen String definieren?
Woher weiss ich beim lesen, wie lang der beim Schreiben war?

Oder wie würdet ihr das angreifen?

Wenn du maximal 32 Zeichen hast kannst du auch ein char Array der Länge 33 nehmen. Dann kann es natürlich vorkommen dass bei kurzen Strings mehr geschrieben wird, aber String Objekte haben auch 6 Bytes Overhead (1 Zeiger und jeweils ein Integer für Länge und Größe)

Woher weiss ich beim lesen, wie lang der beim Schreiben war?

length(). Es wird das gesamte Objekt im EEPROM gespeichert. Also auch die Länge des Strings

OK,
das Schreiben/Lesen des charArrays soll mit einer Schleife 1-32 byteweise kein Problem sein.
Oder soll ich das Terminierungszeichen auch noch ins EEprom schreiben?

Wandeln mit toCharArray() oder?

Wandeln mit toCharArray() oder?

In diesem Fall mal ja. Das macht nämlich intern auch nur strncpy(). Ansonsten müsstest du selbst c_str() (für Lese-Zugriff auf das interne Array) und strncpy() (oder besser strlcpy()) zum Kopieren verwenden.

Früher gab es nichts anderes als toCharArray(). Inzwischen gibt es c_str() für Lese-Zugriff, aber es wird in vielen Beispielen immer noch unnötigerweise toCharArray() verwenden, auch wenn man gar keine Kopie braucht. Hier willst du aber eine.

Oder soll ich das Terminierungszeichen auch noch ins EEprom schreiben?

Ja! Auf jeden Fall. Deshalb soll das Array ja 33 Zeichen haben.

OK
Danke!

Ach ja, und für C Strings bekommst du die Länge natürlich mit strlen(). length() wäre für eine String Objekt.

Im Prinzip würde aber beides gehen. Du kannst wirklich alles mögliche ins EEPROM schreiben (weil das eine Template Funktion ist kann die mit allen Datentypen umgehen, inklusive selbst definierten). Und ein struct kann auch beliebige Datentypen enthalten.

Serenifly:
.... weil das eine Template Funktion ist .....

Die EEPROMAnything Funktion meinst du, oder?

Auf alle Fälle müsste ich mir mit der Funktion keine Gedanken über die Startadressen der einzelnen Strukturelemente im EEprom machen. Nur über die Startadresse der Struktur.

Die EEPROMAnything Funktion meinst du, oder?

Völlig unnötig!
get() und update() reichen in den allermeisten Fällen.

EEPROMAnything halte ich sogar für problematisch.
Denn: Das EEProm altert u.U. unnötig schnell.

Hmmm,
die kannte ich ja noch gar nicht....[schäm]

Wie es bei den Beispielen von get() und put() aussieht, können die ja auch mit einer Struktur arbeiten.
Bei update() bin ich mir nicht sicher, da das Beispiel nur eine float schreibt.

Werde mal testen, ob es mit einem StringObjekt in der Struktur auch geht.

Edit:

exit status 1
no matching function for call to 'EEPROMClass::update(int, config_t&)'

Mit EEPROM.update mault er bei der Struktur...

Also mit StringObject geht es anscheinend auch. Wenn ich die Reset-Taste am Arduino drücke, lese ich immer den String aus dem vorherigen Durchlauf aus dem EEPROM
Allerdings hab ich noch das Problem, dass wenn ich das erste Mal im noch nicht beschriebenen Bereich lese, mir der Arduino abschmiert :frowning:
Erst nachdem ich den Bereich einmal beschrieben habe, läuft es.
Anscheinend kann er keinen String aus dem undefinierten EEPROM Inhalt machen.
Kann man das irgendwie abfangen?
Hier mein TestCode:

struct config_t
  {
     String title;
  } configuration;

void setup() {
  Serial.begin(115200);

  EEPROM.get(0 , configuration);
  Serial.println (configuration.title);

  configuration.title = "xyz";
  configuration.title += analogRead(0);
  Serial.println (configuration.title);
  EEPROM.put(0 , configuration);
  
  EEPROM.get(0 , configuration);
  Serial.println (configuration.title);

hk007:
Anscheinend kann er keinen String aus dem undefinierten EEPROM Inhalt machen.

Das "leere" EEPROM enthält nur 0xFF, was hinsichtlich dem, was da eigentlich in der Struktur gespeichert sein sollte, keinen Sinn ergibt.

Du könntest ein Byte des EEPROMs als Marker lesen und dann mit 0x00 beschreiben. Nur wenn das Markerbyte 0x00 ist, liest Du auch die Struktur.

Wobei ich mir da nicht wirklich sicher bin, was er ins EEPROM schreibt.
Wenn ich mir das EEPROM byteweise auslese, dann stehen in den ersten 10Byte immer die selben Werte drin. Egal welchen Inhalt der String hat.

Ich glaub ich arbeite doch lieber mit charArrays.
Da weiss ich was ich hab, und wo was steht...

Wobei, wenn ich mal darüber nachdenke...geht das mit einem String Objekt wirklich? Das Objekt verwaltet ja nur einen Zeiger auf dynamischen Speicher (+ 2 ints für die Größe und Kapazität des Arrays). Und es wird erstmal nur der Zeiger abgespeichert. Der eigentliche String steht dann weiter im RAM...

Kann gut sein dass das doch nur mit C Strings geht.

Serenifly:
Kann gut sein dass das doch nur mit C Strings geht.

So seh ich das atm auch. Glaube der hat mir nur den Pointer ins EEPROM gespeichert.
Wie auch immer...arbeite jetzt mit C-Strings.

#include <EEPROM.h>


struct DatenSatz
{
  int version;
  char name[64];
  unsigned long resetCounter;
};

const int version = 2; // Versionierung der Datensätze

DatenSatz EEMEM eep_datensatz  = {1      ,"Combie",0}; // Arduino erzeugt *.eep Datei
DatenSatz default_datensatz    = {version,"Combie",0}; 
DatenSatz ram_datensatz; 





void setup() 
{
  Serial.begin(9600);
  EEPROM.begin();
  EEPROM.get(((int)&eep_datensatz),ram_datensatz); // lesen
  if(version != ram_datensatz.version) // versionskonflikt
  {
      EEPROM.put(((int)&eep_datensatz),default_datensatz); //schreiben
      EEPROM.get(((int)&eep_datensatz),ram_datensatz); // lesen
  }


  // update Reset Counter   
  ram_datensatz.resetCounter++;
  EEPROM.put(((int)&eep_datensatz),ram_datensatz); //schreiben


  // Ausgabe der Testdaten
  Serial.println("start");
  
  Serial.println(ram_datensatz.name);
  Serial.println(ram_datensatz.resetCounter);
}

void loop() 
{

  
}

Die erzeugte EEP Datei kann, wenn es gewünscht wird, per Hand, ins EEProm geladen werden. Arduino macht das leider, oder zum Glück, nicht automatisch.

Wenn die Struktur geändert wird, sollte die Versionskonstante um einen erhöht werden. Das vermeidet das lesen von Schrottdaten und erleichtert die Inbetriebnahme.

Evtl. könnte man beim Versionswechsel gleich den neuen Resetcounter mit abspeichern und damit in diesem Fall einen Schreibzyklus sparen.

Gruß Tommy