ESP32 Daten aus Variablen speichern

Hi zusammen,

ich benötige für den ESP32 eine Backup-Funktion bei einem Stromausfall etc. Dafür sollen einige Variablen gespeichert werden.

Ich habe schon etwas mit der EEPROM lib rumexperiemtiert. Konnte aber final noch nicht wirklich bewirken, das meine gespeicherten Werte wieder zum Vorschein kommen. Augenscheinlich ja, aber nach dem nächsten Flashen oder Neustart sind die Werte auf einmal anders.

#include <EEPROM.h>
#define EEPROM_SIZE 128

int posopen = 999;
int posnull = 999;
int posclose = 999;
int poscurrent = 999;

int EEPROMposopen;
int EEPROMposclose;

void setup() {

  //----------------------------------Serielle Schnittstelle|START
  Serial.begin(115200);
  //----------------------------------Serielle Schnittstelle|ENDE
     EEPROM.begin(EEPROM_SIZE);
    if (!EEPROM.begin(EEPROM_SIZE)) // size in Byte
  {
    Serial.println("failed to initialise EEPROM"); delay(1000000);
  }

  EEPROM.get(1, EEPROMposopen);
  EEPROM.get(2, EEPROMposclose);

  if(EEPROMposopen == posopen)
  {
    Serial.println("OK");
  }
  else
  {
    Serial.println("EEPROM value difference");
    Serial.print("EEPROMposopen:");
    Serial.println(EEPROMposopen);
    Serial.print("posopen:");
    Serial.println(posopen);
    delay(2000);
    Serial.println("Store Again ");
    EEPROM.put(1, posopen);
    delay(2000);
    EEPROM.get(1, EEPROMposopen);
    Serial.println("EEPROMposopen:");
    Serial.println(EEPROMposopen);
    Serial.print("posopen:");
    Serial.println(posopen);
    Serial.println("------------------------------------------");
  }

    if(EEPROMposclose == posclose)
  {
    Serial.println("OK");
  }
  else
  {
    Serial.println("EEPROM value difference");
    Serial.print("EEPROMposclose:");
    Serial.println(EEPROMposclose);
    Serial.print("posclose:");
    Serial.println(posclose);
    delay(2000);
    Serial.println("Store again");
    EEPROM.put(2, posclose);
    delay(2000);
    EEPROM.get(2, EEPROMposclose);
    Serial.print("EEPROMposclose:");
    Serial.println(EEPROMposclose);
    Serial.print("posclose:");
    Serial.println(posclose);
    Serial.println("------------------------------------------");
  }
  
}

void loop() {
  // put your main code here, to run repeatedly:

}

Der ESP32 hat kein EEPROM, aber ein eigenes Dateisystem SPIFFS, wo Du eine Parameterdatei ablegen kannst. Siehe in den Beispielen der IDE \libraries\SPIFFS\

Esp32 Webserver Arduino Tab mit einer Anleitung: Einführung zu fipsok.de.

nach dem Befehl "EEPROM.put" müssen Sie ein "EEPROM.commit();" hinzufügen

EEPROM ist am ESP32 abgekündigt.

suche nach ESP32 preferences ... das ist der Weg wie man Variablen am ESP32 persistiert.

@noiasca hat es schon geschrieben; die preferences lib hilft hier weiter. Habe mal aus einem längeren Programm die grundsätzlichen Routinen zusammengedampft, um ein Beispiel zu liefern:


#include <Preferences.h>

const char* csNAMESPACE       = "wifi";
const char* csSSID            = "SSID";
const char* csPWD             = "PWD";
const String clr = "********************";


struct Flash_data_t {
  String   mySSID   = clr;
  String   myPWD    = clr;
  boolean  isvalid  = false;
};


// GLOBALE VARIABLE

Flash_data_t   Flash_data;
Preferences ESP_Flash;

// ESP_Flash lesen, schreiben, drucken

