Merge branch 'feat/nvs_iteration_statistics_examples' into 'master'

feat(examples/storage): add nvs statistics and iteration examples

Closes DOC-12742

See merge request espressif/esp-idf!42327
This commit is contained in:
Martin Vychodil
2025-11-24 17:54:31 +08:00
20 changed files with 756 additions and 0 deletions
@@ -190,6 +190,8 @@ Points to keep in mind when developing NVS related code:
- :example:`storage/nvs/nvs_rw_value` demonstrates how to use NVS to write and read a single integer value.
- :example:`storage/nvs/nvs_rw_blob` demonstrates how to use NVS to write and read a blob.
- :example:`storage/nvs/nvs_statistics` demonstrates how to obtain and interpret NVS usage statistics: free/used/available/total number of entries and number of namespaces in given NVS partition.
- :example:`storage/nvs/nvs_iteration` demonstrates how to iterate over entries of specific (or any) NVS data type and how to obtain info about such entries.
- :example:`security/nvs_encryption_hmac` demonstrates NVS encryption using the HMAC peripheral, where the encryption keys are derived from the HMAC key burnt in eFuse.
- :example:`security/flash_encryption` demonstrates the flash encryption workflow including NVS partition creation and usage.
+4
View File
@@ -53,6 +53,10 @@ Examples
- Shows the use of the C-style API to read and write integer data types in NVS flash.
* - :example:`nvs_rw_value_cxx <storage/nvs/nvs_rw_value_cxx>`
- Shows the use of the C++-style API to read and write integer data types in NVS flash.
* - :example:`nvs_statistics <storage/nvs/nvs_statistics>`
- Shows the use of the C-style API to obtain NVS usage statistics: free/used/available/total number of entries and number of namespaces in given NVS partition.
* - :example:`nvs_iteration <storage/nvs/nvs_iteration>`
- Shows the use of the C-style API to iterate over entries of specific (or any) NVS data type and how to obtain info about such entries.
* - :example:`nvs_bootloader <storage/nvs/nvs_bootloader>`
- Shows the use of the API available to the bootloader code to read NVS data.
* - :example:`nvsgen <storage/nvs/nvsgen>`
@@ -177,6 +177,22 @@ You can find code examples in the :example:`storage/nvs` directory of ESP-IDF ex
This example does exactly the same as :example:`storage/nvs/nvs_rw_value`, except that it uses the C++ NVS handle class.
:example:`storage/nvs/nvs_statistics`
This example demonstrates how to obtain and interpret NVS usage statistics: free/used/available/total number of entries and number of namespaces in given NVS partition.
Default NVS partition is erased for a clean run of this example. Then mock data string values are written.
Usage statistics are obtained prior to and post writing, with the differences being compared to expected values of newly used entries.
:example:`storage/nvs/nvs_iteration`
This example demonstrates how to iterate over entries of specific (or any) NVS data type and how to obtain info about such entries.
Default NVS partition is erased for a clean run of this example. Then mock data consisting of different NVS integer data types are written.
After that, the example iterates over each individual data type as well as the generic ``NVS_TYPE_ANY`` type, and logs the information obtained from each iteration.
Internals
---------
@@ -190,6 +190,8 @@ NVS 具有如下特性:
- :example:`storage/nvs/nvs_rw_value` 演示了如何写入和读取一个整数值。
- :example:`storage/nvs/nvs_rw_blob` 演示如何写入和读取一个 blob。
- :example:`storage/nvs/nvs_statistics` 演示了如何获取并解读 NVS 使用情况统计信息:包括指定 NVS 分区中的空闲、已用、可用、总条目数、以及命名空间数量。
- :example:`storage/nvs/nvs_iteration` 演示了如何遍历特定(或任意)NVS 数据类型的条目,以及如何获取这些条目的相关信息。
- :example:`security/nvs_encryption_hmac` 演示了如何用 HMAC 外设进行 NVS 加密,并通过 efuse 中的 HMAC 密钥生成加密密钥。
- :example:`security/flash_encryption` 演示了如何进行 flash 加密,包括创建和使用 NVS 分区。
@@ -53,6 +53,10 @@
- 演示了如何在 NVS flash 中使用 C 语言 API 读写整数数据类型。
* - :example:`nvs_rw_value <storage/nvs/nvs_rw_value_cxx>`
- 演示了如何在 NVS flash 中使用 C++ 语言 API 读写整数数据类型。
* - :example:`nvs_statistics <storage/nvs/nvs_statistics>`
- 演示了如何使用 C 风格 API 获取 NVS 使用情况统计信息,包括指定 NVS 分区中的空闲、已用、可用、总条目数、以及命名空间数量。
* - :example:`nvs_iteration <storage/nvs/nvs_iteration>`
- 演示了如何使用 C 风格 API 遍历特定(或任意)NVS 数据类型的条目,以及如何获取这些条目的相关信息。
* - :example:`nvs_bootloader <storage/nvs/nvs_bootloader>`
- 演示了如何使用引导加载程序代码中可用的 API 来读取 NVS 数据。
* - :example:`nvsgen <storage/nvs/nvsgen>`
@@ -177,6 +177,22 @@ ESP-IDF :example:`storage/nvs` 目录下提供了数个代码示例:
这个例子与 :example:`storage/nvs/nvs_rw_value` 完全一样,只是使用了 C++ 的 NVS 句柄类。
:example:`storage/nvs/nvs_statistics`
该示例演示了如何获取并解读 NVS 使用情况统计信息:包括指定 NVS 分区中的空闲、已用、可用、总条目数、以及命名空间数量。
默认的 NVS 分区会在运行本示例前被擦除,以确保干净的运行环境。随后,会写入模拟的字符串类型数据。
在写入数据前后分别获取使用情况统计信息,并将两者的差异与新占用条目的预期值进行比较。
:example:`storage/nvs/nvs_iteration`
该示例演示了如何遍历特定(或任意)NVS 数据类型的条目,以及如何获取这些条目的相关信息。
默认的 NVS 分区会在运行本示例前被擦除,以确保干净的运行环境。随后,会写入包含不同 NVS 整型数据类型的模拟数据。
之后,本示例会遍历各个数据类型以及通用的 ``NVS_TYPE_ANY`` 类型,并记录在每次遍历过程中获取到的信息。
内部实现
---------
+2
View File
@@ -18,6 +18,8 @@ The examples are grouped into sub-directories by category. Each category directo
* `nvs_rw_value` example demonstrates how to read and write a single integer value using NVS.
* `nvs_rw_value_cxx` example demonstrates how to read and write a single integer value using NVS (it uses the C++ NVS handle API).
* `nvs_console` example demonstrates how to use NVS through an interactive console interface.
* `nvs_statistics` example demonstrates how to obtain and interpret stats about used/available NVS storage entries in given NVS partition.
* `nvs_iteration` example demonstrates iterating over entries of specific (or any) data type in given namespace, and the info to be obtained about the entries while doing so.
* `partition_api` examples demonstrate how to use different partition APIs.
* `parttool` example demonstrates common operations the partitions tool allows the user to perform.
* `sd_card` examples demonstrate how to use an SD card with an ESP device.
@@ -17,6 +17,12 @@ examples/storage/nvs/nvs_console:
disable_test:
- if: IDF_TARGET not in ["esp32", "esp32c3"]
reason: only one target per arch needed
examples/storage/nvs/nvs_iteration:
depends_components:
- nvs_flash
disable_test:
- if: IDF_TARGET not in ["esp32", "esp32c3"]
reason: only one target per arch needed
examples/storage/nvs/nvs_rw_blob:
depends_components:
- nvs_flash
@@ -38,6 +44,13 @@ examples/storage/nvs/nvs_rw_value_cxx:
- if: IDF_TARGET not in ["esp32", "esp32c3"]
reason: only one target per arch needed
examples/storage/nvs/nvs_statistics:
depends_components:
- nvs_flash
disable_test:
- if: IDF_TARGET not in ["esp32", "esp32c3"]
reason: only one target per arch needed
examples/storage/nvs/nvsgen:
depends_components:
- nvs_flash
@@ -0,0 +1,8 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.22)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
idf_build_set_property(MINIMAL_BUILD ON)
project(nvs_iteration)
@@ -0,0 +1,69 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- |
# Non-Volatile Storage (NVS) Iteration Example
This example showcases how to iterate and obtain info about NVS entries of a specific (or any) NVS datatype.
Default "nvs" partition is first erased to allow for clean example run, followed by writing 2 sets of key value pairs of different types to NVS storage.
After that, iteration is performed over the individual data types, as well as the generic `NVS_TYPE_ANY`, and relevant entry info gained from iteration is verbosely logged.
Iterator creation in this example is performed by [nvs_entry_find()](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/storage/nvs_flash.html#_CPPv414nvs_entry_findPKcPKc10nvs_type_tP14nvs_iterator_t) and accessing entry info by [nvs_entry_info()](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/storage/nvs_flash.html#_CPPv414nvs_entry_infoK14nvs_iterator_tP16nvs_entry_info_t).
Detailed functional description of NVS and API is provided in [documentation](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/storage/nvs_flash.html).
## How to use example
### Hardware required
This example does not require any special hardware, and can be run on any common development board.
### Build and flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
```
...
I (265) nvs_iteration_example: Erasing the contents of the default NVS partition...
I (595) nvs_iteration_example: Opening Non-Volatile Storage (NVS) handle for namespace '_mock_data'...
I (605) nvs_iteration_example: Writing u32 mock data key-value pairs to NVS namespace '_mock_data'...
I (605) nvs_iteration_example: Wrote 7 u32 mock data key-value pairs to NVS namespace '_mock_data'.
I (615) nvs_iteration_example: Writing i8 mock data key-value pairs to NVS namespace '_mock_data'...
I (625) nvs_iteration_example: Wrote 7 i8 mock data key-value pairs to NVS namespace '_mock_data'.
I (625) nvs_iteration_example: Committing data in NVS namespace '_mock_data'...
I (635) nvs_iteration_example: NVS handle for namespace '_mock_data' closed.
I (645) nvs_iteration_example: Iterating over NVS_TYPE_U32 data in namespace '_mock_data'...
I (655) nvs_iteration_example: Entry info: key 'system_startup' for data type type NVS_TYPE_U32 in namespace '_mock_data'
I (665) nvs_iteration_example: Entry info: key 'user_login' for data type type NVS_TYPE_U32 in namespace '_mock_data'
I (675) nvs_iteration_example: Entry info: key 'data_backup' for data type type NVS_TYPE_U32 in namespace '_mock_data'
I (685) nvs_iteration_example: Entry info: key 'sched_update' for data type type NVS_TYPE_U32 in namespace '_mock_data'
I (695) nvs_iteration_example: Entry info: key 'db_cleanup' for data type type NVS_TYPE_U32 in namespace '_mock_data'
I (705) nvs_iteration_example: Entry info: key 'security_scan' for data type type NVS_TYPE_U32 in namespace '_mock_data'
I (715) nvs_iteration_example: Entry info: key 'system_shutdown' for data type type NVS_TYPE_U32 in namespace '_mock_data'
I (725) nvs_iteration_example: Iterated over 7 entries.
I (725) nvs_iteration_example: Iterating over NVS_TYPE_I8 data in namespace '_mock_data'...
I (735) nvs_iteration_example: Entry info: key 'temperature' for data type type NVS_TYPE_I8 in namespace '_mock_data'
I (745) nvs_iteration_example: Entry info: key 'humidity' for data type type NVS_TYPE_I8 in namespace '_mock_data'
I (755) nvs_iteration_example: Entry info: key 'light_level' for data type type NVS_TYPE_I8 in namespace '_mock_data'
I (765) nvs_iteration_example: Entry info: key 'motion_detected' for data type type NVS_TYPE_I8 in namespace '_mock_data'
I (775) nvs_iteration_example: Entry info: key 'battery_level' for data type type NVS_TYPE_I8 in namespace '_mock_data'
I (785) nvs_iteration_example: Entry info: key 'signal_strength' for data type type NVS_TYPE_I8 in namespace '_mock_data'
I (795) nvs_iteration_example: Entry info: key 'error_code' for data type type NVS_TYPE_I8 in namespace '_mock_data'
I (805) nvs_iteration_example: Iterated over 7 entries.
I (815) nvs_iteration_example: Iterating over NVS_TYPE_ANY data in namespace '_mock_data'...
I (825) nvs_iteration_example: Iterated over 14 entries.
I (825) nvs_iteration_example: Returning from app_main().
I (835) main_task: Returned from app_main()
...
```
@@ -0,0 +1,3 @@
idf_component_register(SRCS "nvs_iteration_example.c"
PRIV_REQUIRES nvs_flash
INCLUDE_DIRS ".")
@@ -0,0 +1,220 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* Non-Volatile Storage (NVS) Iteration Example
For other examples please check:
https://github.com/espressif/esp-idf/tree/master/examples
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_check.h"
#include "esp_system.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "nvs.h"
#define MOCK_DATA_NAMESPACE "_mock_data"
static const char *TAG = "nvs_iteration_example";
static const char *u32_mock_data_keys[] = {
"system_startup",
"user_login",
"data_backup",
"sched_update",
"db_cleanup",
"security_scan",
"system_shutdown"
};
// Unix timestamps
static const uint32_t u32_mock_data_values[] = {
1704067200,
1704067260,
1704067320,
1704067440,
1704067500,
1704067560,
1704067620
};
static const size_t u32_mock_data_count = sizeof(u32_mock_data_keys) / sizeof(u32_mock_data_keys[0]);
static const char *i8_mock_data_keys[] = {
"temperature",
"humidity",
"light_level",
"motion_detected",
"battery_level",
"signal_strength",
"error_code"
};
static const int8_t i8_mock_data_values[] = {
22,
45,
-5,
1,
95,
-10,
-42
};
static const size_t i8_mock_data_count = sizeof(i8_mock_data_keys) / sizeof(i8_mock_data_keys[0]);
static esp_err_t save_mock_data()
{
ESP_LOGI(TAG, "Opening Non-Volatile Storage (NVS) handle for namespace '%s'...", MOCK_DATA_NAMESPACE);
nvs_handle_t my_handle;
esp_err_t ret = nvs_open(MOCK_DATA_NAMESPACE, NVS_READWRITE, &my_handle);
ESP_RETURN_ON_ERROR(ret, TAG, "Error (%s) opening NVS handle for namespace '%s'!", esp_err_to_name(ret), MOCK_DATA_NAMESPACE);
size_t successfully_written_entries = 0;
ESP_LOGI(TAG, "Writing u32 mock data key-value pairs to NVS namespace '%s'...", MOCK_DATA_NAMESPACE);
for (int i = 0; i < u32_mock_data_count; i++) {
// Write uint32_t values
ret = nvs_set_u32(my_handle, u32_mock_data_keys[i], u32_mock_data_values[i]);
// Don't break/abort on error, try to continue writing
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) writing '%s':'%u' pair!", esp_err_to_name(ret), u32_mock_data_keys[i], u32_mock_data_values[i]);
} else {
successfully_written_entries++;
}
}
ESP_LOGI(TAG, "Wrote %u u32 mock data key-value pairs to NVS namespace '%s'.", successfully_written_entries, MOCK_DATA_NAMESPACE);
successfully_written_entries = 0;
ESP_LOGI(TAG, "Writing i8 mock data key-value pairs to NVS namespace '%s'...", MOCK_DATA_NAMESPACE);
for (int i = 0; i < i8_mock_data_count; i++) {
// Write int8_t values
ret = nvs_set_i8(my_handle, i8_mock_data_keys[i], i8_mock_data_values[i]);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) writing '%s':'%d' pair!", esp_err_to_name(ret), i8_mock_data_keys[i], i8_mock_data_values[i]);
} else {
successfully_written_entries++;
}
}
ESP_LOGI(TAG, "Wrote %u i8 mock data key-value pairs to NVS namespace '%s'.", successfully_written_entries, MOCK_DATA_NAMESPACE);
ESP_LOGI(TAG, "Committing data in NVS namespace '%s'...", MOCK_DATA_NAMESPACE);
ret = nvs_commit(my_handle);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) committing data for namespace '%s'!", esp_err_to_name(ret), MOCK_DATA_NAMESPACE);
}
// Close the storage handle, freeing allocated resources
nvs_close(my_handle);
ESP_LOGI(TAG, "NVS handle for namespace '%s' closed.", MOCK_DATA_NAMESPACE);
return ESP_OK;
}
static esp_err_t iterate_over_mock_data()
{
ESP_LOGI(TAG, "Iterating over NVS_TYPE_U32 data in namespace '%s'...", MOCK_DATA_NAMESPACE);
nvs_iterator_t it = NULL;
size_t entries_iterated = 0;
// Find only entries of type NVS_TYPE_U32 and iterate over them
esp_err_t ret = nvs_entry_find(NVS_DEFAULT_PART_NAME, MOCK_DATA_NAMESPACE, NVS_TYPE_U32, &it);
while (ret == ESP_OK) {
nvs_entry_info_t info;
ret = nvs_entry_info(it, &info);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) obtaining entry info in namespace '%s'!", esp_err_to_name(ret), MOCK_DATA_NAMESPACE);
} else {
ESP_LOGI(TAG, "Entry info: key '%s' for data type type %s in namespace '%s'", info.key, info.type == NVS_TYPE_U32 ? "NVS_TYPE_U32" : "Error: not the expected NVS_TYPE_U32!", info.namespace_name);
}
entries_iterated++;
// ret gets checked in the while() condition, terminating the loop if it's not ESP_OK
ret = nvs_entry_next(&it);
// ESP_ERR_NVS_NOT_FOUND is expected to be returned when there is no next entry to iterate over (valid return value for last entry)
if (ret != ESP_OK && ret != ESP_ERR_NVS_NOT_FOUND) {
ESP_LOGE(TAG, "Error (%s) obtaining next entry in namespace '%s'!", esp_err_to_name(ret), MOCK_DATA_NAMESPACE);
break;
}
}
nvs_release_iterator(it);
ESP_LOGI(TAG, "Iterated over %u entries.", entries_iterated);
ESP_LOGI(TAG, "Iterating over NVS_TYPE_I8 data in namespace '%s'...", MOCK_DATA_NAMESPACE);
it = NULL;
entries_iterated = 0;
// Find only entries of type NVS_TYPE_I8 and iterate over them
ret = nvs_entry_find(NVS_DEFAULT_PART_NAME, MOCK_DATA_NAMESPACE, NVS_TYPE_I8, &it);
while (ret == ESP_OK) {
nvs_entry_info_t info;
ret = nvs_entry_info(it, &info);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) obtaining entry info in namespace '%s'!", esp_err_to_name(ret), MOCK_DATA_NAMESPACE);
} else {
ESP_LOGI(TAG, "Entry info: key '%s' for data type type %s in namespace '%s'", info.key, info.type == NVS_TYPE_I8 ? "NVS_TYPE_I8" : "Error: not the expected NVS_TYPE_I8!", info.namespace_name);
}
entries_iterated++;
ret = nvs_entry_next(&it);
if (ret != ESP_OK && ret != ESP_ERR_NVS_NOT_FOUND) {
ESP_LOGE(TAG, "Error (%s) obtaining next entry in namespace '%s'!", esp_err_to_name(ret), MOCK_DATA_NAMESPACE);
break;
}
}
nvs_release_iterator(it);
ESP_LOGI(TAG, "Iterated over %u entries.", entries_iterated);
ESP_LOGI(TAG, "Iterating over NVS_TYPE_ANY data in namespace '%s'...", MOCK_DATA_NAMESPACE);
it = NULL;
entries_iterated = 0;
// Iterate over NVS_TYPE_ANY entries only to count them
ret = nvs_entry_find(NVS_DEFAULT_PART_NAME, MOCK_DATA_NAMESPACE, NVS_TYPE_ANY, &it);
while (ret == ESP_OK) {
entries_iterated++;
ret = nvs_entry_next(&it);
if (ret != ESP_OK && ret != ESP_ERR_NVS_NOT_FOUND) {
ESP_LOGE(TAG, "Error (%s) obtaining next entry in namespace '%s'!", esp_err_to_name(ret), MOCK_DATA_NAMESPACE);
break;
}
}
nvs_release_iterator(it);
ESP_LOGI(TAG, "Iterated over %u entries.", entries_iterated);
return ESP_OK;
}
void app_main(void)
{
ESP_LOGI(TAG, "Erasing the contents of the default NVS partition...");
ESP_ERROR_CHECK(nvs_flash_erase());
// Initialize NVS on default "nvs" partition
esp_err_t ret = nvs_flash_init();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) initializing NVS on default 'nvs' partition!", esp_err_to_name(ret));
return;
}
// Write mock data
ret = save_mock_data();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) saving mock data to namespace '%s'!", esp_err_to_name(ret), MOCK_DATA_NAMESPACE);
}
// Iterate over mock data
ret = iterate_over_mock_data();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) iterating over namespace '%s'!", esp_err_to_name(ret), MOCK_DATA_NAMESPACE);
}
ESP_LOGI(TAG, "Returning from app_main().");
}
@@ -0,0 +1,16 @@
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
import pytest
from pytest_embedded import Dut
from pytest_embedded_idf.utils import idf_parametrize
@pytest.mark.generic
@idf_parametrize('target', ['esp32', 'esp32c3'], indirect=['target'])
def test_examples_nvs_iteration(dut: Dut) -> None:
dut.expect("Iterating over NVS_TYPE_U32 data in namespace '_mock_data'...", timeout=5)
dut.expect('Iterated over 7 entries.', timeout=5)
dut.expect("Iterating over NVS_TYPE_I8 data in namespace '_mock_data'...", timeout=5)
dut.expect('Iterated over 7 entries.', timeout=5)
dut.expect("Iterating over NVS_TYPE_ANY data in namespace '_mock_data'...", timeout=5)
dut.expect('Iterated over 14 entries.', timeout=5)
@@ -0,0 +1,8 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.22)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
idf_build_set_property(MINIMAL_BUILD ON)
project(nvs_statistics)
@@ -0,0 +1,119 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-H21 | ESP32-H4 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- |
# Non-Volatile Storage (NVS) Statistics Example
This example demonstrates the usage of obtaining and interpreting statistics about the a given NVS partition, namely free/used/available/total entries and namespace count.
The default "nvs" partition is first erased, then a mock string data configuration is written to 2 different namespaces, followed by checking the changed statistics and mainly the number of newly used NVS entries.
Statistics obtained via [nvs_get_stats()](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/storage/nvs_flash.html#_CPPv413nvs_get_statsPKcP11nvs_stats_t) are the following:
| | |
| -------------- | ------------------- |
| `available entries` | Essential statistic, specifies free non-reserved entries available for data storage. |
| `used entries` | Includes 1 overhead entry per namespace + actual data entries used by data storage. |
| `free entries` | Free entries are both the free available to the user entries, and free but internally reserved (not available to the user). |
| `total entries` | Number of all entries (free + used). |
Detailed functional description of NVS and API is provided in [documentation](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/storage/nvs_flash.html).
## How to use example
### Hardware required
This example does not require any special hardware, and can be run on any common development board.
### Build and flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
```
...
I (265) nvs_statistics_example: Erasing the contents of the default NVS partition...
I (475) nvs_statistics_example: Opening Non-Volatile Storage (NVS) handle for namespace '_mock_data'...
I (485) nvs_statistics_example: Getting NVS statistics...
I (485) nvs_statistics_example: NVS statistics:
I (485) nvs_statistics_example: Used NVS entries: 1
I (485) nvs_statistics_example: Free NVS entries: 755
I (495) nvs_statistics_example: Available NVS entries: 629
I (495) nvs_statistics_example: Total NVS entries: 756
I (505) nvs_statistics_example: Namespace count: 1
I (505) nvs_statistics_example: Writing mock data key-value pairs to NVS namespace '_mock_data'...
I (525) nvs_statistics_example: Committing data in NVS namespace '_mock_data'...
I (525) nvs_statistics_example: Getting post-commit NVS statistics...
I (525) nvs_statistics_example: NVS statistics:
I (535) nvs_statistics_example: Used NVS entries: 30
I (535) nvs_statistics_example: Free NVS entries: 726
I (545) nvs_statistics_example: Available NVS entries: 600
I (545) nvs_statistics_example: Total NVS entries: 756
I (555) nvs_statistics_example: Namespace count: 1
I (555) nvs_statistics_example: Newly used entries match expectation.
I (565) nvs_statistics_example: Newly used entries: 29, expected: 29.
I (575) nvs_statistics_example: NVS handle for namespace '_mock_data' closed.
I (575) nvs_statistics_example: Opening Non-Volatile Storage (NVS) handle for namespace '_mock_data'...
I (585) nvs_statistics_example: Reading stored data from NVS namespace '_mock_data'...
I (595) nvs_statistics_example: Read key-value pair from NVS: 'wifi_ssid':'HomeNetwork'
I (605) nvs_statistics_example: Read key-value pair from NVS: 'wifi_pass':'MySecretPass'
I (605) nvs_statistics_example: Read key-value pair from NVS: 'dev_name':'LivingRoomThermostat'
I (615) nvs_statistics_example: Read key-value pair from NVS: 'temp_unit':'Celsius'
I (625) nvs_statistics_example: Read key-value pair from NVS: 'target_temp':'22'
I (635) nvs_statistics_example: Read key-value pair from NVS: 'eco_mode':'false'
I (635) nvs_statistics_example: Read key-value pair from NVS: 'fw_version':'1.2.3'
I (645) nvs_statistics_example: Read key-value pair from NVS: 'led_bright':'80'
I (655) nvs_statistics_example: Read key-value pair from NVS: 'auto_update':'true'
I (665) nvs_statistics_example: Read key-value pair from NVS: 'last_sync':'2025-01-01T08:00:00Z'
I (665) nvs_statistics_example: Read key-value pair from NVS: 'user_lang':'en'
I (675) nvs_statistics_example: Read key-value pair from NVS: 'long_token':'2f8c1e7b5a4d9c6e3b0f1a8e5d7c2b6f4e1a9c7b'
I (685) nvs_statistics_example: Read key-value pair from NVS: 'very_long_token':'7e2b1c9f5a4d8e3b0f1a6c7e2d9b5a4c8e1f7b2d6c3a9e5b0f1a8c7e2d9b5a4c8e1f7b2d6c3a9e5b'
I (705) nvs_statistics_example: NVS handle for namespace '_mock_data' closed.
I (705) nvs_statistics_example: Opening Non-Volatile Storage (NVS) handle for namespace '_mock_backup'...
I (715) nvs_statistics_example: Getting NVS statistics...
I (725) nvs_statistics_example: NVS statistics:
I (725) nvs_statistics_example: Used NVS entries: 31
I (735) nvs_statistics_example: Free NVS entries: 725
I (735) nvs_statistics_example: Available NVS entries: 599
I (745) nvs_statistics_example: Total NVS entries: 756
I (745) nvs_statistics_example: Namespace count: 2
I (755) nvs_statistics_example: Writing mock data key-value pairs to NVS namespace '_mock_backup'...
I (765) nvs_statistics_example: Committing data in NVS namespace '_mock_backup'...
I (765) nvs_statistics_example: Getting post-commit NVS statistics...
I (775) nvs_statistics_example: NVS statistics:
I (775) nvs_statistics_example: Used NVS entries: 60
I (785) nvs_statistics_example: Free NVS entries: 696
I (785) nvs_statistics_example: Available NVS entries: 570
I (795) nvs_statistics_example: Total NVS entries: 756
I (795) nvs_statistics_example: Namespace count: 2
I (805) nvs_statistics_example: Newly used entries match expectation.
I (805) nvs_statistics_example: Newly used entries: 29, expected: 29.
I (815) nvs_statistics_example: NVS handle for namespace '_mock_backup' closed.
I (825) nvs_statistics_example: Opening Non-Volatile Storage (NVS) handle for namespace '_mock_backup'...
I (835) nvs_statistics_example: Reading stored data from NVS namespace '_mock_backup'...
I (835) nvs_statistics_example: Read key-value pair from NVS: 'wifi_ssid':'HomeNetwork'
I (845) nvs_statistics_example: Read key-value pair from NVS: 'wifi_pass':'MySecretPass'
I (855) nvs_statistics_example: Read key-value pair from NVS: 'dev_name':'LivingRoomThermostat'
I (865) nvs_statistics_example: Read key-value pair from NVS: 'temp_unit':'Celsius'
I (865) nvs_statistics_example: Read key-value pair from NVS: 'target_temp':'22'
I (875) nvs_statistics_example: Read key-value pair from NVS: 'eco_mode':'false'
I (885) nvs_statistics_example: Read key-value pair from NVS: 'fw_version':'1.2.3'
I (895) nvs_statistics_example: Read key-value pair from NVS: 'led_bright':'80'
I (895) nvs_statistics_example: Read key-value pair from NVS: 'auto_update':'true'
I (905) nvs_statistics_example: Read key-value pair from NVS: 'last_sync':'2025-01-01T08:00:00Z'
I (915) nvs_statistics_example: Read key-value pair from NVS: 'user_lang':'en'
I (925) nvs_statistics_example: Read key-value pair from NVS: 'long_token':'2f8c1e7b5a4d9c6e3b0f1a8e5d7c2b6f4e1a9c7b'
I (935) nvs_statistics_example: Read key-value pair from NVS: 'very_long_token':'7e2b1c9f5a4d8e3b0f1a6c7e2d9b5a4c8e1f7b2d6c3a9e5b0f1a8c7e2d9b5a4c8e1f7b2d6c3a9e5b'
I (945) nvs_statistics_example: NVS handle for namespace '_mock_backup' closed.
I (955) nvs_statistics_example: Returning from app_main().
I (955) main_task: Returned from app_main()
...
```
@@ -0,0 +1,3 @@
idf_component_register(SRCS "nvs_statistics_example.c"
PRIV_REQUIRES nvs_flash
INCLUDE_DIRS ".")
@@ -0,0 +1,234 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* Non-Volatile Storage (NVS) Statistics Example
For other examples please check:
https://github.com/espressif/esp-idf/tree/master/examples
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_check.h"
#include "esp_system.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "nvs.h"
#define MOCK_DATA_NAMESPACE "_mock_data"
#define MOCK_DATA_BACKUP_NAMESPACE "_mock_backup"
static const char *TAG = "nvs_statistics_example";
// Maximum key character length is 15 (NVS_KEY_NAME_MAX_SIZE-1)
static const char* mock_data_keys[] = {
"wifi_ssid",
"wifi_pass",
"dev_name",
"temp_unit",
"target_temp",
"eco_mode",
"fw_version",
"led_bright",
"auto_update",
"last_sync",
"user_lang",
"long_token",
"very_long_token"
};
// String values are limited in length by NVS implementation to 4000 bytes (including null character)
// Each string value occupies 1 overhead NVS entry + 1 NVS entry per each 32 characters including null character
#define CHARS_PER_STRING_VALUE_ENTRY 32
static const char* mock_data_values[] = {
"HomeNetwork",
"MySecretPass",
"LivingRoomThermostat",
"Celsius",
"22",
"false",
"1.2.3",
"80",
"true",
"2025-01-01T08:00:00Z",
"en",
"2f8c1e7b5a4d9c6e3b0f1a8e5d7c2b6f4e1a9c7b",
"7e2b1c9f5a4d8e3b0f1a6c7e2d9b5a4c8e1f7b2d6c3a9e5b0f1a8c7e2d9b5a4c8e1f7b2d6c3a9e5b"
};
static const size_t mock_data_count = sizeof(mock_data_keys) / sizeof(mock_data_keys[0]);
static void print_nvs_stats(nvs_stats_t *nvs_stats)
{
ESP_LOGI(TAG, "NVS statistics:");
ESP_LOGI(TAG, "Used NVS entries: %u", nvs_stats->used_entries);
ESP_LOGI(TAG, "Free NVS entries: %u", nvs_stats->free_entries);
ESP_LOGI(TAG, "Available NVS entries: %u", nvs_stats->available_entries);
ESP_LOGI(TAG, "Total NVS entries: %u", nvs_stats->total_entries);
ESP_LOGI(TAG, "Namespace count: %u", nvs_stats->namespace_count);
}
static size_t calculate_expected_entries_for_string_array(const char** string_array, size_t array_size)
{
size_t expected_newly_used_entries = 0;
for (int i = 0; i < array_size; i++) {
// Each string occupies 1 overhead entry + 1 entry per each 32 characters including null character
size_t str_len = strlen(string_array[i]);
size_t span = 1 + ((str_len + CHARS_PER_STRING_VALUE_ENTRY - 1) / CHARS_PER_STRING_VALUE_ENTRY); // 1 overhead + integer division rounding up
expected_newly_used_entries += span;
}
return expected_newly_used_entries;
}
static esp_err_t save_mock_data_to_namespace(const char* namespace_name)
{
ESP_LOGI(TAG, "Opening Non-Volatile Storage (NVS) handle for namespace '%s'...", namespace_name);
nvs_handle_t my_handle;
// Opening NVS storage handle uses up 1 entry for the namespace
esp_err_t ret = nvs_open(namespace_name, NVS_READWRITE, &my_handle);
ESP_RETURN_ON_ERROR(ret, TAG, "Error (%s) opening NVS handle for namespace '%s'!", esp_err_to_name(ret), namespace_name);
ESP_LOGI(TAG, "Getting NVS statistics...");
nvs_stats_t nvs_stats;
ret = nvs_get_stats(NVS_DEFAULT_PART_NAME, &nvs_stats);
ESP_GOTO_ON_ERROR(ret, nvs_close_label, TAG, "Error (%s) getting NVS statistics!", esp_err_to_name(ret));
// Print the pre-write NVS statistics
print_nvs_stats(&nvs_stats);
ESP_LOGI(TAG, "Writing mock data key-value pairs to NVS namespace '%s'...", namespace_name);
for (int i = 0; i < mock_data_count; i++) {
// Write string values
ret = nvs_set_str(my_handle, mock_data_keys[i], mock_data_values[i]);
// Don't break/abort on error, try to continue writing
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) writing '%s':'%s' pair!", esp_err_to_name(ret), mock_data_keys[i], mock_data_values[i]);
}
}
ESP_LOGI(TAG, "Committing data in NVS namespace '%s'...", namespace_name);
ret = nvs_commit(my_handle);
ESP_GOTO_ON_ERROR(ret, nvs_close_label, TAG, "Error (%s) committing data for namespace '%s'!", esp_err_to_name(ret), namespace_name);
ESP_LOGI(TAG, "Getting post-commit NVS statistics...");
nvs_stats_t new_nvs_stats;
ret = nvs_get_stats(NVS_DEFAULT_PART_NAME, &new_nvs_stats);
ESP_GOTO_ON_ERROR(ret, nvs_close_label, TAG, "Error (%s) getting NVS statistics!", esp_err_to_name(ret));
// Print the post-write NVS statistics
print_nvs_stats(&new_nvs_stats);
size_t expected_newly_used_entries = calculate_expected_entries_for_string_array(mock_data_values, mock_data_count);
if (new_nvs_stats.used_entries - nvs_stats.used_entries != expected_newly_used_entries) {
ESP_LOGE(TAG, "Newly used entries: %u, expected: %u.",
new_nvs_stats.used_entries - nvs_stats.used_entries,
expected_newly_used_entries);
} else {
ESP_LOGI(TAG, "Newly used entries match expectation.");
ESP_LOGI(TAG, "Newly used entries: %u, expected: %u.",
new_nvs_stats.used_entries - nvs_stats.used_entries,
expected_newly_used_entries);
}
nvs_close_label:
// Close the storage handle, freeing allocated resources
nvs_close(my_handle);
ESP_LOGI(TAG, "NVS handle for namespace '%s' closed.", namespace_name);
return ESP_OK;
}
static esp_err_t read_mock_data_from_namespace(const char* namespace_name)
{
ESP_LOGI(TAG, "Opening Non-Volatile Storage (NVS) handle for namespace '%s'...", namespace_name);
nvs_handle_t my_handle;
esp_err_t ret = nvs_open(namespace_name, NVS_READONLY, &my_handle);
ESP_RETURN_ON_ERROR(ret, TAG, "Error (%s) opening NVS handle for namespace '%s'!", esp_err_to_name(ret), namespace_name);
ESP_LOGI(TAG, "Reading stored data from NVS namespace '%s'...", namespace_name);
for (int i = 0; i < mock_data_count; i++) {
size_t required_size = 0;
// Obtain required size of the string value including null character
ret = nvs_get_str(my_handle, mock_data_keys[i], NULL, &required_size);
if (ret != ESP_OK && ret != ESP_ERR_NVS_NOT_FOUND) {
ESP_LOGE(TAG, "Error (%s) getting required size for key '%s'!", esp_err_to_name(ret), mock_data_keys[i]);
continue;
}
if (ret == ESP_ERR_NVS_NOT_FOUND) {
ESP_LOGW(TAG, "Key '%s' not found in namespace '%s'!", mock_data_keys[i], namespace_name);
continue;
}
// Allocate memory for the string value
char* value = malloc(required_size);
if (value == NULL) {
ESP_LOGE(TAG, "Failed to allocate memory for key '%s'!", mock_data_keys[i]);
continue;
}
// Read the string value
ret = nvs_get_str(my_handle, mock_data_keys[i], value, &required_size);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) reading key '%s'!", esp_err_to_name(ret), mock_data_keys[i]);
free(value);
continue;
}
ESP_LOGI(TAG, "Read key-value pair from NVS: '%s':'%s'", mock_data_keys[i], value);
free(value);
}
// Close the storage handle, freeing allocated resources
nvs_close(my_handle);
ESP_LOGI(TAG, "NVS handle for namespace '%s' closed.", namespace_name);
return ESP_OK;
}
void app_main(void)
{
// Erase the contents of the default NVS partition for clean run of this example
ESP_LOGI(TAG, "Erasing the contents of the default NVS partition...");
ESP_ERROR_CHECK(nvs_flash_erase());
// Initialize NVS on default "nvs" partition
esp_err_t ret = nvs_flash_init();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) initializing NVS on default 'nvs' partition!", esp_err_to_name(ret));
return;
}
// Write and read mock data
ret = save_mock_data_to_namespace(MOCK_DATA_NAMESPACE);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) saving mock data data to namespace '%s'!", esp_err_to_name(ret), MOCK_DATA_NAMESPACE);
}
ret = read_mock_data_from_namespace(MOCK_DATA_NAMESPACE);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) reading back stored data from namespace '%s'!", esp_err_to_name(ret), MOCK_DATA_NAMESPACE);
}
// Write and read mock data "backup"
ret = save_mock_data_to_namespace(MOCK_DATA_BACKUP_NAMESPACE);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) saving mock data to namespace '%s'!", esp_err_to_name(ret), MOCK_DATA_BACKUP_NAMESPACE);
}
ret = read_mock_data_from_namespace(MOCK_DATA_BACKUP_NAMESPACE);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) reading back stored data from namespace '%s'!", esp_err_to_name(ret), MOCK_DATA_BACKUP_NAMESPACE);
}
ESP_LOGI(TAG, "Returning from app_main().");
}
@@ -0,0 +1,17 @@
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
import pytest
from pytest_embedded import Dut
from pytest_embedded_idf.utils import idf_parametrize
@pytest.mark.generic
@idf_parametrize('target', ['esp32', 'esp32c3'], indirect=['target'])
def test_examples_nvs_statistics(dut: Dut) -> None:
dut.expect('Getting post-commit NVS statistics...', timeout=5)
dut.expect('Newly used entries match expectation.', timeout=5)
dut.expect('Getting post-commit NVS statistics...', timeout=5)
dut.expect('Newly used entries match expectation.', timeout=5)
dut.expect('Returning from app_main().', timeout=5)