mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
feat(examples/storage): Add NVS iteration example
Add a new example showcasing iterating over NVS entries of a specific (or any) type, and the info to be obtained from therein.
This commit is contained in:
@@ -19,7 +19,7 @@ The examples are grouped into sub-directories by category. Each category directo
|
||||
* `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` TODO
|
||||
* `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,14 @@ 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
|
||||
@@ -39,16 +47,16 @@ 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
|
||||
disable_test:
|
||||
- if: IDF_TARGET != "esp32"
|
||||
reason: only one target 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
|
||||
@@ -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(void)
|
||||
{
|
||||
|
||||
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(void)
|
||||
{
|
||||
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)
|
||||
Reference in New Issue
Block a user