1.简介

有时我们需要保存一些信息,然后在下次启动时使用。
我们希望这些数据在掉电的时候不会丢失。esp8266没有自己的rom,所以我们需要保存在外部flash中。
官方已经为我们提供了很多api,我们可以直接使用SDK中的components。

SDK编译环境搭建:https://blog.csdn.net/valonshen/article/details/104124127
由于esp8266文档中没有相关说明,参考esp32:https://docs.espressif.com/projects/esp-idf/zh_CN/latest/api-reference/storage/nvs_flash.html#

非易失性存储 (NVS) 库主要用于在 flash 中存储键值格式的数据。NVS 最适合存储一些较小的数据,而非字符串或二进制大对象 (BLOB) 等较大的数据。如需存储较大的 BLOB 或者字符串,请考虑使用基于磨损均衡库的 FAT 文件系统。

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

  1. 整数型:uint8_t、int8_t、uint16_t、int16_t、uint32_t、int32_t、uint64_t 和 int64_t;
  2. 以 \0 结尾的字符串;
  3. 可变长度的二进制数据 (BLOB)

2. 部分API介绍

esp_err_t nvs_flash_init(void)

初始化默认NVS分区,需要在其他API调用之前调用。esp8266将flash分成几个分区,每个分区的地址偏移和大小都可以自行配置,我会在另一个文章在介绍。

返回值

  • ESP_OK if storage was successfully initialized.
  • ESP_ERR_NVS_NO_FREE_PAGES if the NVS storage contains no empty pages (which may happen if NVS partition was truncated)
  • ESP_ERR_NOT_FOUND if no partition with label “nvs” is found in the partition table one of the error codes from the underlying
    flash storage driver

esp_err_t nvs_flash_erase(void)

擦除默认NVS分区所有数据。若要继续使用,需调用nvs_flash_init初始化。

返回值:

  • ESP_OK on success
  • ESP_ERR_NOT_FOUND if there is no NVS partition labeled “nvs” in the partition table

esp_err_t nvs_open(const char *name, nvs_open_mode_t open_mode, nvs_handle_t * out_handle)
void nvs_close(nvs_handle_t handle)

用于打开和关闭 NVS分区。name为最多15字节的命名空间,用于防止键值对名称的冲突。在不同的命名空间中允许出现相同名称的键值对,但在同一个命名空间中键值对的名称是唯一的。handle用于后续读写键值对操作。
目前使用esp8266的SDK,nvs打开的句柄类型需改成 nvs_handle,而esp-ief是nvs_handle_t

返回值:

  • ESP_OK if storage handle was opened successfully
  • ESP_ERR_NVS_NOT_INITIALIZED if the storage driver is not initialized
  • ESP_ERR_NVS_PART_NOT_FOUND if the partition with label “nvs” is not found
  • ESP_ERR_NVS_NOT_FOUND id namespace doesn’t exist yet and mode is NVS_READONLY
  • ESP_ERR_NVS_INVALID_NAME if namespace name doesn’t satisfy constraints
  • other error codes from the underlying storage driver

参数:

  • name: Namespace name. Maximal length is determined by the underlying implementation, but is guaranteed to be at least 15 characters. Shouldn’t be empty.
  • open_mode: NVS_READWRITE or NVS_READONLY. If NVS_READONLY, will open a handle for reading only. All write requests will be rejected for this handle.
  • out_handle: If successful (return code is zero), handle will be returned in this argument.

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_get_i8(nvs_handle_t handle, const char *key, int8_t *out_value)

保存数值键值对,键的名字在同一命名空间内必须保持唯一,支持最长15字节的ASCII字符串。实际的存储操作将在nvs_commit被调用时完成。

返回值:

  • ESP_OK if value was set successfully
  • ESP_ERR_NVS_INVALID_HANDLE if handle has been closed or is NULL
  • ESP_ERR_NVS_READ_ONLY if storage handle was opened as read only
  • ESP_ERR_NVS_INVALID_NAME if key name doesn’t satisfy constraints
  • ESP_ERR_NVS_NOT_ENOUGH_SPACE if there is not enough space in the underlying storage to save the value
  • ESP_ERR_NVS_REMOVE_FAILED if the value wasn’t updated because flash write operation has failed. The value was written however, and update will be finished after re-initialization of nvs, provided that flash operation doesn’t fail again.
  • ESP_ERR_NVS_VALUE_TOO_LONG if the string value is too long

