NVS的使用方法
简介
NVS是ESP32芯片内置的非易失性存储区域,用于存储用户自定义的数据。NVS可以用于存储配置信息、状态信息、传感器数据等。NVS支持多种数据类型,包括字符串、整数、浮点数、布尔值等。NVS还支持分区,每个分区可以存储不同类型的数据。
使用方法
初始化NVS
在使用NVS之前,需要先初始化NVS。可以使用以下代码初始化NVS:
c
#include "nvs_flash.h"
void app_main()
{
// Initialize NVS
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
}
写入数据
将相应的数据写入到NVS中。可以使用以下代码写入数据:
c
#include "nvs_flash.h"
esp_err_t save_nvs_value(const char* namespace, const char* key, void* value, size_t size, nvs_type_t type) {
nvs_handle_t handle;
esp_err_t err;
err = nvs_open(namespace, NVS_READWRITE, &handle);
if (err != ESP_OK) return err;
switch(type) {
case NVS_TYPE_U8:
err = nvs_set_u8(handle, key, *(uint8_t*)value);
break;
case NVS_TYPE_I8:
err = nvs_set_i8(handle, key, *(int8_t*)value);
break;
case NVS_TYPE_U16:
err = nvs_set_u16(handle, key, *(uint16_t*)value);
break;
case NVS_TYPE_I16:
err = nvs_set_i16(handle, key, *(int16_t*)value);
break;
case NVS_TYPE_U32:
err = nvs_set_u32(handle, key, *(uint32_t*)value);
break;
case NVS_TYPE_I32:
err = nvs_set_i32(handle, key, *(int32_t*)value);
break;
case NVS_TYPE_STR:
err = nvs_set_str(handle, key, (char*)value);
break;
case NVS_TYPE_BLOB:
err = nvs_set_blob(handle, key, value, size);
break;
default:
err = ESP_ERR_NVS_TYPE_MISMATCH;
}
if (err == ESP_OK) {
err = nvs_commit(handle);
}
nvs_close(handle);
return err;
}
读取数据
c
#include "nvs_flash.h"
esp_err_t read_nvs_value(const char* namespace, const char* key, void* value, size_t* size, nvs_type_t type) {
nvs_handle_t handle;
esp_err_t err;
err = nvs_open(namespace, NVS_READONLY, &handle);
if (err != ESP_OK) return err;
switch(type) {
case NVS_TYPE_U8:
err = nvs_get_u8(handle, key, (uint8_t*)value);
break;
case NVS_TYPE_I8:
err = nvs_get_i8(handle, key, (int8_t*)value);
break;
case NVS_TYPE_U16:
err = nvs_get_u16(handle, key, (uint16_t*)value);
break;
case NVS_TYPE_I16:
err = nvs_get_i16(handle, key, (int16_t*)value);
break;
case NVS_TYPE_U32:
err = nvs_get_u32(handle, key, (uint32_t*)value);
break;
case NVS_TYPE_I32:
err = nvs_get_i32(handle, key, (int32_t*)value);
break;
case NVS_TYPE_STR:
size_t required_size = 0;
err = nvs_get_str(handle, key, NULL, &required_size);
if (err != ESP_OK) {
break;
}
err = nvs_get_str(handle, key, (char*)value, &required_size);
break;
case NVS_TYPE_BLOB:
err = nvs_get_blob(handle, key, value, size);
break;
default:
err = ESP_ERR_NVS_TYPE_MISMATCH;
}
nvs_close(handle);
return err;
}
特别说明
对于字符串的读取操作,按官方的文档,需要先获取字符串的长度
,然后再读取字符串
。一定要按这种方式进行读取,不能直接读取。 原因:
根本原因:
NVS对字符串存储有特殊处理,实际存储长度可能比字符串内容长 当直接指定size时,NVS会严格校验传入的size参数必须等于实际存储长度(不是字符串长度)两步读取法获取的required_size是存储长度而非字符串长度
技术细节:
字符串在NVS中存储时会包含额外信息(如长度前缀等) 第一步获取的required_size = 字符串长度 + 1(NULL终止符) + 可能的元数据直接指定size=20时,即使大于字符串长度,但可能不等于存储格式的精确长度
解决方案:
必须使用两步读取法,这是官方推荐的安全做法 直接指定大size不可靠,因为:
- 不同ESP-IDF版本存储格式可能变化
- 可能包含隐藏的元数据
- 破坏数据一致性检查
删除数据
c
#include "nvs_flash.h"
esp_err_t delete_nvs_value(const char* namespace, const char* key) {
nvs_handle_t handle;
esp_err_t err;
err = nvs_open(namespace, NVS_READWRITE, &handle);
if (err != ESP_OK) return err;
err = nvs_erase_key(handle, key);
if (err == ESP_OK) {
err = nvs_commit(handle);
}
nvs_close(handle);
return err;
}