Preferences --> Wieviel passt rein

Hi zusammen,

ich nutze die Preferences LIbrary zum speichern verschiedener Wertepaare.
So wie ich es recherchiert habe, stehen auf dem ESP32 20 KB für diesen Speicher (NVS) zur Verfügung.

Nun versuche ich herauszufinden, wieviele Wertepaare ich ca. darin speichern kann.

Die folgende Tabelle (Quelle: Preferences — Arduino-ESP32 2.0.6 documentation) gibt ja Informationen darüber, wieviel Speicher die verschiedenen Datentypen einnehmen:

Das heißt ja, dass dieses Wertepaar hier 4 byte einnimmt, korrekt?

prefs.putUInt("test", 135456);

Und dieses auch

prefs.putUInt("test", 1);

Aber:

Wie kann ich ausrechnen, wieviel ein String einnimmt. Da steht ja "variable".

Beispiel:
prefs.putString("Mein Passwort", "Hier steht mein Passwort drin");

Kann man irgendwie rausbekommen, wieviele Bytes das sind?
Oder noch besser: Kann man sich irgendwie ausgeben lassen, wieviel Speicher man schon insgesamt für Wertepaare innerhalb der preferences-lib belegt?

Klar: Für größere Speicheraktionen ist LittleFS natürlich die bessere Wahl.

Grüßle
Daniel

Ein Zeichen verbraucht ein Byte. Also Speicherverbrauch = Stringlänge. Ich kenne diese Library nicht und weiß nicht, ob evtl. das String-Ende Zeichen '\0' auch noch mitgespeichert wird. Dann wäre es Stringlänge + 1.

Ich würde davon ausgehen, dass das '\0' bei Key mit gespeichert wird (auch bei Value ist Zeichenkette), sonst müsste man eine Längeninformation mit speichern, was keinen Vorteil bringen würde. Außerdem ist der Typ char * für Zeichenketten 0-terminiert üblich.

Gruß Tommy

Diese Aussage sehe ich etwas skeptisch, da Texte häufig in Unicode und UTF-8 codiert werden. Da braucht ein Zeichen dann ein bis vier Bytes.

#include <Preferences.h>
Preferences preferences;
//                         123456789012345
const char meinText[50] = "Hallo ÄÖÜ€ Ende";

void setup() {
  Serial.begin(115200);
  delay(500);
  Serial.println("\nStart ...");

  if(preferences.begin("credentials"))
  {
    Serial.println("begin erfolgreich");
  }
  if(preferences.clear())
  {
    Serial.println("clear erfolgreich");
  }
  Serial.printf("sizeof(meinText): %2d\n", sizeof(meinText));
  Serial.printf("strlen(meinText): %2d\n", strlen(meinText));
  Serial.printf("     strlen(\"Ä\"): %2d\n", strlen("Ä"));
  Serial.printf("     strlen(\"€\"): %2d\n", strlen("€"));
  Serial.printf("       putString: %2d\n", preferences.putString("text", meinText));
  Serial.println("Ende");
  preferences.end();
}

void loop() {}
Start ...
begin erfolgreich
clear erfolgreich
sizeof(meinText): 50
strlen(meinText): 20
     strlen("Ä"):  2
     strlen("€"):  3
       putString: 20
Ende

Lesestoff:

Eigene Experimente mit

freeEntries

Get the number of free entries available in the key table of the currently open namespace.

könnten Erkenntnisse bringen.

Mir leider nicht, da ich keine Verbindung zur Länge meines Texts erkennen konnte. Wenn ich mich richtig erinnere, war es nach clear 448 und nach putString 446, also eine Differenz von 2.

Möglicherweise ist freeEntries ja die Anzahl der freien Inhaltsverzeichniseinträge, aber nicht die Größe des verfügbaren Speichers.

Naja dann hast die Erkenntnis dass es nicht um die Keynamen Länge geht.
Die Eingangsfrage war ja auch

edit

// https://espressif-docs.readthedocs-hosted.com/projects/arduino-esp32/en/latest/api/preferences.html#freeentries

