ESP-IDF NVS

霍北北發表於2024-04-07

ESP-IDF NVS

目錄
  • ESP-IDF NVS
    • 1. 什麼是NVS?
    • 2. NVS中的概念
      • 2.1. partition 分割槽
      • 2.2. namespace 名稱空間
      • 2.3. key-value 鍵值對
    • 3. ESP-IDF中NVS的程式碼實現

1. 什麼是NVS?

非易失性儲存 (NVS) 庫主要用於在 flash 中儲存鍵值格式的資料。

2. NVS中的概念

2.1. partition 分割槽

這裡的Flash分割槽與分割槽表中所提到的Flash分割槽是相同的概念。

所以在希望呼叫nvs_open_from_partition()進行初始化時,需要先在分割槽表中設定對應名稱的Flash分割槽。

在使用預設分割槽的情況下,可以直接使用nvs_open()進行初始化。

2.2. namespace 名稱空間

為了減少不同元件之間鍵名的潛在衝突,NVS將每個鍵值對分配給一個名稱空間。

  1. 名稱空間的命名規則遵循鍵名的命名規則,例如,最多可佔15個字元。
  2. 單個NVS分割槽最多隻能容納254個不同的名稱空間。
  3. 不同NVS分割槽中具有相同名稱的名稱空間將被視為不同的名稱空間。

2.3. key-value 鍵值對

  1. 支援的最大鍵長為15個字元。
  2. 值可以分為以下幾種型別:整數型;以0結尾的字串;可變長度的二進位制資料(BLOB)。
  3. 讀取值時會執行資料型別檢查。如果讀取操作預期的資料型別與對應鍵的資料型別不匹配,則返回錯誤。

3. ESP-IDF中NVS的程式碼實現

基於nvs_flash_init()nvs_open(space_name, NVS_READWRITE, &my_handle)實現的NVS

#include <stdio.h>
#include "esp_system.h"
#include "nvs_flash.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

void app_main(void)
{
    // 初始化NVS
    esp_err_t err = nvs_flash_init();
    if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        err = nvs_flash_init();
    }
    ESP_ERROR_CHECK(err);

    nvs_handle_t my_handle;
    char* space_name = "storage";
    err = nvs_open(space_name, NVS_READWRITE, &my_handle); 
    if (err != ESP_OK) {    
        printf("開啟名稱空間失敗,具體錯誤 (%s) \n", esp_err_to_name(err));
    } else {                
        printf("開啟名稱空間成功 \n");

        // printf("Reading restart counter from NVS ... ");
        int32_t restart_counter = 0;
        char* key_name = "restart_counter";
        err = nvs_get_i32(my_handle, key_name, &restart_counter); // 獲取鍵值對restart_counter的值
        switch (err) {
            case ESP_OK:
                printf("鍵值對restart_counter的值獲取成功 \n");
                printf("Restart counter的值為%ld\n", restart_counter);
                break;
            case ESP_ERR_NVS_NOT_FOUND:
                printf("鍵值對restart_counter的值未被設定 \n");
                break;
            default :
                printf("鍵值對restart_counter的值讀取錯誤,具體錯誤 (%s) \n", esp_err_to_name(err));
        }

        // 更新restart_counter的值
        restart_counter++;
        err = nvs_set_i32(my_handle, key_name, restart_counter); 
        printf((err != ESP_OK) ? "設定鍵值對失敗!\n" : "設定鍵值對成功\n");

        // 提交寫入值
        err = nvs_commit(my_handle); 
        printf((err != ESP_OK) ? "提交寫入值失敗!\n" : "提交寫入值成功\n");

        nvs_close(my_handle);
    }

    printf("\n");

    // 重啟倒數計時
    for (int i = 10; i >= 0; i--) {
        printf("%ds後重啟裝置...\n", i);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
    printf("現在重啟!\n");
    esp_restart();
}