void Read_ESP_Flash() {
  ESP_Flash.begin(csNAMESPACE, false);
  Flash_data.mySSID                = ESP_Flash.getString(csSSID, clr);
  Flash_data.myPWD                 = ESP_Flash.getString(csPWD, clr);
  Flash_data.isvalid               = (Flash_data.mySSID != clr &&  Flash_data.myPWD != clr);
  ESP_Flash.end();
};

void Write_ESP_Flash() {
  if (Flash_data.mySSID != clr && Flash_data.myPWD != clr) {  
    ESP_Flash.begin(csNAMESPACE, false);
    ESP_Flash.putString(csSSID, Flash_data.mySSID);
    ESP_Flash.putString(csPWD, Flash_data.myPWD);
    ESP_Flash.end();
  }
}

void Write_ESP_Flash_SSID() {
  ESP_Flash.begin(csNAMESPACE, false);
  ESP_Flash.putString(csSSID, Flash_data.mySSID);
  ESP_Flash.end();
}

void Write_ESP_Flash_PWD() {
  ESP_Flash.begin(csNAMESPACE, false);
  ESP_Flash.putString(csPWD, Flash_data.myPWD);
  ESP_Flash.end();
}

void Print_ESP_Flash_Data() {
    if (Flash_data.isvalid) {
      Serial.print(csSSID);Serial.println(": "+Flash_data.mySSID);
      Serial.print(csPWD) ;Serial.println(": "+ Flash_data.myPWD);
    } else {
      Serial.println("Keine gültigen Daten im Flash");
    }   
}


// SETUP

void setup() {
  Serial.begin(115200);
  // SSI und PWD beispielhaft mit clr beschreiben
  Serial.println("Daten im Flash auf * rücksetzen");
  Flash_data.mySSID = clr;
  Flash_data.myPWD  = clr;
  Write_ESP_Flash_PWD();
  Write_ESP_Flash_SSID();
  // Flash lesen und ausgeben
  Serial.println("Daten aus dem Flash lesen und ausgeben");
  Read_ESP_Flash();
  Print_ESP_Flash_Data();
  // SSI und PWD beispielhaft setzen
  Serial.println("Daten im Flash auf MeinWLAN und MeinPWD setzen");
  Flash_data.mySSID = "MeinWLAN";
  Flash_data.myPWD  = "MeinPWD";
  Write_ESP_Flash();
  // Flash lesen und ausgeben
  Serial.println("Daten aus dem Flash lesen und ausgeben");
  Read_ESP_Flash();
  Print_ESP_Flash_Data();
}


void loop() {
}

Nur als Anschauungsobjekt und zum Ausprobieren.

Hmmm, ja ich hab bereits einiges gelesen.

Das kurriose ist, das es Tutorials gibt wo ein ESP32 mit der EEPROM Variante trotzdem daten speichert. Das SPIFFS wäre nach meiner Recherche bereits auch auf dem absteigenden Ast, weil hier wieder die Schreibzyklen im ungünstigsten Fall das Leben des ESP32 verkürzen.

Auf jeden Fall danke für die Beispiele. Hat sich jemand schon mit Alternativen z.B. LittleFS beschäftigt bzw. damit Erfahrung?

LIttleFS ist "nur" eine andere Art der Datenspeicherung als Flash-Filesystem, als SPIFFS. Beim ESP8266 ist SPIFFS schon deprecated.
EEPROM auf den ESP ist auch nur eine Emulation, die Daten landen auch im Flash. Der Nachfolger sind Prefrences. Alles liegt in Flash und ist dessen Beschränkung der Schreibzyklen unterworfen. Die meisten neueren Technologien unterstützen Wear-Leveling.

Gruß Tommy

Hier ein Programm dass ich zum Demo-Programm heruntergekürzt habe.
Daher die nicht so ganz passenden Variablennamen.
Man kann aber trotzdem erkennen wie Daten schreiben / Daten lesen funktioniert.
Man muss vor dem Sketch-Upload als Partitionsschema ein Schema mit SPIFFS einstellen

