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_t
、int8_t
、uint16_t
、int16_t
、uint32_t
、int32_t
、uint64_t
和 int64_t
;
◍ 以 \0
结尾的字符串;
◍ 可变长度的二进制数据 (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("\n"); 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!\n", esp_err_to_name(err)); 29 } else { 30 printf("Done\n"); 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\n"); 39 printf("Restart counter = %d\n", restart_counter); 40 break; 41 case ESP_ERR_NVS_NOT_FOUND: 42 printf("The value is not initialized yet!\n"); 43 break; 44 default : 45 printf("Error (%s) reading!\n", 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!\n" : "Done\n"); 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!\n" : "Done\n"); 61 62 // Close 63 nvs_close(my_handle); 64 } 65 66 printf("\n"); 67 68 // Restart module 69 for (int i = 10; i >= 0; i--) { 70 printf("Restarting in %d seconds...\n", i); 71 vTaskDelay(1000 / portTICK_PERIOD_MS); 72 } 73 printf("Restarting now.\n"); 74 fflush(stdout); 75 esp_restart(); 76 } 77 78 void loop() 79 { 80 delay(0xFFFFFF); 81 }
资料来源: