ESP32 Save Data Permanently using Preferences Library

This guide shows how to save data permanently on the ESP32 flash memory using the Preferences.h library. The data held in the flash memory persists across resets or power failures. Using the Preferences.h library is useful to save data like network credentials, API keys, threshold values, or even the last state of a GPIO. You’ll learn how to save and read data from flash memory.

ESP32 Save Data Permanently using the Preferences Library

In this tutorial, we’ll cover the following topics:

Preferences.h Library

In a previous tutorial, we recommended using the EEPROM library to save data on flash memory. However, the EEPROM library is deprecated in favor of the Preferences.h library. This library is “installed” automatically when you install the ESP32 boards in your Arduino IDE.

The Preferences.h library is preferably used to store variable values through key:value pairs. Saving data permanently can be important to:

  • remember the last state of a variable;
  • save settings;
  • save how many times an appliance was activated;
  • or any other data type you need to save permanently.

If, instead of variables, you need to save files on the ESP32, we recommend using the filesystem (SPIFFS) instead. To learn how to save files in the ESP32 filesystem, you can read one of the following tutorials:

Save Data Using Preferences.h Library

The data saved using preferences is structured like this:

namespace {
  key:value
}

You can save different keys on the same namespace, for example:

namespace {
  key1: value1
  key2: value2
}

In a practical example, this configuration could be used to save your network credentials:

credentials {
  ssid: "your_ssid"
  pass: "your_pass"
}

In the preceding example, credentials is the namespace, and ssid and pass are the keys.

You can also have multiple namespaces with the same key (but each key with its value):

namespace1{
  key:value1
}
namespace2{
  key:value2
}

When using the Preferences.h library, you should define the data type you want to save. Later, if you want to read that data, you must know the saved data type. In other words, the data type of writing and reading should be the same.

You can save the following data types using Preferences.h: char, Uchar, short, Ushort, int, Uint, long, Ulong, long64, Ulong64, float, double, bool, string and bytes.

For more information, you can access the Preferences.cpp file here.

Preferences.h Library Useful Functions

To use the Preferences.h library to store data, first you need to include it in your sketch:

#include <Preferences.h>

Then, you must initiate an instance of the Preferences library. You can call it preferences, for example:

Preferences preferences;

After this, you can use the following methods to handle data using the Preferences.h library.

Start Preferences

The begin() method opens a “storage space” with a defined namespace. The false argument means that we’ll use it in read/write mode. Use true to open or create the namespace in read-only mode.

preferences.begin("my-app", false); 

In this case, the namespace name is my-app. Namespace name is limited to 15 characters.

Clear Preferences

Use clear() to clear all preferences under the opened namespace (it doesn’t delete the namespace):

preferences.clear();

Remove Key

Remove a key from the opened namespace:

preferences.remove(key);

Close Preferences

Use the end() method to close the preferences under the opened namespace:

preferences.end();

Put a Key Value (Save a value)

You should use different methods depending on the variable type you want to save.

CharputChar(const char* key, int8_t value)
Unsigned CharputUChar(const char* key, int8_t value)
ShortputShort(const char* key, int16_t value)
Unsigned ShortputUShort(const char* key, uint16_t value)
IntputInt(const char* key, int32_t value)
Unsigned IntputUInt(const char* key, uint32_t value)
LongputLong(const char* key, int32_t value)
Unsigned LongputULong(const char* key, uint32_t value)
Long64putLong64(const char* key, int64_t value)
Unsigned Long64putULong64(const char* key, uint64_t value)
FloatputFloat(const char* key, const float_t value)
DoubleputDouble(const char* key, const double_t value)
BoolputBool(const char* key, const bool value)
StringputString(const char* key, const String value)
BytesputBytes(const char* key, const void* value, size_t len)

Get a Key Value (Read Value)

Similarly, you should use different methods depending on the variable type you want to get.