#include <Preferences.h>
Preferences preferences;

void info()
{
  Serial.print(F("preferences free:"));
  Serial.println(preferences.freeEntries());
}

void setup() {
  Serial.begin(115200);
  delay(500);
  Serial.println("\nStart ...");

  if (preferences.begin("credentials"))
  {
    Serial.println("begin erfolgreich");
  }
  info();
  if (preferences.clear())
  {
    Serial.println("clear erfolgreich");
  }
  info();
  //size_t putInt(const char* key, int32_t value)
  Serial.println(F("store a"));
  preferences.putInt("a", 42);
  info();
  Serial.println(F("store bb"));
  preferences.putInt("bb", 42);
  info();
  Serial.println(F("store ccc"));
  preferences.putInt("ccc", 42);
  info();
  Serial.println(F("store Char 1"));
  preferences.putChar("c", 'a');
  info();
  Serial.println(F("store Char 2 "));
  preferences.putChar("another char", 'a');
  info();
  Serial.println(F("store Char 3"));
  preferences.putChar("yet another char", 'a');
  info();
  Serial.println(F("store Char 4"));
  preferences.putChar("a char with a remarkable key", 'a');
  info();
  if (preferences.clear())
  {
    Serial.println("clear erfolgreich");
  }
  info();
  preferences.end();
}

void loop() {}

preferences free:454
clear erfolgreich
preferences free:454
store a
preferences free:453
store bb
preferences free:452
store ccc
preferences free:451
store Char 1
preferences free:450
store Char 2
preferences free:449
store Char 3
preferences free:449
store Char 4
preferences free:449
clear erfolgreich
preferences free:454

@basementmedia könntest bitte den Tippfehler in deinem Thread-Titel ausbessern. Danke.

Zum Aufräumen: Remove a Namespace auf ESP32 Save Data Permanently using Preferences Library.

Ich steh grad noch auf der Leitung :see_no_evil:
Welcher Tippfehler? Ich möchte nach wie vor wissen, wieviel Speicher insgesamt noch im NVS verfügbar ist, der für die Preferences-(Key-/)Wertepaare zur Verfügung steht.

freeEntries gibt mir ja nur die freien Einträge des gerade geöffneten Namespace, oder?
Aber ich muss auch zugeben, dass ich heut nicht mehr dazugekommen bin, mich komplett in den Link einzulesen. War den ganzen restlichen Tag beim Nachbar Hofplastern und bin auf der Couch zusammengeklappt :wink:

Hilf mir mal :wink:

InTutorial steht

"In this tutorial, you’ve learned how to save data permanently on the ESP32 flash memory using the Preferences.h library. This library is handy to save key:value pairs. Data held on the flash memory remains there even after resetting the ESP32 or removing power.

If you need to store bigger amounts of data or files, you should use the ESP32 filesystem (SPIFFS) or a microSD card instead:"

Verstehe das so Dein vorhandener Flashspeicher - Größe vom Programm - OTA (eventuell) - SPIFFS (eventuell)

Hab ich schon verstanden.

ja.

Die Speicherung erfolgt organisiert in Pages und Entries (innerhalb der 20KB preferences), ist auf der espressif Seite auch ausführlich beschrieben im Kapitel zu NVS Non-volatile Storage Library - ESP32 - — ESP-IDF Programming Guide v5.1.2 documentation

Da stehen dann auch Informationen ala:
String values are currently limited to 4000 bytes. This includes the null terminator. Blob values are limited to 508,000 bytes or 97.6% of the partition size - 4000 bytes, whichever is lower.

Mein letzter Testsketch zum Experimentieren:

// https://espressif-docs.readthedocs-hosted.com/projects/arduino-esp32/en/latest/api/preferences.html#freeentries
// https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/storage/nvs_flash.html

#include <Preferences.h>
Preferences preferences;

// print used entries
void info(size_t written = -1)
{
  static size_t previousValue = 0;
  size_t value = preferences.freeEntries();
  Serial.print(F("preferences free:")); Serial.print(value);
  Serial.print(F(" - used entries since last call:")); Serial.print(previousValue - value);
  if (written == 0) Serial.print(F(" - 0 byte written!"));
  Serial.println();
  previousValue = value;
}

