Skip to content

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;
}