参数:

  • handle: Handle obtained from nvs_open function. Handles that were opened read only cannot be used.
  • key: Key name. Maximal length is determined by the underlying implementation, but is guaranteed to be at least 15
  • characters. Shouldn’t be empty.
  • value: The value to set. For strings, the maximum length (including null character) is 4000 bytes.

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_OK if the value was retrieved successfully
  • ESP_ERR_NVS_NOT_FOUND if the requested key doesn’t exist
  • ESP_ERR_NVS_INVALID_HANDLE if handle has been closed or is NULL
  • ESP_ERR_NVS_INVALID_NAME if key name doesn’t satisfy constraints
  • ESP_ERR_NVS_INVALID_LENGTH if length is not sufficient to store data

参数:

  • handle: Handle obtained from nvs_open function.
  • key: Key name. Maximal length is determined by the underlying implementation, but is guaranteed to be at least 15
  • characters. Shouldn’t be empty.
  • out_value: Pointer to the output value. May be NULL for nvs_get_str and nvs_get_blob, in this case required length will be returned in length argument.
  • length: A non-zero pointer to the variable holding the length of out_value. In case out_value a zero, will be set to the length required to hold the value. In case out_value is not zero, will be set to the actual length of the value written. For nvs_get_str this includes zero terminator.

esp_err_t nvs_commit(nvs_handle_t handle)

将修改的键值对写入flash。所以操作过键值对后,执行此命令。

返回值:

  • ESP_OK if the changes have been written successfully
  • ESP_ERR_NVS_INVALID_HANDLE if handle has been closed or is NULL
  • other error codes from the underlying storage driver

参数:

  • handle: Storage handle obtained with nvs_open. Handles that were opened read only cannot be used.

esp_err_t nvs_erase_key(nvs_handle_t handle, const char *key)

擦除某一命名空间的键值对。

3. 代码示例

#include <string.h>
#include <stdlib.h>
#include "esp_event_loop.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "nvs.h"


static const char* TAG = "main";

void app_main(void)
{
    size_t length;
    char* buf ;
    // Initialize NVS
    esp_err_t err = nvs_flash_init();
    if (err == ESP_ERR_NVS_NO_FREE_PAGES) {
        // 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 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 string from NVS ... \r\n");
        //若传入的buffer为null,数据长度将保存在length中
        err = nvs_get_str(my_handle, "string", NULL,&length);
        switch (err) {
            case ESP_OK:
                buf = (char*)malloc(length);
                if(nvs_get_str(my_handle,"string",buf,&length) == ESP_OK){
                    ESP_LOGI(TAG,"get NVS data key: string, value: %s",buf);
                }
                else{
                    ESP_LOGE(TAG,"get NVS data fault");
                }
                free(buf);
                nvs_erase_key(my_handle,"string");
                ESP_LOGI(TAG,"erase key");
                break;
            case ESP_ERR_NVS_NOT_FOUND:
                ESP_LOGI(TAG,"no found NVS Key");
                if( nvs_set_str(my_handle,"string","exampleString") == ESP_OK ){
                    ESP_LOGI(TAG,"write key");
                }
                else{
                    ESP_LOGE(TAG,"nvs key write fault");
                }
                break;
            default :
                printf("Error (%s) reading!\n", esp_err_to_name(err));
        }
        //需要commit才能生效修改
        nvs_commit(my_handle);
        nvs_close(my_handle);
        vTaskDelay(800);
        esp_restart();
}

执行

idf.py flash monitor

将打印如下信息:
第一次打印

Opening Non-Volatile Storage (NVS) handle… Done
Reading string from NVS …
I (305) main: no found NVS Key
I (312) main: write key
Done

等待8s重启,第二次打印

Opening Non-Volatile Storage (NVS) handle… Done
Reading string from NVS …
I (306) main: get NVS data key: string, value: exampleString
I (317) main: erase key

更多推荐