ESP32学习笔记(23)——NVS(非易失性存储)接口使用

Posted Leung_ManWah

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ESP32学习笔记(23)——NVS(非易失性存储)接口使用相关的知识,希望对你有一定的参考价值。

一、简介

非易失性存储 (NVS) 库主要用于在 flash 中存储键值格式的数据。

NVS适合存储一些小数据,如果对象占用空间比较大,使用负载均衡的FAT文件系统。

如果NVS分区被截断,比如更改分区表布局的时候,应该擦除分区内容。可以使用 idf.py erase_flash 命令擦除flash上全部的内容。

NVS 的操作对象为键值对,其中键是 ASCII 字符串,当前支持最大键长为 15 个字符,值可以为以下几种类型:

  • 整数型: uint8_tint8_tuint16_tint16_tuint32_tint32_tuint64_tint64_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 <stdio.h>
#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的操作(存储和读取大数组)(为什么存入数据成功,读取却为零的原因)

以上是关于ESP32学习笔记(23)——NVS(非易失性存储)接口使用的主要内容,如果未能解决你的问题,请参考以下文章

MicroPython ESP32NVS数据非易失性存储示例讲解说明

ESP-C3入门4. NVS非易失性存储使用

ESP-C3入门3. NVS非易失性存储使用

ESP-C3入门4. NVS非易失性存储使用

ESP32-IDF04-2 存储-NVS

STM32-常用存储器