ChargetChar(const char* key, const int8_t defaultValue)
Unsigned ChargetUChar(const char* key, const uint8_t defaultValue)
ShortgetShort(const char* key, const int16_t defaultValue
Unsigned ShortgetUShort(const char* key, const uint16_t defaultValue)
IntgetInt(const char* key, const int32_t defaultValue)
Unsigned IntgetUInt(const char* key, const uint32_t defaultValue)
LonggetLong(const char* key, const int32_t defaultValue)
Unsigned LonggetULong(const char* key, const uint32_t defaultValue)
Long64getLong64(const char* key, const int64_t defaultValue)
Unsigned Long64gettULong64(const char* key, const uint64_t defaultValue)
FloatgetFloat(const char* key, const float_t defaultValue)
DoublegetDouble(const char* key, const double_t defaultValue)
BoolgetBool(const char* key, const bool defaultValue)
StringgetString(const char* key, const String defaultValue)
StringgetString(const char* key, char* value, const size_t maxLen)
BytesgetBytes(const char* key, void * buf, size_t maxLen)

Remove a Namespace

In the Arduino implementation of Preferences, there is no method of completely removing a namespace. As a result, over the course of several projects, the ESP32 non-volatile storage (nvs) Preferences partition may become full. To completely erase and reformat the NVS memory used by Preferences, create a sketch that contains:

#include <nvs_flash.h>

void setup() {
  nvs_flash_erase(); // erase the NVS partition and...
  nvs_flash_init(); // initialize the NVS partition.
  while(true);
}

void loop() {

}

You should download a new sketch to your board immediately after running the above, or it will reformat the NVS partition every time it is powered up.

Preferences.h – Save key:value Pairs

For a simple example on how to save and get data using Preferences.h, in your Arduino IDE, go to File > Examples > Preferences > StartCounter.

/*
 ESP32 startup counter example with Preferences library.

 This simple example demonstrates using the Preferences library to store how many times the ESP32 module has booted. 
 The Preferences library is a wrapper around the Non-volatile storage on ESP32 processor.

 created for arduino-esp32 09 Feb 2017 by Martin Sloup (Arcao)
 
 Complete project details at https://RandomNerdTutorials.com/esp32-save-data-permanently-preferences/
*/

#include <Preferences.h>

Preferences preferences;

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

  // Open Preferences with my-app namespace. Each application module, library, etc
  // has to use a namespace name to prevent key name collisions. We will open storage in
  // RW-mode (second parameter has to be false).
  // Note: Namespace name is limited to 15 chars.
  preferences.begin("my-app", false);

  // Remove all preferences under the opened namespace
  //preferences.clear();

  // Or remove the counter key only
  //preferences.remove("counter");

  // Get the counter value, if the key does not exist, return a default value of 0
  // Note: Key name is limited to 15 chars.
  unsigned int counter = preferences.getUInt("counter", 0);

  // Increase counter by 1
  counter++;

  // Print the counter to Serial Monitor
  Serial.printf("Current counter value: %u\n", counter);

  // Store the counter to the Preferences
  preferences.putUInt("counter", counter);

  // Close the Preferences
  preferences.end();

  // Wait 10 seconds
  Serial.println("Restarting in 10 seconds...");
  delay(10000);

  // Restart ESP
  ESP.restart();
}

void loop() {

}

View raw code

This example increases a variable called counter between resets. This illustrates that the ESP32 “remembers” the value even after a reset.

Upload the previous sketch to your ESP32 board. Open the Serial Monitor at a baud rate of 115200 and press the on-board RST button. You should see the counter variable increasing between resets.

ESP32 Preferences StartCounter Example Serial Monitor

How the Code Works

This example uses the functions we’ve seen in the previous sections.

First, include the Preferences.h library.

#include <Preferences.h>

Then, create an instance of the library called preferences.

Preferences preferences;

In the setup(), initialize the Serial Monitor at a baud rate of 115200.

Serial.begin(115200);

Create a “storage space” in the flash memory called my-app in read/write mode. You can give it any other name.

preferences.begin("my-app", false);

Get the value of the counter key saved on preferences. If it doesn’t find any value, it returns 0 by default (which happens when this code runs for the first time).

unsigned int counter = preferences.getUInt("counter", 0);

The counter variable is increased one unit every time the ESP runs:

counter++;

Print the value of the counter variable:

Serial.printf("Current counter value: %u\n", counter);

Store the new value on the “counter” key:

preferences.putUInt("counter", counter);

Close the Preferences.

preferences.end();

Finally, restart the ESP32 board:

ESP.restart();

ESP32 – Save/Read Network Credentials using the Preferences.h Library

The Preferences.h library is many times used to save your network credentials permanently on the flash memory. This way, you don’t have to hard code the credentials in every sketch that involves connecting the ESP32 to the internet.

In this section, we’ll show you two simple sketches that might be useful in your projects:

To learn more about ESP32 Wi-Fi related functions, read the following article:

Save Network Credentials using Preferences.h

The following sketch saves your network credentials permanently on the ESP32 flash memory using Preferences.h.

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-save-data-permanently-preferences/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/

#include <Preferences.h>

Preferences preferences;

const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

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

  preferences.begin("credentials", false);
  preferences.putString("ssid", ssid); 
  preferences.putString("password", password);

  Serial.println("Network Credentials Saved using Preferences");

  preferences.end();
}

void loop() {

}

View raw code

Don’t forget to insert your network credentials in the following variables:

const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

How the Code Works

Let’s take a quick look at the relevant parts of the code for this example.

In the setup(), create a new storage space on the flash memory with the credentials namespace.

preferences.begin("credentials", false);

Then, create a key called ssid that saves your SSID value (ssid variable) – use the putString() method.

preferences.putString("ssid", ssid); 

Add another key called password to save the password value (password variable):

preferences.putString("password", password);

So, your data is structured in this way:

credentials{
  ssid: your_ssid
  password: your_password
}

Upload the code to your board and this is what you should get on the Serial Monitor:

ESP32 Save Network Credentials using Preferences Library

In the following example, we’ll show you how to read the network credentials from preferences and use them to connect the ESP32 to your network.

Connect to Wi-Fi with Network Credentials Saved on Preferences

The following sketch gets the network credentials’ values and connects to your network using those credentials.

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-save-data-permanently-preferences/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/

