一、简介
非易失性存储 (NVS) 库主要用于在 flash 中存储键值格式的数据。
NVS适合存储一些小数据,如果对象占用空间比较大,使用负载均衡的FAT文件系统。
如果NVS分区被截断,比如更改分区表布局的时候,应该擦除分区内容。可以使用
idf.py erase_flash
命令擦除flash上全部的内容。
NVS 的操作对象为键值对,其中键是 ASCII 字符串,当前支持最大键长为 15 个字符,值可以为以下几种类型:
- 整数型:
uint8_t
、int8_t
、uint16_t
、int16_t
、uint32_t
、int32_t
、uint64_t
和int64_t
; - 字符型: 以
\0
结尾的字符串; - 二进制数据: 可变长度的二进制数据 (BLOB)。
ESP-IDF 编程指南——非易失性存储库
二、API说明
以下 NVS 接口位于 nvs_flash/include/nvs_flash.h。
2.1 nvs_flash_init
2.2 nvs_flash_erase
2.3 nvs_open
2.4 读取函数
esp_err_t nvs_get_i8 (nvs_handle_t handle, const char* key, int8_t* out_value);
esp_err_t nvs_get_u8 (nvs_handle_t handle, const char* key, uint8_t* out_value);
esp_err_t nvs_get_i16 (nvs_handle_t handle, const char* key, int16_t* out_value);
esp_err_t nvs_get_u16 (nvs_handle_t handle, const char* key, uint16_t* out_value);
esp_err_t nvs_get_i32 (nvs_handle_t handle, const char* key, int32_t* out_value);
esp_err_t nvs_get_u32 (nvs_handle_t handle, const char* key, uint32_t* out_value);
esp_err_t nvs_get_i64 (nvs_handle_t handle, const char* key, int64_t* out_value);
esp_err_t nvs_get_u64 (nvs_handle_t handle, const char* key, uint64_t* out_value);
//这两个的长度需要特殊操作
esp_err_t nvs_get_str (nvs_handle_t handle, const char* key, char* out_value, size_t* length);
esp_err_t nvs_get_blob(nvs_handle_t handle, const char* key, void* out_value, size_t* length);
2.5 写入函数
esp_err_t nvs_set_i8 (nvs_handle_t handle, const char* key, int8_t value);
esp_err_t nvs_set_u8 (nvs_handle_t handle, const char* key, uint8_t value);
esp_err_t nvs_set_i16 (nvs_handle_t handle, const char* key, int16_t value);
esp_err_t nvs_set_u16 (nvs_handle_t handle, const char* key, uint16_t value);
esp_err_t nvs_set_i32 (nvs_handle_t handle, const char* key, int32_t value);
esp_err_t nvs_set_u32 (nvs_handle_t handle, const char* key, uint32_t value);
esp_err_t nvs_set_i64 (nvs_handle_t handle, const char* key, int64_t value);
esp_err_t nvs_set_u64 (nvs_handle_t handle, const char* key, uint64_t value);
esp_err_t nvs_set_str (nvs_handle_t handle, const char* key, const char* value);
//用来存储大二进制数据的函数(比如说结构体)
esp_err_t nvs_set_blob(nvs_handle_t handle, const char* key, const void* value, size_t length);
2.6 nvs_commit
2.7 nvs_close
三、编程流程
1. 配置分区表
配置分区表: 我们也可以使用默认的分区表。默认分区表中nvs大小是24k(0x6000),可以根据自己需要对nvs空间进行修改。
2. 初始化NVS Flash
使用 nvs_flash_init()
,如果 Flash 满了或者希望清空原来的数据,就使用 nvs_flash_erase()
清空。
3. 打开NVS,配置句柄
- 对NVS空间进行操作的时候,是使用句柄实现的。
- 同时,为了尽可能减少键值对的冲突,NVS引入了命名空间的概念,不同命名空间下的key捕获产生冲突。
- 同时也要在这里配置对NVS空间进行操作的权限,分为读和读写两种。
nvs_handle_t handle;
nvs_open("namespace1", NVS_READWRITE, &handle);
4. 读写操作
按照不同的数据类型,对数据进行get和set操作
调用中使用nvs_get_*
,nvs_set_*
和nvs_commit()
功能函数。
5. 关闭NVS
nvs_close(handle);
四、应用实例
4.1 单变量读写
使用 esp-idf\examples\storage\nvs_rw_value 中的例程
#include
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "nvs.h"
void app_main(void)
{
// Initialize NVS
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
// NVS partition was truncated and needs to be erased
// Retry nvs_flash_init
ESP_ERROR_CHECK(nvs_flash_erase());
err = nvs_flash_init();
}
ESP_ERROR_CHECK( err );
// Open
printf("\n");
printf("Opening Non-Volatile Storage (NVS) handle... ");
nvs_handle_t my_handle;
err = nvs_open("storage", NVS_READWRITE, &my_handle);
if (err != ESP_OK) {
printf("Error (%s) opening NVS handle!\n", esp_err_to_name(err));
} else {
printf("Done\n");
// Read
printf("Reading restart counter from NVS ... ");
int32_t restart_counter = 0; // value will default to 0, if not set yet in NVS
err = nvs_get_i32(my_handle, "restart_counter", &restart_counter);
switch (err) {
case ESP_OK:
printf("Done\n");
printf("Restart counter = %d\n", restart_counter);
break;
case ESP_ERR_NVS_NOT_FOUND:
printf("The value is not initialized yet!\n");
break;
default :
printf("Error (%s) reading!\n", esp_err_to_name(err));
}
// Write
printf("Updating restart counter in NVS ... ");
restart_counter++;
err = nvs_set_i32(my_handle, "restart_counter", restart_counter);
printf((err != ESP_OK) ? "Failed!\n" : "Done\n");
// Commit written value.
// After setting any values, nvs_commit() must be called to ensure changes are written
// to flash storage. Implementations may write to storage at other times,
// but this is not guaranteed.
printf("Committing updates in NVS ... ");
err = nvs_commit(my_handle);
printf((err != ESP_OK) ? "Failed!\n" : "Done\n");
// Close
nvs_close(my_handle);
}
printf("\n");
// Restart module
for (int i = 10; i >= 0; i--) {
printf("Restarting in %d seconds...\n", i);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
printf("Restarting now.\n");
fflush(stdout);
esp_restart();
}
查看打印:
4.2 字符串及数组读写
初始化后
#include "nvs_flash.h"
void main(void)
{
...
ESP_ERROR_CHECK(nvs_flash_init());
...
}
写入
void NvsWriteDataToFlash(void)
{
nvs_handle handle;
// 写入一个整形数据,一个字符串,WIFI信息以及版本信息
static const char *NVS_CUSTOMER = "customer data";
static const char *DATA2 = "String";
static const char *DATA3 = "blob_wifi";
static const char *DATA4 = "blob_version";
// 要写入的字符串
char str_for_store[32] = "i am a string.";
// 要写入的WIFI信息
wifi_config_t wifi_config_to_store = {
.sta = {
.ssid = "store_ssid:hello_kitty",
.password = "store_password:1234567890",
},
};
// 要写入的版本号
uint8_t version_for_store[4] = {0x01, 0x01, 0x01, 0x00};
printf("set size:%u\r\n", sizeof(wifi_config_to_store));
ESP_ERROR_CHECK( nvs_open( NVS_CUSTOMER, NVS_READWRITE, &handle) );
ESP_ERROR_CHECK( nvs_set_str( handle, DATA2, str_for_store) );
ESP_ERROR_CHECK( nvs_set_blob( handle, DATA3, &wifi_config_to_store, sizeof(wifi_config_to_store)) );
ESP_ERROR_CHECK( nvs_set_blob( handle, DATA4, version_for_store, 4) );
ESP_ERROR_CHECK( nvs_commit(handle) );
nvs_close(handle);
}
读出
void NvsReadDataFromFlash(void)
{
esp_err_t err;
nvs_handle handle;
static const char *NVS_CUSTOMER = "customer data";
static const char *DATA2 = "String";
static const char *DATA3 = "blob_wifi";
static const char *DATA4 = "blob_version";
uint32_t str_length = 32;
char str_data[32] = {0};
wifi_config_t wifi_config_stored;
uint8_t version[4] = {0};
uint32_t version_len = 4;
memset(&wifi_config_stored, 0x0, sizeof(wifi_config_stored));
uint32_t wifi_len = sizeof(wifi_config_stored);
ESP_ERROR_CHECK( nvs_open(NVS_CUSTOMER, NVS_READWRITE, &handle) );
ESP_ERROR_CHECK ( nvs_get_str(handle, DATA2, str_data, &str_length) );
ESP_ERROR_CHECK ( nvs_get_blob(handle, DATA3, &wifi_config_stored, &wifi_len) );
ESP_ERROR_CHECK ( nvs_get_blob(handle, DATA4, version, &version_len) );
printf("[data1]: %s len:%u\r\n", str_data, str_length);
printf("[data2]: %d\r\n", value);
printf("[data3]: ssid:%s passwd:%s\r\n", wifi_config_stored.sta.ssid, wifi_config_stored.sta.password);
nvs_close(handle);
}
• 由 Leung 写于 2021 年 6 月 8 日
• 参考:【ESP32-IDF】04-2 存储-NVS
ESP32 学习日志(5)——NVS
ESP32_学习笔记(一)NVS的操作(存储和读取大数组)(为什么存入数据成功,读取却为零的原因)