// delete complete NVS if you have unknown namespaces (brings back around 629 free key pairs)
#include <nvs_flash.h>
void resetNVS() {
  nvs_flash_erase(); // erase the NVS partition and...
  nvs_flash_init();  // initialize the NVS partition.
  //
}

void setup() {
  Serial.begin(115200);
  delay(500);
  Serial.println("\nStart ...");
  info();
  //resetNVS(); // reset NVS once to delete all namespaces
  size_t written = 0; // written bytes
  if (preferences.begin("credentials"))
  {
    Serial.println("begin erfolgreich");
  }
  info();
  if (preferences.clear())
  {
    Serial.println("clear erfolgreich");
  }
  info();

  char name[16];
  for (int i = 1; i <= 20; i++)
  {
    strcpy (name, "auto1024 ");
    char buf[16];
    itoa(i, buf, 10);
    strcat (name, buf);
    Serial.println(F("1024 chars"));
    written = preferences.putString(name, "1234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678");
    info(written);
    if (written == 0)
    {
      Serial.print(F("write error at iteration "));
      Serial.println(i);
      break;     // lets break out of for loop, it's unlikely that next write attempt would be successful
    }
  }
  for (int i = 1; i <= 2; i++)
  {
    strcpy (name, "auto512 ");
    char buf[16];
    itoa(i, buf, 10);
    strcat (name, buf);
    Serial.println(F("512 chars"));
    size_t written = preferences.putString(name, "12345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678");
    info(written);
    if (written == 0)
    {
      Serial.print(F("write error at iteration "));
      Serial.println(i);
      break;
    }
  }
  Serial.println(F("256 chars"));
  written = preferences.putString("char256", "1234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678");
  info(written);
  Serial.println(F("128 chars"));
  written = preferences.putString("char128", "12345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678123456781234567812345678");
  info(written);
  Serial.println(F("64 chars"));
  written = preferences.putString("char64", "1234567812345678123456781234567812345678123456781234567812345678");
  info(written);
  Serial.println(F("32 chars"));
  written = preferences.putString("char32", "12345678123456781234567812345678");
  info(written);
  Serial.println(F("16 chars"));
  written = preferences.putString("my SSID", "1234567812345678");
  info(written);
  Serial.println(F("8 chars"));
  preferences.putString("my password", "12345678");
  info(written);
  Serial.println(F("4 chars"));
  preferences.putString("char4", "1234");
  info(written);
  Serial.println(F("2 chars"));
  preferences.putString("char2", "12");
  info(written);

  // this is just to fill up nvs
  /*
  for (int i = 1; i < 20; i++)
  {
    strcpy (name, "auto32 ");
    char buf[16];
    itoa(i, buf, 10);
    strcat (name, buf);
    Serial.println(F("32 chars"));
    size_t written = preferences.putString(name, "12345678123456781234567812345678");
    info(written);
    if (written == 0)
    {
      Serial.print(F("write error at iteration "));
      Serial.println(i);
      break;
    }
  }
  */

  //size_t putInt(const char* key, int32_t value)
  Serial.println(F("store a"));
  written = preferences.putInt("a", 42);
  info(written);
  Serial.println(F("store bb"));
  written = preferences.putInt("bb", 42);
  info(written);
  Serial.println(F("store ccc"));
  written = preferences.putInt("ccc", 42);
  info(written);
  Serial.println(F("store Char 1"));
  written = preferences.putChar("c", 'a');
  info(written);
  Serial.println(F("store Char 2 "));
  written = preferences.putChar("another char", 'a');
  info(written);
  Serial.println(F("store Char 3"));
  written = preferences.putChar("yet another char", 'a');
  info(written);
  Serial.println(F("store Char 4"));
  written = preferences.putChar("a char with a remarkable key", 'a');
  info(written);
  // end test and clear used namespace
  if (preferences.clear())
  {
    Serial.println("clear ok");
  }
  info();
  preferences.end();
}

