In today’s tutorial you’ll learn how to permanently store information, so that they are preserved even if the esp32 chip is reset or power is removed.
NVS (Non Volatile Storage) is a library included in the esp-idf framework that allows to store information (in the form of key/value) in the flash memory, the content of which is not erased when you reset the chip or remove the power.
If you remember, in a previous post I explained how the external flash memory is organized. The main purpose of that memory is for sure to store the program that will be executed by the esp32 chip. However, it’s possible to divide the flash memory in partitions: the framework offers some ready-to-use partition tables but you’re free to define custom ones.
If you use the default partition table (“Single factory app, no OTA”), you may notice that it contains a partition of type data and subtype nvs:
The default size for that partition is 24Kbyte.
Thanks to the NVS library, you can store custom data into that partition. Information is organized in key/value pairs; a label (= key) with a maximum length of 15 characters is assigned to each value:
You can store different types of data: from numeric values to text strings and byte sequences (blob, binary large object). You’ll learn that the library provides specific methods based on the type of data you want to store or retrieve.
To be able to use the library in your code, first include the following headers:
#include "esp_partition.h" #include "esp_err.h" #include "nvs_flash.h" #include "nvs.h"
The first step to use the nvs partition is to initialize the library, with the command:
esp_err_t err = nvs_flash_init();
The command returns ESP_OK if successful; on the contrary it returns one of the error codes defined in the nvs.h file (see below). In particular, if the partition was resized or changed, you may get the ESP_ERR_NVS_NO_FREE_PAGES error code. This error can be resolved “formatting” the partition.
First you have to identify the nvs partition in the flash memory:
const esp_partition_t* nvs_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, NULL); if(!nvs_partition) printf("FATAL ERROR: No NVS partition found\n");
then you can format it with the esp_partition_erase_range() command:
err = (esp_partition_erase_range(nvs_partition, 0, nvs_partition->size)); if(err != ESP_OK) printf("FATAL ERROR: Unable to erase the partition\n");
The last step is to open the partition, you can do it in READONLY o READWRITE mode:
nvs_handle my_handle; err = nvs_open("storage", NVS_READWRITE, &my_handle); if (err != ESP_OK) printf("FATAL ERROR: Unable to open NVS\n");
Set – Get
Once the partition is open, you can store (set) new values or retrieve (get) existing ones:
There are different methods, based on the data type (i8 stands for integer with 8bits, u8 per unsigned integer 8bit…):
- nvs_set_i8(), nvs_set_u8(), nvs_set_i16(), nvs_set_u16()…
All the set methods require as parameters the partition handler, the key name and the value to be stored:
esp_err_t nvs_set_i8(nvs_handle handle, const char* key, int8_t value);
The only exception is the nvs_set_blob() method which requires as additional parameter the length of the byte array to be stored:
esp_err_t nvs_set_blob(nvs_handle handle, const char* key, const void* value, size_t length);
After having called a set method, you have to commit the change with the method:
esp_err_t nvs_commit(nvs_handle handle);
To get data from the flash the library offers similar methods:
- nvs_get_i8(), nvs_get_u8(), nvs_get_i16(), nvs_get_u16()…
The parameters for those methods are the partition handler, the key name and a pointer to the variable that has to be updated with the value retrieved:
esp_err_t nvs_get_i8(nvs_handle handle, const char* key, int8_t* out_value);
Because of you cannot know a priori the size for string or blob values, you can do a “trick”: first call the nvs_get_string() method passing NULL as pointer to get the length of the value, then allocate a variable with the correct size and finally call again the method passing that variable to get the value:
size_t string_size; esp_err_t err = nvs_get_str(my_handle, parameter, NULL, &string_size); char* value = malloc(string_size); err = nvs_get_str(my_handle, parameter, value, &string_size);
The library also include a couple of methods to erase the partition content:
esp_err_t nvs_erase_key(nvs_handle handle, const char* key); esp_err_t nvs_erase_all(nvs_handle handle);
The first method is more selective and allows you to delete a single key while the second deletes the entire memory content.
Both the methods must be followed by the nvs_commit() command as explained before.
All the methods in the nvs library return an error code (esp_err_t variable).
The possible errors are listed in the nvs.h file:
For example, if you’re calling a get method with a key that is not present in the flash, you’ll get the ESP_ERR_NOT_FOUND error. You should handle the different errors in your program:
esp_err_t err = nvs_get_i32(my_handle, parameter, &value); if(err == ESP_ERR_NVS_NOT_FOUND) printf("\nKey %s not found\n", parameter);
For this tutorial I wrote a program that allows, using a simple command line, to store and retrieve information from the nvs partition. The source code is available on Github, here’s a video that shows how it works:
[youtube id=”2x4S8ceaYEY” width=”600″ height=”350″]