#include <Preferences.h>
#include "WiFi.h"

Preferences preferences;

String ssid;
String password;

void setup() {
  Serial.begin(115200);
  Serial.println();
  
  preferences.begin("credentials", false);
 
  ssid = preferences.getString("ssid", ""); 
  password = preferences.getString("password", "");

  if (ssid == "" || password == ""){
    Serial.println("No values saved for ssid or password");
  }
  else {
    // Connect to Wi-Fi
    WiFi.mode(WIFI_STA);
    WiFi.begin(ssid.c_str(), password.c_str());
    Serial.print("Connecting to WiFi ..");
    while (WiFi.status() != WL_CONNECTED) {
      Serial.print('.');
      delay(1000);
    }
    Serial.println(WiFi.localIP());  
  }
}

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

View raw code

How the Code Works

Let’s take a quick look at the relevant parts of the code for this example.

Open the credentials namespace:

preferences.begin("credentials", false);

Get the SSID and password values using the getString() method. You need to use the key name that you used to save the variables, in this case, ssid and password keys:

ssid = preferences.getString("ssid", ""); 
password = preferences.getString("password", "");

As a second argument to the getString() function, we passed an empty String. This is the returned value in case there aren’t ssid or password keys saved on preferences.

If that’s the case, we print a message indicating that there aren’t any saved values:

if (ssid == "" || password == ""){
  Serial.println("No values saved for ssid or password");
}

Otherwise, we connect to Wi-Fi using the SSID and password saved on preferences.

else {
  // Connect to Wi-Fi
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid.c_str(), password.c_str());
  Serial.print("Connecting to WiFi ..");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(1000);
  }
  Serial.println(WiFi.localIP());
}

Upload this code to your board after the previous one (to ensure that you have the credentials saved). If everything goes as expected, this is what you should get on your Serial Monitor.

ESP32 Connect to Network with Credentials Saved on Preferences

Remember Last GPIO State After RESET

Another application of the Preferences.h library is to save the last state of an output. For example, imagine the following scenario:

  1. You’re controlling an output with the ESP32;
  2. You set your output to turn on;
  3. The ESP32 suddenly loses power;
  4. When the power comes back on, the output stays off – because it didn’t keep its last state.
ESP32 Preferences Library Remember Last GPIO State (without preferences)

You don’t want this to happen. You want the ESP32 to remember what was happening before losing power and return to the last state.

To solve this problem, you can save the lamp’s state in the flash memory. Then, you need to add a condition at the beginning of your sketch to check the last lamp state and turn the lamp on or off accordingly.

The following figure shows what we’re going to do:

ESP32 Preferences Library Remember Last GPIO State

We’ll show you an example using an LED and a pushbutton. The pushbutton controls the LED state. The LED keeps its state between resets. This means that if the LED is lit when you remove power, it will be lit when it gets powered again.

Schematic Diagram

Wire a pushbutton and an LED to the ESP32 as shown in the following schematic diagram.

ESP32 Schematics diagram circuit LED output pushbutton input

Recommended reading: ESP32 Pinout Reference: Which GPIO pins should you use?

Code

This is a debounce code that changes the LED state every time you press the pushbutton. But there’s something special about this code – it remembers the last LED state, even after resetting or removing power from the ESP32. This is possible because we save the led state on Preferences whenever it changes.

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp32-save-data-permanently-preferences/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/

#include <Preferences.h>

Preferences preferences;

const int buttonPin = 4;    
const int ledPin = 5;      

bool ledState;         
bool buttonState;             
int lastButtonState = LOW;

unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long debounceDelay = 50;    // the debounce time; increase if the output flickers

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

  //Create a namespace called "gpio"
  preferences.begin("gpio", false);

  pinMode(buttonPin, INPUT);
  pinMode(ledPin, OUTPUT);

  // read the last LED state from flash memory
  ledState = preferences.getBool("state", false); 
  Serial.printf("LED state before reset: %d \n", ledState);
  // set the LED to the last stored state
  digitalWrite(ledPin, ledState);
}

void loop() {
  int reading = digitalRead(buttonPin);

  if (reading != lastButtonState) {
    lastDebounceTime = millis();
  }
  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (reading != buttonState) {
      buttonState = reading;
      if (buttonState == HIGH) {
        ledState = !ledState;
      }
    }
  }
  lastButtonState = reading;
  if (digitalRead(ledPin)!= ledState) {  
    Serial.println("State changed");
    // change the LED state
    digitalWrite(ledPin, ledState);
    
    // save the LED state in flash memory
    preferences.putBool("state", ledState);
    
    Serial.printf("State saved: %d \n", ledState);
  }
}

View raw code

How the Code Works

Let’s take a quick look at the relevant parts of code for this example.

In the setup(), start by creating a section in the flash memory to save the GPIO state. In this example, we’ve called it gpio.

preferences.begin("gpio", false);

Get the GPIO state saved on Preferences on the state key. It is a boolean variable, so use the getBool() function. If there isn’t any state key yet (which happens when the ESP32 first runs), return false (the LED will be off).

ledState = preferences.getBool("state", false); 

Print the state and set the LED to the right state:

Serial.printf("LED state before reset: %d \n", ledState);
// set the LED to the last stored state
digitalWrite(ledPin, ledState);

Finally, in the loop() update the state key on Preferences whenever there’s a change.

// save the LED state in flash memory
preferences.putBool("state", ledState);

Serial.printf("State saved: %d \n", ledState);

Demonstration

Upload the code to your board and wire the circuit. Open the Serial Monitor at a baud rate of 115200 and press the on-board RST button.

Press the pushbutton to change the LED state and then remove power or press the RST button.

ESP32 Schematics diagram circuit LED output pushbutton input demonstration testing

When the ESP32 restarts, it will read the last state saved on Preferences and set the LED to that state. It also prints a message on the Serial Monitor whenever there’s a change on the GPIO state.

ESP32 Remember Last LED State Preferences

Wrapping Up

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:

We hope you’ve found this tutorial useful.

Learn more about the ESP32 with our resources:



Build Web Server projects with the ESP32 and ESP8266 boards to control outputs and monitor sensors remotely. Learn HTML, CSS, JavaScript and client-server communication protocols DOWNLOAD »

Build Web Server projects with the ESP32 and ESP8266 boards to control outputs and monitor sensors remotely. Learn HTML, CSS, JavaScript and client-server communication protocols DOWNLOAD »


Enjoyed this project? Stay updated by subscribing our newsletter!