void loop() {}

gibt bei mir folgendes aus

Start ...
preferences free:629 - used entries since last call:4294966667
begin erfolgreich
preferences free:629 - used entries since last call:0
clear erfolgreich
preferences free:629 - used entries since last call:0
1024 chars
preferences free:595 - used entries since last call:34
1024 chars
preferences free:561 - used entries since last call:34
1024 chars
preferences free:527 - used entries since last call:34
1024 chars
preferences free:493 - used entries since last call:34
1024 chars
preferences free:459 - used entries since last call:34
1024 chars
preferences free:425 - used entries since last call:34
1024 chars
preferences free:391 - used entries since last call:34
1024 chars
preferences free:357 - used entries since last call:34
1024 chars
preferences free:323 - used entries since last call:34
1024 chars
preferences free:289 - used entries since last call:34
1024 chars
preferences free:255 - used entries since last call:34
1024 chars
preferences free:221 - used entries since last call:34
1024 chars
preferences free:221 - used entries since last call:0 - 0 byte written!
write error at iteration 13
512 chars
preferences free:203 - used entries since last call:18
512 chars
preferences free:185 - used entries since last call:18
256 chars
preferences free:175 - used entries since last call:10
128 chars
preferences free:169 - used entries since last call:6
64 chars
preferences free:165 - used entries since last call:4
32 chars
preferences free:162 - used entries since last call:3
16 chars
preferences free:160 - used entries since last call:2
8 chars
preferences free:158 - used entries since last call:2
4 chars
preferences free:156 - used entries since last call:2
2 chars
preferences free:154 - used entries since last call:2
store a
preferences free:153 - used entries since last call:1
store bb
preferences free:152 - used entries since last call:1
store ccc
preferences free:151 - used entries since last call:1
store Char 1
preferences free:150 - used entries since last call:1
store Char 2 
preferences free:149 - used entries since last call:1
store Char 3
preferences free:149 - used entries since last call:0 - 0 byte written!
store Char 4
preferences free:149 - used entries since last call:0 - 0 byte written!
clear ok
preferences free:629 - used entries since last call:4294966816

Was wie viele Entries verbraucht, oder wann ein Schreiben fehl schlägt ist schon interessant.

Was ich mir aktuell denke:

  • man hat Anfangs 629 Keypaare
  • man bekommt den 20K Speicher mit großen Strings voll, ob das aber noch reale Relevanz hat, muss jeder selber entscheiden. Im Beispiel habe ich z.B. 12 mal 1kB Strings schreiben können. Wie viele derartige Wertpaare braucht man denn wirklich in einer Applikation *)
  • es ist ratsam auf die Free Entries zu achten
  • man soll überprüfen ob das Speichern erfolgreich war, bei großen Anwendungen auf alle Fälle.

*) OT:
Genausowenig wie man "640 kB ought to be enough for anybody" Gates zuschreiben darf, sag ich auch nicht dass 20kB preferences für jeden Anwendungsfall genug sind ... aber fürs durchschnittliche LED Geblinke wirds oft reichen.

Vielen Dank für die ausführliche Hilfe :hugs:

Ich plane, die preferences für Daten wie z.B. Zugangsdaten und Einstellwerte zu verwenden. Dafür finde ich das ideal. Ich glaub sogar du oder jemand anderes aus dem Forum haben mich erst drauf gebracht. Vorher hatte ich das mit SPIFFS erledigt.

um auf Post #7 zurückzukommen: Wenn, dann hab dir vermutlich geraten die

Preferences

zu nutzen. Nicht die

Prferences

Ah, da haben wir den Tippfehler. Auf die Überschrift hatte ich gar nicht mehr geschaut

Dieses Forum wird vielfach nur lesend genutzt, das sehe ich an der Verleihung "beliebter Link". Da ist das Auffinden per richtig geschriebenem Stchwort schon wichtig. Da dieses Thema ein paar gute Infos enthält, ist m. E. die Korrektur die Mühe wert.

Daher Danke!