#define dbg(myFixedText, variableName) \
  Serial.print( F(#myFixedText " "  #variableName"=") ); \
  Serial.println(variableName);

#define dbgi(myFixedText, variableName,timeInterval) \
  do { \
    static unsigned long intervalStartTime; \
    if ( millis() - intervalStartTime >= timeInterval ){ \
      intervalStartTime = millis(); \
      Serial.print( F(#myFixedText " "  #variableName"=") ); \
      Serial.println(variableName); \
    } \
  } while (false);

#include <FS.h>
#include <LITTLEFS.h>
#define FORMAT_LITTLEFS_IF_FAILED true

#include <SafeString.h>
createSafeString(myDemo_SS, 256);
createSafeString(KeyBoardText, 256);

createSafeString(MyEmail_SS   ,64);
createSafeString(MyPassWord_SS,64);
createSafeString(MySecret_SS  ,64);

int myCounter = 0;

void PrintFileNameDateTime() {
  Serial.println( F("Code running comes from file ") );
  Serial.println( F(__FILE__) );;
  Serial.print( F("  compiled ") );
  Serial.print( F(__DATE__) );
  Serial.print( F(" ") );
  Serial.println( F(__TIME__) );  
}

unsigned long MyTestTimer;

boolean TimePeriodIsOver (unsigned long &expireTime, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();  
  if ( currentMillis - expireTime >= TimePeriod )
  {
    expireTime = currentMillis; // set new expireTime
    return true;                // more time than TimePeriod) has elapsed since last time if-condition was true
  } 
  else return false;            // not expired
}

const byte    OnBoard_LED = 2;


void BlinkHeartBeatLED(int IO_Pin, int BlinkPeriod) {
  static unsigned long MyBlinkTimer;
  pinMode(IO_Pin, OUTPUT);
  
  if ( TimePeriodIsOver(MyBlinkTimer,BlinkPeriod) ) {
    digitalWrite(IO_Pin,!digitalRead(IO_Pin) ); 
  }
}



void prepareLittleFS() {
  //Start LittleFS
  if(!LITTLEFS.begin(FORMAT_LITTLEFS_IF_FAILED)){
    Serial.println("LITTLEFS Mount fehlgeschlagen");
    return;  
  }  
}


void readData() {
  File file = LITTLEFS.open("/SavedFile.txt", "r");
  
  if(!file){
    Serial.println("No Saved Data!"); 
    writeDefaultData();    
    Serial.println("writeDefaultData(); done"); 
  }
  while(file.available()){
    MyEmail_SS    = file.readStringUntil('\n').c_str();
    dbg("FileRead:",MyEmail_SS);
    
    MyPassWord_SS = file.readStringUntil('\n').c_str();
    dbg("FileRead:",MyPassWord_SS);

    MySecret_SS   = file.readStringUntil('\n').c_str();
    dbg("FileRead:",MySecret_SS);
  }   
  file.close();
}


void writeDefaultData() {
  dbg("writeDefaultData:",0);
  File file = LITTLEFS.open("/SavedFile.txt", "w");

  MyEmail_SS = "enter_your_email_adress2";
  file.println(MyEmail_SS);
  
  dbg("default file.println:",MyEmail_SS);
  
  MyPassWord_SS = "enter_your_password2";
  file.println(MyPassWord_SS);
  dbg("default file.println:",MyPassWord_SS);
  
  MySecret_SS = "enter_your_secret2";
  file.println(MySecret_SS);
  dbg("default file.println:",MySecret_SS);
  delay(1);

  file.close();
  
  Serial.println("default Write successful");  
}


void writeData() {
  dbg("writeData:",0);
  File file = LITTLEFS.open("/SavedFile.txt", "w");
  
  file.println(MyEmail_SS);
  dbg("file.println:",MyEmail_SS);
  
  file.println(MyPassWord_SS);
  dbg("file.println:",MyPassWord_SS);
  
  file.println(MySecret_SS);
  dbg("file.println:",MySecret_SS);
  delay(1);

  file.close();
  
  Serial.println("writeData Write successful");
}


void setup() {
  Serial.begin(115200);
  Serial.println();
  Serial.println( F("Setup-Start") );
  PrintFileNameDateTime();

  prepareLittleFS();
  readData();  
}


void loop() {    
  BlinkHeartBeatLED(OnBoard_LED,500);
  if ( TimePeriodIsOver(MyTestTimer,10000) ) {
    myCounter++;
    MyEmail_SS = myCounter;
    writeData();
    readData();
  }        
}

vgs

Gott was ist das ein riesen Aufwand um zwei Variablen zu speichern... Was das angeht hängt die Technik da irgendwie hinterher....

Danke für den Demo Code. Ich weiß nicht genau ob ich davon alles verstehe.
Also ich verstehe so ungefähr das anscheined eine Textdatei Zeile für Zeile geschrieben wird und dann wieder eingelesen. Wobei ich mich frage, wenn ich einen integer ablegen will. wird er als String gespeichert und beim Laden wieder in einen Integer automatisch umkonvertiert? Oder wie läuft das ab? In diesem Beispiel werden z.B. ja nur Strings gespeichert.

Unterschied zwischen writeData und writeDefaultData? Eigentlich nicht relevant oder?
createSafeStrings(var,zeichenanzahl?) oder was sind die 256 und 64?

Nimm wie schon mehrfach vorgeschlagen, die ESP32 preferences. Da ist der Aufwand wirklich nicht groß.

1 Like

Wie wäre es mit einem Beispiel-Programm das das demonstriert?
vgs

Das ist ja mal eine richtig gute Idee. :wink:

Hier mal was kleines als Democode zusammen getippt:

// Hier gib es weitere Beispiele: https://unsinnsbasis.de/esp32-preferences/
#include <Preferences.h>                                // Einstellungen speichern statt EEProm

Preferences prefs;
unsigned int mp3Volume;
unsigned int mp3TitelNo;


void readPrefsData()
{
  prefs.begin("my_prefs", false);
  mp3Volume = prefs.getUInt("mp3Volume", 0);
  mp3TitelNo = prefs.getUInt("mp3TitelNo", 0);
  prefs.end();
}


void writePrefsData(char* address, unsigned int newData)      // Daten speichern
{
  prefs.begin("my_prefs", false);
  prefs.putUInt(address, newData);
  prefs.end();
}


void setup()
{
  // eigenes Setup
  readPrefsData();
}

void loop()
{

 // eigener Code....

  if (saveConfigData)
  {
    writePrefsData("mp3Volume", mp3Volume);
    writePrefsData("mp3TitelNo", mp3TitelNo);
  }
}

Gibt es von mir in Post 5 auch schon ... Allerdings etwas länger, da auch aus einem größeren Zusammenhang herausgeschnitten und mit ein paar zusätzlichen Beispielen "verziert" ... ;-

Genau deswegen war ich auch von der Frage von StefanL38 echt überrascht.

Dein Beispiel ist kürzer und damit sicherlich auch übersichtlicher. Und doppelt hält besser ...

Ok, mal schauen, was der TO daraus macht.

Das Demo-Programm in Post #5 habe ich komplett übersehen.
Das mit den Preferences ist supi
vgs

Ok und super, dass wir dich überzeugen konnten.
Und die Flexibilität von Preferences ist enorm. Genau so einfach geht es auch mit Strings.

Siehe mein Beispiel :wink:

Eine ordentliche Übersicht und Anleitung gibt es hier

Ausführliches gibt's hier:

EDIT 30.10.2024:

Der folgende Link scheint nicht mehr erreichbar zu sein!!!
ec2021

https://unsinnsbasis.de/esp32-preferences/

Oder meinen Link im obigen Codebeispiel. :wink:

1 Like