48 thoughts on “ESP32 Save Data Permanently using Preferences Library”

  1. Hi Rui and all,
    First, good post! A clear explanation of Preferences and how to use them (especially when preserving WiFi credentials, ESP-NOW node ID information, and so on).
    I used Preferences in a large ESP32 project a while back. It is useful if you have a small’ish number of data items that need to be stored.
    However, I had around a half-dozen or so larger structures, each with a dozen or more fields. I maintained the half-dozen or so keys, using putBytes/getBytes to save/restore data.
    As the project grew, this became cumbersome. New versions of the project had additional data, and I could not use Preferences to maintain stored-data integrity as I moved to new versions (with the larger data blobs). I eventually switched over to SPIFFS and ArduinoJSON. This way, newer code could autodetect missing data, supply defaults, and then save the updated JSON data for later runs.
    For simpler projects, Preferences works well. For larger projects, RandomNerd readers may wish to look into SPIFFS/JSON as a more flexible alternative.
    -Steve

    Reply
    • Hi Steve.
      Thanks for your comment.
      That’s right. Preferences is good to save a small number of values.
      If you need to save huge amounts of data, it is preferable to create a file in SPIFFS and use JSON.
      Thanks for following our work.
      Regards,
      Sara

      Reply
  2. With EEPROM, we know how much space was allotted. I think it was 500 bytes or 1K. Then, we specified how memory memory we needed. That gave confidence we would not overrun the space and screw up something else, like our program. Is there any such protection with Preferences? What if we save 1000 variables (exaggeration, of course)?

    Reply
  3. Nice tutorial. I hadn’t heard of Preferences.h. It is a wrapper on the Espressif NVS API, which has a few more features. The NVS documentation has a good explanation of how this works: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/storage/nvs_flash.html

    NVS is optimized to store a set of key:value pairs grouped by namespace, and written into flash by appending in 4096 byte pages. When a new value is written the prior value is marked deleted, and new value appended. This way it doesn’t wear out the flash. A RAM hash table helps search for entries (128-640 bytes of RAM per 4096 byte flash page). It’s mainly intended for short values.

    The main difference between Preferences.h and the NVS api is an automatic commit for each write and has error checking and logging. That doesn’t seem to be explained in the Preferences.h file.

    I also just learned about the Arduino error logging, log_e()..log_v(), used by Preferences.cpp. Rui & Sara maybe that would make a nice additional tutorial to explain how logging works and how to control the level selected at compile time. It seems nontrivial, since stdout can be redefined and selecting log levels is slightly different in PlatformIO than basic Arduino IDE.

    Note you can still use JSON with the NVS, storing the JSON as a string or blob. I haven’t figured out the SPIFFS format, but depending on the JSON size, partition sizes, and write frequency, NVS could be better. [Note you can also select the partition in the begin().] NVS writes 32 byte chunks vs 256 byte pages with SPIFFS. Both wear level across 4096 byte blocks in the partition.

    Reply
    • Muy buena aportación Carl Hage !!!!

      Lo tendré en cuenta, sobre todo en el tema de la encriptación para todo el tipo de credenciales en donde veo que NVS mejora a Preferences.

      Para casos simples de variables de estado creo que Preferences es más simple de utilizar ¿no?

      Reply
  4. Hi.

    As usual another nice tutorial was born. Thank you Sara & Rui for your efforts and for the space you provide here for all of us.

    I like key-value access way on this library. I like the automatic variable type recognition, background eeprom addressing. All it’s methods seem simple and useful. But what more about very frequent writes?

    Someone may be concerned about the life and durability of the flash. There are very frequent writes to “eeprom” required also in my projects. Maybe I don’t understand well and maybe I am not alone, so let me have a few questions.

    Carl, you said “This way it doesn’t wear out the flash.” But how is it possible, when there is a commit after each write?

    Carl, did you you mean that the commit will be done only and only on the “4096 byte flash page” (meaning namespace?) and not on the whole partition from the namespace comes? If so, would it be enough to separate commit (remove commit from every write method) and execute it separately? I am not very sure how Preferences lib (nvm.h) shares flash partition.

    The problem of memory life can be solved by rotating partitions using EEPROM32_Rotate library by Xose Péres. Details here https://tinkerman.cat/post/eeprom-rotation-for-esp8266-and-esp32/
    Unfortunately this nice library as it is, despite that it shares same library (nvs.h), it does not allow key-value access. Also it does not support automatic variable type recognition, does not support background addressing in flash space and so on. Please correct me if I am wrong … On the other side this library inherits all well known methods from famous standard library EEPROM.h. And very imortant for me is, that there the commit method may be called once after all data have been prepared previously (written in buffer). So this way does not wear out the flash, because each additional commit reduces it’s life.

    In this sense back to Preferences library.
    Is it possible to consider each open namespace in Preference as a “partition” on which and only which a commit will be performed? If so, memory wear out can be solved by using multiple namespaces and by switching (rotating) between them.

    I am not sure if it works this way. Can anyone answer that, please?

    Reply
  5. Interesting timing. I just finished a project requiring a deep dive into Preferences and was going to suggest a new tutorial.

    There are number of subtle things not really explained well in the examples nor in the official Espressif documentation.

    Pretences workflow, once everything is initialized, is pretty simple.

    To get a value: Open the namespace in read-only mode. Get the value using a given key. Close the namespace.
    To put a value: Open the namespace in read/write mode. Put the value into the key. Close the namespace.

    Ensuring that the data types of your “gets” and “puts” all match, you’re good to go.

    The nuances are in initializing everything at the start.

    Before you can write or read anything using Preferences, both the namespace and the key within that namespace need to exist. So the initialization workflow is:

    Create the namespace.
    Test for the initial existence of your key(s).
    If the key(s) don’t exist, create the key(s) within that namespace.
    Once the key(s) are created, put any required default values into the key(s).
    Close the namespace.
    Carry on with the rest of your sketch.

    To elaborate:

    Create (or open) the namespace.

    In the preamble of your sketch insert a declaration of a Preferences object by including a line like;

    Preferences mySketchPrefs; // "mySketchPrefs" is the name of the Preferences object. Can be whatever you want.

    The namespace is created using the ".begin" method:

    mySketchPrefs.begin("myPrefs", false); // This creates the namespace "myPrefs" if it doesn't exist or opens the namespace "myPrefs" if it does exist.
    // The second argument is "false" to open or create the namespace in read/write mode or "true" to open or create the namespace in read-only mode.

    Test for initial existence of your key(s).

    The ".putX" methods, and the values they return, actually do a few things depending how they are called. (Here "X" is one of the Preferences data types; Bool, UInt, Char, etc.)

    Assume we have a key named "chocolate" that holds a value of a boolean data type.

    If we do a:

    mySketchPrefs.putBool("chocolate", false);

    One of two things will happen.

    If the key already exists in the opened namespace, the value "false" will be written into the value associated with that key and the function returns with a "status" code.

    If the key "chocolate" does not already exist, it will be created, and the function will return with a value of "false" but no value will actually be written into the value associated with that key.

    That is, any ".getX" method of the form:

    preferences.getX("keyname", default);

    will return either the value associated with "keyname" if "keyname" exists or, if "keyname" does not yet exist in the opened namespace, create the key "keyname" and then return the value "default". ("default" is of the same data type as "X".)

    Why would it do that? So we can determine if this is the very very first time the sketch has been run or if we have run the sketch previously. That is why we should test first whether the key:value pairs exist and if not, we know we are in a first-time run and therefore we need to create all the key:value pairs we need and put default values into each key.

    By example:

    bool doesExist;
    mySketchPrefs.begin("myPrefs", false); // open (or create if it does not yet exist) the namespace "myPrefs".
    doesExist = mySketchPrefs.getInt("chocolate", false)
    if (doesExist == false) {
    // this is the very first time through the sketch and so we will need to create the key(s) we need before we can put values into them. Insert your key create & assign values code here.
    ;
    }
    // After your setup code, set the test key to a value that can only be true (or hold some unique value) if we've done the setup stuff before...
    mySketchPrefs.putInt("chocolate", true) // now the "chocolate" key has been created, and set to "true" so we know we'll know next time we power up we've already done this part.

    If after the statement;

    doesExist = mySketchPrefs.getInt("chocolate", 0)

    "doesExist" is "true" then we know we previously created and initialized all our key:value pairs.

    To create any key(s) we need, we can use the other form of the ".getX" methods:

    preferences.getX("keyname");

    In this form, we are only creating the key "keyname" in the opened namespace. We can then later use the ".putX" methods to assign a value to "keyname".

    So the TLDR; version is: You cannot write to or read from a key:value pair until the key exists in the opened namespace. If the key already exits, chances are you already created it the first time the sketch was run.

    Real world example:

    #include <Preferences.h>
    Preferences stcPrefs; // holds the operational parameters in NVS for retention across power cycles.

    void setup() { // not a complete setup(), but in the setup() function, include this...
    stcPrefs.begin("STCPrefs", true); // open our preferences in R/O mode (also creates the namespace if it doesn't exist)
    tpInit = stcPrefs.getBool("nvsInit", false); // test for existing name space being initialized (also creates the nvsInit key
    // if it doesn't exist)

    if (tpInit == false) { // if tpInit didn't exist, this is our first time run. Need to set up our preferences.
    stcPrefs.end(); // close the namespace in R/O mode and...
    stcPrefs.begin("STCPrefs", false); // reopen it in R/W mode

    // need to create the namespace key entries first by 'getting' the key.
    stcPrefs.getUChar("curBright");
    stcPrefs.getString("talChan");
    stcPrefs.getUChar("talMax");
    stcPrefs.getBool("ctMde");

    stcPrefs.putUChar("curBright", 10); // now store the initial "factory default" values
    stcPrefs.putString("talChan", "1");
    stcPrefs.putUChar("talMax", 6);
    stcPrefs.putBool("ctMde", true);
    stcPrefs.putBool("nvsInit", true); // set the iniitlaization done flag...
    stcPrefs.end(); // and close the namespace
    }

    currentBrightness = stcPrefs.getUChar("curBright"); // read and set the operational parameters from NVS
    tChannel = stcPrefs.getString("talChan"); // these variables and structures are defined earlier in the sketch.
    tChanMax = stcPrefs.getUChar("talMax");
    ctMode = stcPrefs.getBool("ctMde");
    stcPrefs.end(); // all done. Close our preferences namespace
    }

    Note that identifiers (labels or names) for both the namespace and keys can only be a maximum of 15 characters.

    There are a couple of other functions that are useful when working with namespaces.

    preferences.clear(); // erases all the key:value pairs in the currently opened namespace. The namespace still exists.
    preferences.remove("keyname") // erases the "keyname" and value associated with it from the currently opened namespace.

    If either of the above are used, the key:value pair will need to be created before using it again.

    Advanced item:

    *** WARNING THIS COULD BE ABUSED. PROCEED WITH CAUTION ***
    In the Arduino implementation of Preferences there is no method of completely removing a namespace. As a result, over the course of a number of projects, it is possible that the esp32 non volatile storage (nvs) Preferences partition becomes full. To completely erase and reformat the NVS memory used by Preferences, create a sketch that contains:

    #include <nvs_flash.h>

    void setup() {
    nvs_flash_erase(); // erase the NVS partition and...
    nvs_flash_init(); // initialize the NVS partition.
    while(true);
    }

    void loop() {
    ;
    }

    *** You should download a new sketch to your board immediately after running the above or else it will reformat the NVS partition every time it is powered up. ***

    Reply
    • Hi.
      Thank you so much for sharing your knowledge.
      I’ve updated the tutorial with some of your tips.
      Thanks for taking the time to do this.
      Regards,
      Sara

      Reply
      • Hi Sara. Didn’t realize that markdown is supported by default when posting. Is there a way to edit a post so I can clean up the formatting? Thanks.

        Reply
        • Hey, quick note to say I just read through your long comment and found it very useful. Basically most of the way through reading the article I was thinking “yeh this is great but it’s a bit simple – I’ll definitely need to have some way of writing initial factory default values to memory, which also means I will need to create a bool flag called firstRun or somesuch”. Not difficult in any way but was really useful to see all your detailed comments. Must have taken a good couple of hours writing that comment – it definitely helps me as I’m writing my first firmware for an ESP32 and trying to figure these things out. Cheers!

          Reply
          • Mat, glad it was of use. Was also writing my first Arduino sketch at the time and figured this might help others fill a few gaps I found in the docs. All the best.

  6. I great tutorial for the ESP32. However, because of it’s very small size, I use Node Mini ESP8266 quite often. Does Preferences work on ESP8266?

    Thx, and keep up the great work,
    P

    Reply
  7. How would you use this to store an array of values? It will be 30 x int’s of values between 1 and 500 (approx) that over a period of 30 days each value gets updated/replaced to create a “last 30 days” average?

    Reply
    • If you have ‘int myArr[30]’, you can use ‘myPrefs.putBytes(“tag”, myArr, 30*sizeof(int))’.

      I usually use a typedef for the array, and just use sizeof(theType) instead of the 30*sizeof(int).

      Reply
      • Thank you Steve,
        I’ve got a bit of learning / testing to do to see if I can get this to work. Very much appreciate your guidance.

        I used tinkercad to figure out how to do a “run-once” when the first usage sample is calculated which then populates the averaging array thereafter we step along the array each write and update.

        This does create a new question though, if after the first full set of array usage values and have a complete 30 day average and want to store that as a historical value would it be better to do all the storage in “SPIFFS” or do the averaging in “Preferences” and the logging in “SPIFFS”?

        Cheers

        Reply
        • Hi Ralph,

          I don’t think you can use SPIFFS and Preferences at the same time. I haven’t examined the driver code, but I imagine they read/write raw(ish) flash blocks. (Same with FATFS vs SPIFFS, both use the raw flash space.)

          That’s why I moved to SPIFFS from Preferences for the near term. I may move to FAT at some point, but at least the API is the same!

          Does your node have WiFi access? If so, you can connect, grab the date from an NTP server, and use this to construct the archival filenames under SPIFFS or FAT (at least as far as filename length restrictions). If you’ve done this, you can also set up the real time clock on the ESP as well, should you need to timestamp the datapoints in the data file.

          -Steve

          Reply
          • Sorry Steve, I missed your reply for some reason and I’ve been sidetracked with other things so my progress stopped for a while. Back to it now though………………. After some investigating I found out why no reply, my bad. Should be OK now.

            As you have probably already gathered I’m just a hobbyist and am self learning so sometimes things take me a while to understand.

            Thanks for the compatibility warning.

            Yes I already have NTP running on a weather prediction node that has been running for some time now to debug the modifications I did to the original sketch. The goal is to pass outdoor temp/humidity/water tank level data sent from remote remote node. It’s the remote node where I want to save the array. The remote node will be run taking advantage of “Deep Sleep” power saving and I was thinking of getting it running as a stand-alone “Blynk” instance first to aid development hence the need to save the array. I’ve already got the “Blynk” side running just not with the array and “Deep Sleep”.

            Right or wrong I find it easier to work things out by building in modules and then when each works reliably I combine them.

            Cheers
            Ralph

  8. Hii,
    is it possible to replace “state” in below line with var?
    I try to make recursive routine to get some key
    ex: “state”+ String(i),
    I try but still fail

    ledState = preferences.getBool(“state”, false);

    Reply
      • I am sorry Sara,
        I want to read the bool value from key in namespace.

        bool a;
        for (int x = 1; x < 4 ;x++){
        a = preferences.getBool(“Var_A” + String(x), false);
        Serial.Println(a);
        }

        the will give the read value
        Var_A1
        Var_A2
        Var_A3
        Var_A4

        Reply
        • Hi Sara,
          I already found the solution, thanks for the good tutorial

          for (int x = 1; x < DO_n+1;x++){
          a = preferences.getBool(("AM_S_CH"+String(x)).c_str(), false);
          b = preferences.getBool(("AM_C_CH"+String(x)).c_str(), false);
          c = preferences.getBool(("SCH_"+String(preferences.getInt(("SCH_CH"+String(x)+"_D"+String(c_day)).c_str()))).c_str(), false);
          d = preferences.getBool(("DI1_En_CH"+String(x)).c_str(), false);
          e = preferences.getBool(("DI2_En_CH"+String(x)).c_str(), false);
          f = preferences.getBool(("DI3_En_CH"+String(x)).c_str(), false);

          Reply
          • Hi. Might be a couple of things to check. Outside of this snippet, I’ll assume the namespace was opened with a preferences.begin("namespace", true); statement? Also assuming this namespace has keys created elsewhere that match the descriptions in your statements a = thought to f =?

            When using the preferences.getXXX("keyname", default); form, default is returned if "keyname" does not exist in the currently opened namespace. If you’re sure "keyname" exists, use the preferences.getXXX("keyname"); form.

            Also, keynames are limited to 13 characters maximum. The assignment for c = above might be problematic in that regard.

            There is a pretty detailed explanation in this comment above: https://randomnerdtutorials.com/esp32-save-data-permanently-preferences/#comment-566327

  9. Dear Xylopyrographer,
    the preferences.begin(“namespace”, true); already define above ,
    I just copy some off code,

    thanks for the explanation,

    by the way,
    – is there any way to list/print the available namespace and keys ?
    – and is there any keys limit number we can create?

    Reply
    • It looks like there is a way to list what is in a namespace. That facility isn’t supported in the ESP32 Arduino Preferences implementation but is available using the ESP32 function calls directly. See:
      https://docs.espressif.com/projects/esp-idf/en/release-v4.1/api-reference/storage/nvs_flash.html

      and look near the bottom of that page for the nvs_entry_find() method.

      If I recall the default NVS partition is made up of 4 pages of 4096 bytes each. So that makes about 16k of NVS storage available. This is the limit, not really the number of entries. From that same link, there are the methods that can be used to determine the amount of NVS available.

      In your sketch,

      #include nvs.h
      #include nvs_flash.h

      and you’ll be able to access these methods.

      Reply
      • I try to write 1440 keys with bool type
        but only 500 can be write, then the nvs become hang, cannot write anything, should be format again.
        with i = 1440, is there any other method to store so much keys, >?

        for (int x = 0;x < i;x++){
        Serial.print(“Address : “);
        Serial.print(x);
        Serial.print(” Tags : “);
        Serial.print(“SCH_”+b+”“+String(g+x));
        Serial.print(” yang ditulis : “);
        Serial.println(j);
        preferences.putBool((“SCH
        “+b+”_”+String(g+x)).c_str(),j);
        }
        preferences.end();//Close the Preferences.
        Serial.println(“jadwal berhasil diupdate”);}

        Reply
        • I’d suggest taking a look at this line:

          preferences.putBool((“SCH“+b+”_”+String(g+x)).c_str(),j);

          A namespace key can be a maximum of 15 characters. Is there a chance this could cause that limit to be exceeded? The .putX(); functions will fail if passed a key name longer than 15 characters. (The fail is graceful–writing an entry to the log. The function call returns 0. No value is written.)

          Are all the key names created before the sketch tries to write to them?

          If that isn’t an issue, would suggest writing a test sketch that creates a namespace and then creates the 1440 key names, and then populates them but in a simple format like:

          "A" + String(i)

          so that it’s certain that the 15 character limit won’t be exceeded.

          You could also:

          test the return value after doing a preferences.putBool(). That set of functions returns 0 if the write failed or 1 if successful.
          test the length of the key name to ensure its length is no greater than 15 characters before attempting to do a put.

          Bool’s are written to NVS as uint8_t data type. You could use that t odd a bit of math on the space required for your values.

          Hope that helps.

          Reply
  10. When should I use preferences.end()? There were examples that this command was used and in other examples it was not used.

    Reply
    • Hi Eduardo.
      It is mandatory to call preferences.end(), if you want to open preferences on a different namespace than the previously open namespace.
      Regards,
      Sara

      Reply
  11. I hope I can still use the EEPROM library, I only need to save an array of byte data, this seems far more complex?? (new ESP32 used from Arduino Nano)

    Reply
  12. I gave this a good go last night, could not get it to work…. it compiled fine, but nothing was ever saved. It was only a single byte value I needed saved – lucky that old EEPROM library still works a treat, 5 minutes later all was how I wanted it….

    Reply
  13. Wow just wow great tutorial. i am trying to get the following to work but to no avail. I’m new to Arduino esp32. i am trying to save the state of 7 GPIOs every time getOutputStates() is called
    I am having problems getting the following line to work and not sure how to get it going

    putInt(String(outputGPIOs[i]), int32_t String(digitalRead(outputGPIOs[i])));

    String getOutputStates() {
    JSONVar myArray;
    for (int i = 0; i < NUM_OUTPUTS; i++) {
    myArray[“gpios”][i][“output”] = String(outputGPIOs[i]);

    myArray["gpios"][i]["state"] = String(digitalRead(outputGPIOs[i]));
    Serial.println(String(outputGPIOs[i]) + "=" + String(digitalRead(outputGPIOs[i])));

    putInt(String(outputGPIOs[i]), int32_t String(digitalRead(outputGPIOs[i])));

    //preferences.putBool("state", String(digitalRead(outputGPIOs[i])));

    }

    i get the following error:

    exit status 1
    invalid conversion from ‘int’ to ‘const char*’ [-fpermissive]

    Thank you

    Reply
    • i noticed i cut off preferences. on a couple of lines:

      String getOutputStates() {
      JSONVar myArray;
      for (int i = 0; i < NUM_OUTPUTS; i++) {
      myArray[“gpios”][i][“output”] = String(outputGPIOs[i]);

      myArray[“gpios”][i][“state”] = String(digitalRead(outputGPIOs[i]));
      Serial.println(String(outputGPIOs[i]) + “=” + String(digitalRead(outputGPIOs[i])));

      preferences.putInt(int(outputGPIOs[i]), int32_t (digitalRead(outputGPIOs[i])));

      //preferences.putBool("state", String(digitalRead(outputGPIOs[i])));

      }

      Reply
      • Hi Mark,
        I had the same struggles composing the complex statements. I wanted to build a 30 day averaging array and store an incrementing index value, last sensor read value and the array[30]. The reason to work this way is the use of “Deep Sleep” mode.

        Being a self learning hacker (no formal training) and tinkerer I did a fair bit of research and realized it would be far better to use “SPIFFS” with an abstract layer helper library and stay away from the use of “JSONVar” which can lead to memory leakage issues (or so I read)

        I created an example that uses “daily” random values simulating 22500lts as a full water tank and each reading reduces by what would be a “daily use” and is put into the array[10] to get the average use value. Obviously the array size can be anything you want. Example sketch here https://pastebin.com/jPG5cXBk

        Note: Use the “SPIFFS” format sketch on this site to start over and I discovered even with this you’ll get an incorrect first value so I used the “clear()” class to get round that. You can use “esptool.py” and do an “erase_flash” which will zero the flash but you must do a “SPIFFS format” before loading a new sketch or it will error.

        Hope this helps
        Cheers
        Ralph

        Reply
  14. Ralph,

    Thanks for the pointers. i think i have figured out. the “key” in preferences has to be char can not be any other type.

    I am using SPIFFS for the web stuff to turn on and off the relays manually and set the schedule on and off times for each relay using an RTC.

    I’m not concerned about memory leakage as the eps will be reset every night at midnight. now that i have the preferences working.

    Thanks
    Mark

    Reply

Leave a Reply to Paul Cancel reply

Download our Free eBooks and Resources

Get instant access to our FREE eBooks, Resources, and Exclusive Electronics Projects by entering your email address below.