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

一、簡介

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

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

如果NVS分區(qū)被截斷氏义,比如更改分區(qū)表布局的時候,應(yīng)該擦除分區(qū)內(nèi)容图云」哂疲可以使用 idf.py erase_flash 命令擦除flash上全部的內(nèi)容。

NVS 的操作對象為鍵值對竣况,其中鍵是 ASCII 字符串克婶,當(dāng)前支持最大鍵長為 15 個字符,值可以為以下幾種類型:

  • 整數(shù)型: uint8_t丹泉、int8_t情萤、uint16_tint16_t摹恨、uint32_t筋岛、int32_tuint64_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ū)表: 我們也可以使用默認(rèn)的分區(qū)表。默認(rèn)分區(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);

四莫辨、應(yīng)用實例

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 字符串及數(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 學(xué)習(xí)日志(5)——NVS
    ESP32_學(xué)習(xí)筆記(一)NVS的操作(存儲和讀取大數(shù)組)(為什么存入數(shù)據(jù)成功,讀取卻為零的原因)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末喻粹,一起剝皮案震驚了整個濱河市蟆融,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌守呜,老刑警劉巖型酥,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異弛饭,居然都是意外死亡,警方通過查閱死者的電腦和手機萍歉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進店門侣颂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人枪孩,你說我怎么就攤上這事憔晒≡逡蓿” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵拒担,是天一觀的道長嘹屯。 經(jīng)常有香客問我,道長从撼,這世上最難降的妖魔是什么州弟? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮低零,結(jié)果婚禮上婆翔,老公的妹妹穿的比我還像新娘。我一直安慰自己掏婶,他們只是感情好啃奴,可當(dāng)我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著雄妥,像睡著了一般最蕾。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上老厌,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天瘟则,我揣著相機與錄音,去河邊找鬼梅桩。 笑死壹粟,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的宿百。 我是一名探鬼主播趁仙,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼垦页!你這毒婦竟也來了雀费?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤痊焊,失蹤者是張志新(化名)和其女友劉穎盏袄,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體薄啥,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡辕羽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了垄惧。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片刁愿。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖到逊,靈堂內(nèi)的尸體忽然破棺而出铣口,到底是詐尸還是另有隱情滤钱,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布脑题,位于F島的核電站件缸,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏叔遂。R本人自食惡果不足惜他炊,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望掏熬。 院中可真熱鬧佑稠,春花似錦、人聲如沸旗芬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽疮丛。三九已至幔嫂,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間誊薄,已是汗流浹背履恩。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留呢蔫,地道東北人切心。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像片吊,于是被迫代替她去往敵國和親绽昏。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,515評論 2 359

推薦閱讀更多精彩內(nèi)容