ESP-C3入门3. NVS非易失性存储使用
Posted 编程圈子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ESP-C3入门3. NVS非易失性存储使用相关的知识,希望对你有一定的参考价值。
ESP-C3入门3. NVS非易失性存储使用
一、NVS简介
1. 存储类型
nvs是在Flash中存储键值对数据,适合存储小规模的数据,不适合存储大的数据。
写键值对时,key 的最大长度为154个字符, 值支持常用的数据类型, 如:
- uint8_t
- int8_t
- uint16_t
- int16_t
- uint32_t
- int32_t
- uint64_t
- int64_t
- 字符串(以
\\0
结尾) - 可变二进制数据(BLOB)
2. 命名空间
NVS为每个键值对分配了一个命名空间,命名空间的长度最大为 NVS_KEY_NAME_MAX_SIZE-1
个字符。
3. 注意事项
另外,Flash的操作要注意:
- Flash在写之前要保证页是空的
- 如果nvs分区被截断,如更改分区表布局,或内容区是满的要先擦除分区内容
二、NVS的使用流程
nvs操作是在库: nvs_flash.h
中。
1. 配置分区表
如果使用默认的nvs分区操作,这一步可以不用。
2. 擦除NVS空间
(1)删除默认分区
函数定义
esp_err_t nvs_flash_erase(void)
删除名称为"nvs"的默认nvs分区。如果分区已经被初始化了,则先要执行反初始化。
返回值
- ESP_OK on success
- ESP_ERR_NOT_FOUND 分区表中没有找到"nvs"空间
- 其它值很少出现
(2)删除指定分区
esp_err_t nvs_flash_erase_partition(const char *part_name)
参数
- part_name :分区表名称
返回值
与nvs_flash_erase(void)
相同
(3) 删除自定义分区
esp_err_t nvs_flash_erase_partition_ptr(const esp_partition_t *partition)
参数
- partition 指向由 ESP 分区 API 获取的分区指针
返回值
- ESP_OK on success
- ESP_ERR_NOT_FOUND 分区表中没有找到指定分区
- ESP_ERR_INVALID_ARG 分区为NULL
- 其它基础闪存错误代码
3. 初始化NVS空间
(1) 函数原型
esp_err_t err = nvs_flash_init();
默认的NVS分区是在分区表中标记为“nvs"的分区。初始化NVS空间时,注意要检查flash是否已满、另外SDK版本与flash中使用的版本是否一致。
(2) 示例
下面是示例初始化代码:
// 初始化nvs flash
esp_err_t err = nvs_flash_init();
// 如果nvs flash 满了就清空
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);
4. 获取NVS空间的操作句柄
(1) 函数定义
esp_err_t nvs_open(const char *namespace_name, nvs_open_mode_t open_mode, nvs_handle_t *out_handle)
(2) 参数
- namespace_name:命名空间名称。最大长度为
NVS_KEY_NAME_MAX_SIZE-1
个字符,不能为空。 - open_mode:读写标志,NVS_READWRITE或NVS_READONLY。
- out_handle:操作成功(返回代码为零),将在此参数中返回句柄。
(3) 返回值
- ESP_OK:存储句柄是否成功打开
- ESP_FAIL:是否存在内部错误
- ESP_ERR_NVS_NOT_INITIALIZED:存储驱动程序未初始化
- ESP_ERR_NVS_PART_NOT_FOUND:是否找不到标签为“NVS”的分区
- ESP_ERR_NVS_NOT_FOUND :id 命名空间尚不存在,模式NVS_READONLY
- ESP_ERR_NVS_INVALID_NAME:命名空间名称不满足约束
- ESP_ERR_NO_MEM:无法为内部结构分配内存的情况
- ESP_ERR_NVS_NOT_ENOUGH_SPACE:如果没有空间容纳新条目或有太多不同的命名空间(允许的不同命名空间的最大值:254)
(4) 示例
printf("Opening Non-Volatile Storage (NVS) handle... ");
nvs_handle 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");
5. 读NVS空间
(1) 函数定义
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) 参数
- handle 从nvs_open函数获取的句柄。
- key 键,最大长度为 (NVS_KEY_NAME_MAX_SIZE-1) 个字符,不应为空。
- out_value : 指向输出值的指针。对于nvs_get_str和nvs_get_blob可能是 NULL,在这种情况下,所需的长度将在长度参数中返回。
(3) 返回
- ESP_OK:是否成功检索
- ESP_FAIL:是否存在内部错误,很可能是 NVS 分区损坏
- ESP_ERR_NVS_NOT_FOUND:请求的密钥不存在
- ESP_ERR_NVS_INVALID_HANDLE: 句柄是否已关闭或为 NULL
- ESP_ERR_NVS_INVALID_NAME:如果键名不满足约束
- ESP_ERR_NVS_INVALID_LENGTH:长度是否不足以存储数据
(4) 示例
// Example of using nvs_get_i32:
int32_t max_buffer_size = 4096; // default value
esp_err_t err = nvs_get_i32(my_handle, "max_buffer_size", &max_buffer_size);
assert(err == ESP_OK || err == ESP_ERR_NVS_NOT_FOUND);
// if ESP_ERR_NVS_NOT_FOUND was returned, max_buffer_size will still
// have its default value.
// Example (without error checking) of using nvs_get_str to get a string into dynamic array:
size_t required_size;
nvs_get_str(my_handle, "server_name", NULL, &required_size);
char* server_name = malloc(required_size);
nvs_get_str(my_handle, "server_name", server_name, &required_size);
// Example (without error checking) of using nvs_get_blob to get a binary data
into a static array:
uint8_t mac_addr[6];
size_t size = sizeof(mac_addr);
nvs_get_blob(my_handle, "dst_mac_addr", mac_addr, &size);
6. 保存
(1) 函数定义
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)
(2) 参数
- handle : 由nvs_open函数返回的句柄
- key:键名
- value : 值,如果是字符串且有完整的空间,最大值是4000字节;如果空间是碎片化的,则值会减少。
(3) 返回
- ESP_OK:是否设置成功
- ESP_ERR_NVS_INVALID_HANDLE:句柄是否被关闭或为空
- ESP_ERR_NVS_READ_ONLY:只读
- ESP_ERR_NVS_INVALID_NAME:名称是否无效
- ESP_ERR_NVS_NOT_ENOUGH_SPACE:空间不足
- ESP_ERR_NVS_REMOVE_FAILED:闪存写入操作失败导致无法更新数值,但是该值已写入,并且重新初始化nvs后将更新(前提是闪存不会再次写入失败)。
- ESP_ERR_NVS_VALUE_TOO_LONG : 值太长
7. 删除
(1)删除所有命名空间所有值
esp_err_t nvs_erase_all(nvs_handle_t handle)
函数定义
参数
- handle :由nvs_open返回的句柄
返回
- ESP_OK:删除操作成功
- ESP_FAIL:内部错误,一般是NVS分区故障
- ESP_ERR_NVS_INVALID_HANDLE :无效句柄
- ESP_ERR_NVS_READ_ONLY :句柄只读
- 其它值:其它存储驱动问题
(2) 删除指定键
esp_err_t nvs_erase_key(nvs_handle_t handle, const char *key)
参数
- handle:由nvs_open返回的句柄
- key :键,最大 (NVS_KEY_NAME_MAX_SIZE-1) ,不得为空
返回
与nvs_erase_all
相同 。
7. 提交修改
(1) 函数定义
esp_err_t nvs_commit(nvs_handle_t handle)
将任何挂起的更改写入非易失性存储。
(2) 参数
- handle :由nvs_open返回的句柄。
(3) 返回
- ESP_OK 写入成功
- ESP_ERR_NVS_INVALID_HANDLE 句柄为空或关闭
- 其它错误代码:存储驱动故障
8. 关闭NVS空间
(1)函数定义
void nvs_close(nvs_handle_t handle)
参数
- handle 要关闭的句柄
三、 其它操作
1. 获取nvs状态
esp_err_t nvs_get_stats(const char *part_name, nvs_stats_t *nvs_stats)
示例:
// Example of nvs_get_stats() to get the number of used entries and free entries:
nvs_stats_t nvs_stats;
nvs_get_stats(NULL, &nvs_stats);
printf("Count: UsedEntries = (%d), FreeEntries = (%d), AllEntries = (%d)\\n",
nvs_stats.used_entries, nvs_stats.free_entries, nvs_stats.total_entries);
结构体定义:
struct nvs_stats_t
- size_t used_entries : 存储条目数量
- size_t free_entries :空闲条目数量
- size_t total_entries:有效条目总数
- size_t namespace_count:命名空间数量
2. 计算命名空间内的存储数量
esp_err_t nvs_get_used_entry_count(nvs_handle_t handle, size_t *used_entries)
// Example of nvs_get_used_entry_count() to get amount of all key-value pairs in one namespace:
nvs_handle_t handle;
nvs_open("namespace1", NVS_READWRITE, &handle);
...
size_t used_entries;
size_t total_entries_namespace;
if(nvs_get_used_entry_count(handle, &used_entries) == ESP_OK)
// the total number of entries occupied by the namespace
total_entries_namespace = used_entries + 1;
3. 键搜索
esp_err_t nvs_entry_find(const char *part_name, const char *namespace_name, nvs_type_t type, nvs_iterator_t *output_iterator)
esp_err_t nvs_entry_next(nvs_iterator_t *iterator)
esp_err_t nvs_entry_info(const nvs_iterator_t iterator, nvs_entry_info_t *out_info)
示例:
// Example of listing all the key-value pairs of any type under specified partition and namespace
nvs_iterator_t it = NULL;
esp_err_t res = nvs_entry_find(<nvs_partition_name>, <namespace>, NVS_TYPE_ANY, &it);
while(res == ESP_OK)
nvs_entry_info_t info;
nvs_entry_info(it, &info); // Can omit error check if parameters are guaranteed to be non-NULL
printf("key '%s', type '%d' \\n", info.key, info.type);
res = nvs_entry_next(&it);
nvs_release_iterator(it);
四、操作示例
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "nvs.h"
void setup()
//01 初始化nvs flash
esp_err_t err = nvs_flash_init();
//02 如果nvs flash 满了就清空
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND)
ESP_ERROR_CHECK(nvs_flash_erase());
err = nvs_flash_init();
ESP_ERROR_CHECK(err);
//03 打开nvs,配置句柄
printf("\\n");
printf("Opening Non-Volatile Storage (NVS) handle... ");
nvs_handle 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");
//04 读操作
printf("Reading restart counter from NVS ... ");
int32_t restart_counter = 0;
// 如果没有在nvs中设置,则值默认为0
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));
//05 写操作
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");
//06 提交修改
printf("Committing updates in NVS ... ");
err = nvs_commit(my_handle);
printf((err != ESP_OK) ? "Failed!\\n" : "Done\\n");
//07 关闭nvs
nvs_close(my_handle);
printf("\\n");
// 定时重启模块
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();
void app_main()
printf("main");
setup();
以上是关于ESP-C3入门3. NVS非易失性存储使用的主要内容,如果未能解决你的问题,请参考以下文章
MicroPython ESP32NVS数据非易失性存储示例讲解说明
Arduino框架下对ESP32 NVS非易失性存储解读以及应用示例