ESP32學習筆記(23)——NVS(非易失性存儲)接口使用

一、簡介

非易失性存儲 (NVS) 庫主要用于在 flash 中存儲鍵值格式的數(shù)據(jù)。

NVS適合存儲一些小數(shù)據(jù),如果對象占用空間比較大,使用負載均衡的FAT文件系統(tǒng)。

如果NVS分區(qū)被截斷,比如更改分區(qū)表布局的時候,應該擦除分區(qū)內(nèi)容??梢允褂?idf.py erase_flash 命令擦除flash上全部的內(nèi)容。

NVS 的操作對象為鍵值對,其中鍵是 ASCII 字符串,當前支持最大鍵長為 15 個字符,值可以為以下幾種類型:

  • 整數(shù)型: uint8_t、int8_tuint16_t、int16_tuint32_t、int32_t、uint64_tint64_t;
  • 字符型:\0 結(jié)尾的字符串;
  • 二進制數(shù)據(jù): 可變長度的二進制數(shù)據(jù) (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 讀取函數(shù)

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 寫入函數(shù)

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);
//用來存儲大二進制數(shù)據(jù)的函數(shù)(比如說結(jié)構(gòu)體)
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. 配置分區(qū)表
配置分區(qū)表: 我們也可以使用默認的分區(qū)表。默認分區(qū)表中nvs大小是24k(0x6000),可以根據(jù)自己需要對nvs空間進行修改。
2. 初始化NVS Flash
使用 nvs_flash_init(),如果 Flash 滿了或者希望清空原來的數(shù)據(jù),就使用 nvs_flash_erase() 清空。
3. 打開NVS,配置句柄

  • 對NVS空間進行操作的時候,是使用句柄實現(xiàn)的。
  • 同時,為了盡可能減少鍵值對的沖突,NVS引入了命名空間的概念,不同命名空間下的key捕獲產(chǎn)生沖突。
  • 同時也要在這里配置對NVS空間進行操作的權(quán)限,分為讀和讀寫兩種。
nvs_handle_t handle;
nvs_open("namespace1", NVS_READWRITE, &handle);

4. 讀寫操作
按照不同的數(shù)據(jù)類型,對數(shù)據(jù)進行g(shù)et和set操作
調(diào)用中使用nvs_get_*,nvs_set_*nvs_commit()功能函數(shù)。
5. 關(guān)閉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();
}

查看打?。?/p>


4.2 字符串及數(shù)組讀寫

初始化后

#include "nvs_flash.h"
void main(void)
{
    ...
    ESP_ERROR_CHECK(nvs_flash_init());
    ...
}

寫入

void NvsWriteDataToFlash(void)
{
    nvs_handle handle;
    // 寫入一個整形數(shù)據(jù),一個字符串,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的操作(存儲和讀取大數(shù)組)(為什么存入數(shù)據(jù)成功,讀取卻為零的原因)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容