zoukankan      html  css  js  c++  java
  • ESP32-NVS存储(非易失性存储库)

    简介

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

    底层存储

      NVS 通过调用 spi_flash_{read|write|erase} API 对主 flash 的部分空间进行读、写、擦除操作,包括 data 类型和 nvs 子类型的所有分区。应用程序可调用 nvs_open API 选择使用带有 nvs 标签的分区,也可以通过调用 nvs_open_from_part API 选择使用指定名称的任意分区。

    键值对

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

        ◍ 整数型:uint8_tint8_tuint16_tint16_tuint32_tint32_tuint64_t 和 int64_t

        ◍ 以  结尾的字符串; 

        ◍ 可变长度的二进制数据 (BLOB)

          *注:字符串值当前上限为 4000 字节,其中包括空终止符。BLOB 值上限为 508,000 字节或分区大小减去 4000 字节的 97.6%,以较低值为准。

      键必须唯一。为现有的键写入新的值可能产生如下结果:

        ◍ 如果新旧值数据类型相同,则更新值;

        ◍ 如果新旧值数据类型不同,则返回错误。

      读取值时也会执行数据类型检查。如果读取操作的数据类型与该值的数据类型不匹配,则返回错误。

    NVS 优势

      1、接口更加安全: NVS 不直接操作 address. 对于终端用户而已, 更加安全。

      2、接口使用接近用户习惯,NVS 接口类似于电脑上操作文件一样:
        打开文件(nvs_open), 写文件(nvs_set_xxx), 保存文件(nvs_commit), 关闭文件(nvs_close)
        打开文件(nvs_open), 读取文件(nvs_get_xxx), 关闭文件(nvs_close)

      3、擦写均衡, 使 flash 寿命更长,NVS 在操作少量数据上, NVS 分区更大时, 擦写均衡表现的更为明显。

    API 分析参考

    初始化

    /**
     * @brief Initialize the default NVS partition.
     *
     * This API initialises the default NVS partition. The default NVS partition
     * is the one that is labeled "nvs" in the partition table.
     *
     * @return
     *      - 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_init(void);
    /**
     * @brief Initialize NVS flash storage for the specified partition.
     *
     * @param[in]  partition_label   Label of the partition. Note that internally a reference to
     *                               passed value is kept and it should be accessible for future operations
     *
     * @return
     *      - 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 specified partition is not found in the partition table
     *      - one of the error codes from the underlying flash storage driver
     */
    esp_err_t nvs_flash_init_partition(const char *partition_label);

    擦除

    /**
     * @brief Erase the default NVS partition
     *
     * This function erases all contents of the default NVS partition (one with label "nvs")
     *
     * @return
     *      - ESP_OK on success
     *      - ESP_ERR_NOT_FOUND if there is no NVS partition labeled "nvs" in the
     *        partition table
     */
    esp_err_t nvs_flash_erase(void);
    /**
     * @brief Erase specified NVS partition
     *a
     * This function erases all contents of specified NVS partition
     *
     * @param[in]  part_name    Name (label) of the partition to be erased
     *
     * @return
     *      - ESP_OK on success
     *      - ESP_ERR_NOT_FOUND if there is no NVS partition with the specified name
     *        in the partition table
     */
    esp_err_t nvs_flash_erase_partition(const char *part_name);

    打开文件

    /**
     * @brief      Open non-volatile storage with a given namespace from the default NVS partition
     *
     * Multiple internal ESP-IDF and third party application modules can store
     * their key-value pairs in the NVS module. In order to reduce possible
     * conflicts on key names, each module can use its own namespace.
     * The default NVS partition is the one that is labelled "nvs" in the partition
     * table.
     *
     * @param[in]  name        Namespace name. Maximal length is determined by the
     *                         underlying implementation, but is guaranteed to be
     *                         at least 15 characters. Shouldn't be empty.
     * @param[in]  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.
     * @param[out] out_handle  If successful (return code is zero), handle will be
     *                         returned in this argument.
     *
     * @return
     *             - 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
     */
    esp_err_t nvs_open(const char* name, nvs_open_mode open_mode, nvs_handle *out_handle);
    /**
     * @brief      Open non-volatile storage with a given namespace from specified partition
     *
     * The behaviour is same as nvs_open() API. However this API can operate on a specified NVS
     * partition instead of default NVS partition. Note that the specified partition must be registered
     * with NVS using nvs_flash_init_partition() API.
     *
     * @param[in]  part_name   Label (name) of the partition of interest for object read/write/erase
     * @param[in]  name        Namespace name. Maximal length is determined by the
     *                         underlying implementation, but is guaranteed to be
     *                         at least 15 characters. Shouldn't be empty.
     * @param[in]  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.
     * @param[out] out_handle  If successful (return code is zero), handle will be
     *                         returned in this argument.
     *
     * @return
     *             - 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 specified name 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
     */
    esp_err_t nvs_open_from_partition(const char *part_name, const char* name, nvs_open_mode open_mode, nvs_handle *out_handle);

    数据写入

    /**@{*/
    /**
     * @brief      set value for given key
     *
     * This family of functions set value for the key, given its name. Note that
     * actual storage will not be updated until nvs_commit function is called.
     *
     * @param[in]  handle  Handle obtained from nvs_open function.
     *                     Handles that were opened read only cannot be used.
     * @param[in]  key     Key name. Maximal length is determined by the underlying
     *                     implementation, but is guaranteed to be at least
     *                     15 characters. Shouldn't be empty.
     * @param[in]  value   The value to set.
     *                     For strings, the maximum length (including null character) is
     *                     4000 bytes.
     *
     * @return
     *             - 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
     */
    esp_err_t nvs_set_i8  (nvs_handle handle, const char* key, int8_t value);
    esp_err_t nvs_set_u8  (nvs_handle handle, const char* key, uint8_t value);
    esp_err_t nvs_set_i16 (nvs_handle handle, const char* key, int16_t value);
    esp_err_t nvs_set_u16 (nvs_handle handle, const char* key, uint16_t value);
    esp_err_t nvs_set_i32 (nvs_handle handle, const char* key, int32_t value);
    esp_err_t nvs_set_u32 (nvs_handle handle, const char* key, uint32_t value);
    esp_err_t nvs_set_i64 (nvs_handle handle, const char* key, int64_t value);
    esp_err_t nvs_set_u64 (nvs_handle handle, const char* key, uint64_t value);
    esp_err_t nvs_set_str (nvs_handle handle, const char* key, const char* value);
    /**
     * @brief       set variable length binary value for given key
     *
     * This family of functions set value for the key, given its name. Note that
     * actual storage will not be updated until nvs_commit function is called.
     *
     * @param[in]  handle  Handle obtained from nvs_open function.
     *                     Handles that were opened read only cannot be used.
     * @param[in]  key     Key name. Maximal length is 15 characters. Shouldn't be empty.
     * @param[in]  value   The value to set.
     * @param[in]  length  length of binary value to set, in bytes; Maximum length is
     *                     508000 bytes or (97.6% of the partition size - 4000) bytes
     *                     whichever is lower.
     *
     * @return
     *             - 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 value is too long
     */
    esp_err_t nvs_set_blob(nvs_handle handle, const char* key, const void* value, size_t length);

    数据读出

    /**@{*/
    /**
     * @brief      get value for given key
     *
     * These functions retrieve value for the key, given its name. If key does not
     * exist, or the requested variable type doesn't match the type which was used
     * when setting a value, an error is returned.
     *
     * In case of any error, out_value is not modified.
     *
     * All functions expect out_value to be a pointer to an already allocated variable
     * of the given type.
     *
     * code{c}
     * // Example of using nvs_get_i32:
     * int32_t max_buffer_size = 4096; // default value
     * esp_err_t err = nvs_get_i32(my_handle, "max_buffer_size", &max_buffer_size);
     * assert(err == ESP_OK || err == ESP_ERR_NVS_NOT_FOUND);
     * // if ESP_ERR_NVS_NOT_FOUND was returned, max_buffer_size will still
     * // have its default value.
     *
     * endcode
     *
     * @param[in]     handle     Handle obtained from nvs_open function.
     * @param[in]     key        Key name. Maximal length is determined by the underlying
     *                           implementation, but is guaranteed to be at least
     *                           15 characters. Shouldn't be empty.
     * @param         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.
     *
     * @return
     *             - 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
     */
    esp_err_t nvs_get_i8  (nvs_handle handle, const char* key, int8_t* out_value);
    esp_err_t nvs_get_u8  (nvs_handle handle, const char* key, uint8_t* out_value);
    esp_err_t nvs_get_i16 (nvs_handle handle, const char* key, int16_t* out_value);
    esp_err_t nvs_get_u16 (nvs_handle handle, const char* key, uint16_t* out_value);
    esp_err_t nvs_get_i32 (nvs_handle handle, const char* key, int32_t* out_value);
    esp_err_t nvs_get_u32 (nvs_handle handle, const char* key, uint32_t* out_value);
    esp_err_t nvs_get_i64 (nvs_handle handle, const char* key, int64_t* out_value);
    esp_err_t nvs_get_u64 (nvs_handle handle, const char* key, uint64_t* out_value);
    /**
     * @brief      get value for given key
     *
     * These functions retrieve value for the key, given its name. If key does not
     * exist, or the requested variable type doesn't match the type which was used
     * when setting a value, an error is returned.
     *
     * In case of any error, out_value is not modified.
     *
     * All functions expect out_value to be a pointer to an already allocated variable
     * of the given type.
     * 
     * nvs_get_str and nvs_get_blob functions support WinAPI-style length queries.
     * To get the size necessary to store the value, call nvs_get_str or nvs_get_blob
     * with zero out_value and non-zero pointer to length. Variable pointed to
     * by length argument will be set to the required length. For nvs_get_str,
     * this length includes the zero terminator. When calling nvs_get_str and
     * nvs_get_blob with non-zero out_value, length has to be non-zero and has to
     * point to the length available in out_value.
     * It is suggested that nvs_get/set_str is used for zero-terminated C strings, and
     * nvs_get/set_blob used for arbitrary data structures.
     *
     * code{c}
     * // Example (without error checking) of using nvs_get_str to get a string into dynamic array:
     * size_t required_size;
     * nvs_get_str(my_handle, "server_name", NULL, &required_size);
     * char* server_name = malloc(required_size);
     * nvs_get_str(my_handle, "server_name", server_name, &required_size);
     *
     * // Example (without error checking) of using nvs_get_blob to get a binary data
     * into a static array:
     * uint8_t mac_addr[6];
     * size_t size = sizeof(mac_addr);
     * nvs_get_blob(my_handle, "dst_mac_addr", mac_addr, &size);
     * endcode
     *
     * @param[in]     handle     Handle obtained from nvs_open function.
     * @param[in]     key        Key name. Maximal length is determined by the underlying
     *                           implementation, but is guaranteed to be at least
     *                           15 characters. Shouldn't be empty.
     * @param         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.
     * @param[inout]  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.
     *
     * @return
     *             - 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
     */
    /**@{*/
    esp_err_t nvs_get_str (nvs_handle handle, const char* key, char* out_value, size_t* length);
    esp_err_t nvs_get_blob(nvs_handle handle, const char* key, void* out_value, size_t* length);

    提交保存数据

    /**
     * @brief      Write any pending changes to non-volatile storage
     *
     * After setting any values, nvs_commit() must be called to ensure changes are written
     * to non-volatile storage. Individual implementations may write to storage at other times,
     * but this is not guaranteed.
     *
     * @param[in]  handle  Storage handle obtained with nvs_open.
     *                     Handles that were opened read only cannot be used.
     *
     * @return
     *             - 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
     */
    esp_err_t nvs_commit(nvs_handle handle);

    不透明句柄

    typedef uint32_t nvs_handle;
    
    #define ESP_ERR_NVS_BASE                    0x1100                     /*!< Starting number of error codes */
    #define ESP_ERR_NVS_NOT_INITIALIZED         (ESP_ERR_NVS_BASE + 0x01)  /*!< The storage driver is not initialized */
    #define ESP_ERR_NVS_NOT_FOUND               (ESP_ERR_NVS_BASE + 0x02)  /*!< Id namespace doesn’t exist yet and mode is NVS_READONLY */
    #define ESP_ERR_NVS_TYPE_MISMATCH           (ESP_ERR_NVS_BASE + 0x03)  /*!< The type of set or get operation doesn't match the type of value stored in NVS */
    #define ESP_ERR_NVS_READ_ONLY               (ESP_ERR_NVS_BASE + 0x04)  /*!< Storage handle was opened as read only */
    #define ESP_ERR_NVS_NOT_ENOUGH_SPACE        (ESP_ERR_NVS_BASE + 0x05)  /*!< There is not enough space in the underlying storage to save the value */
    #define ESP_ERR_NVS_INVALID_NAME            (ESP_ERR_NVS_BASE + 0x06)  /*!< Namespace name doesn’t satisfy constraints */
    #define ESP_ERR_NVS_INVALID_HANDLE          (ESP_ERR_NVS_BASE + 0x07)  /*!< Handle has been closed or is NULL */
    #define ESP_ERR_NVS_REMOVE_FAILED           (ESP_ERR_NVS_BASE + 0x08)  /*!< 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. */
    #define ESP_ERR_NVS_KEY_TOO_LONG            (ESP_ERR_NVS_BASE + 0x09)  /*!< Key name is too long */
    #define ESP_ERR_NVS_PAGE_FULL               (ESP_ERR_NVS_BASE + 0x0a)  /*!< Internal error; never returned by nvs API functions */
    #define ESP_ERR_NVS_INVALID_STATE           (ESP_ERR_NVS_BASE + 0x0b)  /*!< NVS is in an inconsistent state due to a previous error. Call nvs_flash_init and nvs_open again, then retry. */
    #define ESP_ERR_NVS_INVALID_LENGTH          (ESP_ERR_NVS_BASE + 0x0c)  /*!< String or blob length is not sufficient to store data */
    #define ESP_ERR_NVS_NO_FREE_PAGES           (ESP_ERR_NVS_BASE + 0x0d)  /*!< NVS partition doesn't contain any empty pages. This may happen if NVS partition was truncated. Erase the whole partition and call nvs_flash_init again. */
    #define ESP_ERR_NVS_VALUE_TOO_LONG          (ESP_ERR_NVS_BASE + 0x0e)  /*!< String or blob length is longer than supported by the implementation */
    #define ESP_ERR_NVS_PART_NOT_FOUND          (ESP_ERR_NVS_BASE + 0x0f)  /*!< Partition with specified name is not found in the partition table */
    
    #define ESP_ERR_NVS_NEW_VERSION_FOUND       (ESP_ERR_NVS_BASE + 0x10)  /*!< NVS partition contains data in new format and cannot be recognized by this version of code */
    #define ESP_ERR_NVS_XTS_ENCR_FAILED         (ESP_ERR_NVS_BASE + 0x11)  /*!< XTS encryption failed while writing NVS entry */
    #define ESP_ERR_NVS_XTS_DECR_FAILED         (ESP_ERR_NVS_BASE + 0x12)  /*!< XTS decryption failed while reading NVS entry */
    #define ESP_ERR_NVS_XTS_CFG_FAILED          (ESP_ERR_NVS_BASE + 0x13)  /*!< XTS configuration setting failed */
    #define ESP_ERR_NVS_XTS_CFG_NOT_FOUND       (ESP_ERR_NVS_BASE + 0x14)  /*!< XTS configuration not found */
    #define ESP_ERR_NVS_ENCR_NOT_SUPPORTED      (ESP_ERR_NVS_BASE + 0x15)  /*!< NVS encryption is not supported in this version */
    #define ESP_ERR_NVS_KEYS_NOT_INITIALIZED    (ESP_ERR_NVS_BASE + 0x16)  /*!< NVS key partition is uninitialized */
    #define ESP_ERR_NVS_CORRUPT_KEY_PART        (ESP_ERR_NVS_BASE + 0x17)  /*!< NVS key partition is corrupt */

     例子(保存系统重启次数):

     1 #include <Arduino.h>
     2 #include <stdio.h>
     3 #include "freertos/FreeRTOS.h"
     4 #include "freertos/task.h"
     5 #include "esp_system.h"
     6 #include "nvs_flash.h"
     7 #include "nvs.h"
     8 
     9 void setup()
    10 {
    11   Serial.begin(9600);
    12   // 初始化NVS,并检查初始化情况
    13   esp_err_t err = nvs_flash_init();
    14   if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
    15       // 如果NVS分区被占用则对其进行擦除
    16       // 并再次初始化
    17       ESP_ERROR_CHECK(nvs_flash_erase());
    18       err = nvs_flash_init();
    19   }
    20   ESP_ERROR_CHECK( err );
    21 
    22   // Open 打开NVS文件
    23   printf("
    ");
    24   printf("Opening Non-Volatile Storage (NVS) handle... ");
    25   nvs_handle my_handle; // 定义不透明句柄
    26   err = nvs_open("storage", NVS_READWRITE, &my_handle); // 打开文件
    27   if (err != ESP_OK) {
    28       printf("Error (%s) opening NVS handle!
    ", esp_err_to_name(err));
    29   } else {
    30       printf("Done
    ");
    31 
    32       // Read
    33       printf("Reading restart counter from NVS ... ");
    34       int32_t restart_counter = 0; // value will default to 0, if not set yet in NVS
    35       err = nvs_get_i32(my_handle, "restart_counter", &restart_counter);
    36       switch (err) {
    37           case ESP_OK:
    38               printf("Done
    ");
    39               printf("Restart counter = %d
    ", restart_counter);
    40               break;
    41           case ESP_ERR_NVS_NOT_FOUND:
    42               printf("The value is not initialized yet!
    ");
    43               break;
    44           default :
    45               printf("Error (%s) reading!
    ", esp_err_to_name(err));
    46       }
    47 
    48       // Write
    49       printf("Updating restart counter in NVS ... ");
    50       restart_counter++;
    51       err = nvs_set_i32(my_handle, "restart_counter", restart_counter);
    52       printf((err != ESP_OK) ? "Failed!
    " : "Done
    ");
    53 
    54       // Commit written value.
    55       // After setting any values, nvs_commit() must be called to ensure changes are written
    56       // to flash storage. Implementations may write to storage at other times,
    57       // but this is not guaranteed.
    58       printf("Committing updates in NVS ... ");
    59       err = nvs_commit(my_handle);
    60       printf((err != ESP_OK) ? "Failed!
    " : "Done
    ");
    61 
    62       // Close
    63       nvs_close(my_handle);
    64     }
    65 
    66     printf("
    ");
    67 
    68     // Restart module
    69     for (int i = 10; i >= 0; i--) {
    70         printf("Restarting in %d seconds...
    ", i);
    71         vTaskDelay(1000 / portTICK_PERIOD_MS);
    72     }
    73     printf("Restarting now.
    ");
    74     fflush(stdout);
    75     esp_restart();
    76 }
    77 
    78 void loop()
    79 {
    80   delay(0xFFFFFF);
    81 }

    资料来源:

      ESP32编程指南

      nvs介绍

  • 相关阅读:
    drf3
    字典的操作方法
    列表的操作方法
    字符串的操作方法
    while循环和基本运算符
    初识数据类型
    USDT相关
    带团队
    CentOS7更改时区及同步网络时间
    mac胡刷新dns
  • 原文地址:https://www.cnblogs.com/zy-cnblogs/p/13301330.html
Copyright © 2011-2022 走看看