Merge branch 'update/nvs_example' into 'master'

refactor(nvs_examples): refactor nvs storage examples and add nvs_console example

See merge request espressif/esp-idf!37978
This commit is contained in:
Martin Vychodil
2025-04-07 16:27:06 +08:00
71 changed files with 1020 additions and 493 deletions

View File

@ -186,6 +186,6 @@ The current bootloader implementation allows a project to extend it or modify it
In the bootloader space, you cannot use the drivers and functions from other components unless they explicitly support run in bootloader. If necessary, then the required functionality should be placed in the project's `bootloader_components` directory (note that this will increase its size). Examples of components that can be used in the bootloader are:
* :example:`storage/nvs_bootloader`
* :example:`storage/nvs/nvs_bootloader`
If the bootloader grows too large then it can collide with the partition table, which is flashed at offset 0x8000 by default. Increase the :ref:`partition table offset <CONFIG_PARTITION_TABLE_OFFSET>` value to place the partition table later in the flash. This increases the space available for the bootloader.

View File

@ -181,8 +181,8 @@ Points to keep in mind when developing NVS related code:
**Examples:**
- :example:`storage/nvs_rw_value` demonstrates how to use NVS to write and read a single integer value.
- :example:`storage/nvs_rw_blob` demonstrates how to use NVS to write and read a blob.
- :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:`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.

View File

@ -47,16 +47,18 @@ Examples
* - **Code Example**
- **Description**
* - :example:`nvs_rw_blob <storage/nvs_rw_blob>`
* - :example:`nvs_rw_blob <storage/nvs/nvs_rw_blob>`
- Shows the use of the C-style API to read and write blob data types in NVS flash.
* - :example:`nvs_rw_value <storage/nvs_rw_value>`
* - :example:`nvs_rw_value <storage/nvs/nvs_rw_value>`
- 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_rw_value_cxx>`
* - :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_bootloader <storage/nvs_bootloader>`
* - :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/nvsgen>`
* - :example:`nvsgen <storage/nvs/nvsgen>`
- Demonstrates how to use the Python-based NVS image generation tool to create an NVS partition image from the contents of a CSV file.
* - :example:`nvs_console <storage/nvs/nvs_console>`
- Demonstrates how to use NVS through an interactive console interface.
.. list-table:: Common Filesystem API
:widths: 25 75

View File

@ -37,7 +37,7 @@ Applications are expected to follow the steps below in order to enable decryptio
Application Example
-------------------
You can find code examples in :example:`storage/nvs_bootloader` (in the :example:`storage` directory of ESP-IDF examples).
You can find code examples in :example:`storage/nvs/nvs_bootloader` (in the :example:`storage/nvs` directory of ESP-IDF examples).
This section demonstrates how to prepare data in the input-output structure for various data types, namespaces, and keys. It includes an example of reading string data from NVS.

View File

@ -154,9 +154,9 @@ If ``FLASH_IN_PROJECT`` is not specified, the image will still be generated, but
Application Example
-------------------
You can find code examples in the :example:`storage` directory of ESP-IDF examples:
You can find code examples in the :example:`storage/nvs` directory of ESP-IDF examples:
:example:`storage/nvs_rw_value`
:example:`storage/nvs/nvs_rw_value`
Demonstrates how to read a single integer value from, and write it to NVS.
@ -164,7 +164,7 @@ You can find code examples in the :example:`storage` directory of ESP-IDF exampl
The example also shows how to check if a read/write operation was successful, or if a certain value has not been initialized in NVS. The diagnostic procedure is provided in plain text to help you track the program flow and capture any issues on the way.
:example:`storage/nvs_rw_blob`
:example:`storage/nvs/nvs_rw_blob`
Demonstrates how to read a single integer value and a blob (binary large object), and write them to NVS to preserve this value between {IDF_TARGET_NAME} module restarts.
@ -173,9 +173,9 @@ You can find code examples in the :example:`storage` directory of ESP-IDF exampl
The example also shows how to implement the diagnostic procedure to check if the read/write operation was successful.
:example:`storage/nvs_rw_value_cxx`
:example:`storage/nvs/nvs_rw_value_cxx`
This example does exactly the same as :example:`storage/nvs_rw_value`, except that it uses the C++ NVS handle class.
This example does exactly the same as :example:`storage/nvs/nvs_rw_value`, except that it uses the C++ NVS handle class.
Internals
---------

View File

@ -186,6 +186,6 @@ ESP-IDF 二级引导加载程序位于 flash 的 {IDF_TARGET_CONFIG_BOOTLOADER_O
在引导加载程序的代码中,不能使用其他组件提供的驱动和函数,除非某个驱动或函数明确声明支持在引导加载程序中运行。如果确实需要,请将所需功能放在项目的 `bootloader_components` 目录中(注意,这会增加引导加载程序的大小)。以下是可以在引导加载程序中使用的组件示例:
* :example:`storage/nvs_bootloader`
* :example:`storage/nvs/nvs_bootloader`
如果引导加载程序过大,则可能与内存中的分区表重叠,分区表默认烧录在偏移量 0x8000 处。增加 :ref:`分区表偏移量 <CONFIG_PARTITION_TABLE_OFFSET>` ,将分区表放在 flash 中靠后的区域,这样可以增加引导加载程序的可用空间。

View File

@ -181,8 +181,8 @@ NVS 具有如下特性:
**示例:**
- :example:`storage/nvs_rw_value` 演示了如何写入和读取一个整数值。
- :example:`storage/nvs_rw_blob` 演示如何写入和读取一个 blob。
- :example:`storage/nvs/nvs_rw_value` 演示了如何写入和读取一个整数值。
- :example:`storage/nvs/nvs_rw_blob` 演示如何写入和读取一个 blob。
- :example:`security/nvs_encryption_hmac` 演示了如何用 HMAC 外设进行 NVS 加密,并通过 efuse 中的 HMAC 密钥生成加密密钥。
- :example:`security/flash_encryption` 演示了如何进行 flash 加密,包括创建和使用 NVS 分区。

View File

@ -47,16 +47,18 @@
* - **例程**
- **描述**
* - :example:`nvs_rw_blob <storage/nvs_rw_blob>`
* - :example:`nvs_rw_blob <storage/nvs/nvs_rw_blob>`
- 演示了如何在 NVS flash 中使用 C 语言 API 读写 blob 数据类型。
* - :example:`nvs_rw_value <storage/nvs_rw_value>`
* - :example:`nvs_rw_value <storage/nvs/nvs_rw_value>`
- 演示了如何在 NVS flash 中使用 C 语言 API 读写整数数据类型。
* - :example:`nvs_rw_value <storage/nvs_rw_value>`
* - :example:`nvs_rw_value <storage/nvs/nvs_rw_value_cxx>`
- 演示了如何在 NVS flash 中使用 C++ 语言 API 读写整数数据类型。
* - :example:`nvs_bootloader <storage/nvs_bootloader>`
* - :example:`nvs_bootloader <storage/nvs/nvs_bootloader>`
- 演示了如何使用引导加载程序代码中可用的 API 来读取 NVS 数据。
* - :example:`nvsgen <storage/nvsgen>`
* - :example:`nvsgen <storage/nvs/nvsgen>`
- 演示了如何使用基于 Python 的 NVS 镜像生成工具,根据 CSV 文件内容创建 NVS 分区镜像。
* - :example:`nvs_console <storage/nvs/nvs_console>`
- 演示了如何通过交互式控制台界面使用 NVS。
.. list-table:: 常用文件系统 API
:widths: 25 75

View File

@ -37,7 +37,7 @@
应用示例
-----------
代码示例请参阅 ESP-IDF 示例 :example:`storage` 目录下的 :example:`storage/nvs_bootloader`
代码示例请参阅 ESP-IDF 示例 :example:`storage/nvs` 目录下的 :example:`storage/nvs/nvs_bootloader`
本节演示了如何在输入/输出结构中准备数据,以支持不同的数据类型、命名空间和键。此外,还包含从 NVS 读取字符串数据的示例。

View File

@ -154,9 +154,9 @@ NVS 分区生成程序帮助生成 NVS 分区二进制文件,可使用烧录
应用示例
-------------------
ESP-IDF :example:`storage` 目录下提供了数个代码示例:
ESP-IDF :example:`storage/nvs` 目录下提供了数个代码示例:
:example:`storage/nvs_rw_value`
:example:`storage/nvs/nvs_rw_value`
演示如何读取及写入 NVS 单个整数值。
@ -164,7 +164,7 @@ ESP-IDF :example:`storage` 目录下提供了数个代码示例:
该示例也演示了如何检测读取/写入操作是否成功,以及某个特定值是否在 NVS 中尚未初始化。诊断程序以纯文本形式提供,有助于追踪程序流程,及时发现问题。
:example:`storage/nvs_rw_blob`
:example:`storage/nvs/nvs_rw_blob`
演示如何读取及写入 NVS 单个整数值和 BLOB二进制大对象并在 NVS 中存储这一数值,即便 {IDF_TARGET_NAME} 模组重启也不会消失。
@ -173,9 +173,9 @@ ESP-IDF :example:`storage` 目录下提供了数个代码示例:
该示例也演示了如何执行诊断程序以检测读取/写入操作是否成功。
:example:`storage/nvs_rw_value_cxx`
:example:`storage/nvs/nvs_rw_value_cxx`
这个例子与 :example:`storage/nvs_rw_value` 完全一样,只是使用了 C++ 的 NVS 句柄类。
这个例子与 :example:`storage/nvs/nvs_rw_value` 完全一样,只是使用了 C++ 的 NVS 句柄类。
内部实现
---------

View File

@ -16,44 +16,6 @@ examples/storage/emmc:
- if: IDF_TARGET == "esp32s3"
reason: only support on esp32s3
examples/storage/nvs_bootloader:
depends_components:
- nvs_flash
- nvs_sec_provider
disable:
- if: CONFIG_NAME == "nvs_enc_flash_enc" and (SOC_AES_SUPPORTED != 1 and ESP_ROM_HAS_MBEDTLS_CRYPTO_LIB != 1)
- if: CONFIG_NAME == "nvs_enc_hmac" and (SOC_HMAC_SUPPORTED != 1 or (SOC_HMAC_SUPPORTED == 1 and (SOC_AES_SUPPORTED != 1 and ESP_ROM_HAS_MBEDTLS_CRYPTO_LIB != 1)))
reason: As of now in such cases, we do not have any way to perform AES operations in the bootloader build
examples/storage/nvs_rw_blob:
depends_components:
- nvs_flash
- driver
disable_test:
- if: IDF_TARGET not in ["esp32", "esp32c3"]
reason: only one target per arch needed
examples/storage/nvs_rw_value:
depends_components:
- nvs_flash
disable_test:
- if: IDF_TARGET not in ["esp32", "esp32c3"]
reason: only one target per arch needed
examples/storage/nvs_rw_value_cxx:
depends_components:
- nvs_flash
disable_test:
- if: IDF_TARGET not in ["esp32", "esp32c3"]
reason: only one target per arch needed
examples/storage/nvsgen:
depends_components:
- nvs_flash
disable_test:
- if: IDF_TARGET != "esp32"
reason: only one target needed
examples/storage/partition_api/partition_find:
depends_components:
- esp_partition

View File

@ -17,6 +17,7 @@ The examples are grouped into sub-directories by category. Each category directo
* `nvs_rw_blob` example demonstrates how to read and write a single integer value and a blob (binary large object) using NVS to preserve them between ESP module restarts.
* `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.
* `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.

View File

@ -0,0 +1,47 @@
# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps
examples/storage/nvs/nvs_bootloader:
depends_components:
- nvs_flash
- nvs_sec_provider
disable:
- if: CONFIG_NAME == "nvs_enc_flash_enc" and (SOC_AES_SUPPORTED != 1 and ESP_ROM_HAS_MBEDTLS_CRYPTO_LIB != 1)
- if: CONFIG_NAME == "nvs_enc_hmac" and (SOC_HMAC_SUPPORTED != 1 or (SOC_HMAC_SUPPORTED == 1 and (SOC_AES_SUPPORTED != 1 and ESP_ROM_HAS_MBEDTLS_CRYPTO_LIB != 1)))
reason: As of now in such cases, we do not have any way to perform AES operations in the bootloader build
examples/storage/nvs/nvs_console:
depends_components:
- nvs_flash
- console
- vfs
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
- driver
disable_test:
- if: IDF_TARGET not in ["esp32", "esp32c3"]
reason: only one target per arch needed
examples/storage/nvs/nvs_rw_value:
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_value_cxx:
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

View File

@ -137,7 +137,7 @@ Enable NVS encryption using your preferred scheme. Please find more details rega
(Note: In case you select the `HMAC based NVS encryption scheme`, make sure that you burn the below mentioned [HMAC key](./main/nvs_enc_hmac_key.bin) in the efuses.)
For generating the encrypted NVS partitions, we shall use [NVS partition generator](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/storage/nvs_partition_gen.html#nvs-partition-generator-utility).
We shall use the [nvs_partition_gen.py](../../../components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py) script for the operations.
We shall use the [nvs_partition_gen.py](../../../../components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py) script for the operations.
Along with the above mentioned file structure, the project folder also contains pre-generated encrypted partitions and the partition corresponding to the selected NVS encryption scheme is flashed along with the build artefacts using the `main/CMakeLists.txt`.
@ -146,13 +146,13 @@ In case the data in `nvs_data.csv` is modified, these encrypted NVS partitions c
1. NVS Encryption using the flash encryption scheme
```
python nvs_partition_gen.py encrypt $IDF_PATH/examples/storage/nvs_bootloader/nvs_data.csv $IDF_PATH/examples/storage/nvs_bootloader/main/nvs_encrypted.bin 0x6000 --inputkey $IDF_PATH/examples/storage/nvs_bootloader/main/encryption_keys.bin
python nvs_partition_gen.py encrypt $IDF_PATH/examples/storage/nvs/nvs_bootloader/nvs_data.csv $IDF_PATH/examples/storage/nvs/nvs_bootloader/main/nvs_encrypted.bin 0x6000 --inputkey $IDF_PATH/examples/storage/nvs/nvs_bootloader/main/encryption_keys.bin
```
2. NVS Encryption using the HMAC scheme
```
python nvs_partition_gen.py encrypt $IDF_PATH/examples/storage/nvs_bootloader/nvs_data.csv $IDF_PATH/examples/storage/nvs_bootloader/main/nvs_encrypted_hmac.bin 0x6000 --keygen --key_protect_hmac --kp_hmac_inputkey $IDF_PATH/examples/storage/nvs_bootloader/main/nvs_enc_hmac_key.bin
python nvs_partition_gen.py encrypt $IDF_PATH/examples/storage/nvs/nvs_bootloader/nvs_data.csv $IDF_PATH/examples/storage/nvs/nvs_bootloader/main/nvs_encrypted_hmac.bin 0x6000 --keygen --key_protect_hmac --kp_hmac_inputkey $IDF_PATH/examples/storage/nvs/nvs_bootloader/main/nvs_enc_hmac_key.bin
```
Build the application using configurations corresponding to the NVS encryption scheme that you have selected:

View File

Can't render this file because it has a wrong number of fields in line 4.

View File

@ -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.16)
list(APPEND EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/examples/system/console/advanced/components")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(nvs_console_example)

View File

@ -0,0 +1,104 @@
| 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 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- |
## Overview
This example demonstrates how to use Non-Volatile Storage (NVS) through an interactive console interface. It provides a set of commands to read, write, and manage data in NVS.
## Hardware Required
This example can run on any ESP32 family development board.
## Configuration
The example can be configured through `menuconfig`:
1. Enable/disable command history storage (`CONFIG_CONSOLE_STORE_HISTORY`)
2. Configure UART parameters
3. Configure console prompt color settings
## How to Use
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```bash
idf.py -p PORT flash monitor
```
(Replace PORT with the name of the serial port to use.)
### Console Commands
The following commands are available:
1. NVS Operations:
- `nvs_namespace <namespace>` - Set current namespace
- Example: `nvs_namespace storage`
- `nvs_set <key> <type> -v <value>` - Set a value in NVS
- type can be: i8, u8, i16, u16 i32, u32 i64, u64, str, blob
- Example: `nvs_set counter i32 -v 42`
- Example: `nvs_set name str -v "esp"`
- Example: `nvs_set blob_val blob -v "657370"` - To set a blob value, provide the hex representation of the data you want to store. For example, 65 (e), 73 (s), 70 (p).
- `nvs_get <key>` - Get a value from NVS
- Example: `nvs_get counter`
- `nvs_erase <key>` - Erase a key from NVS
- Example: `nvs_erase counter`
- `nvs_list <partition> [-n <namespace>] [-t <type>]` - List stored key-value pairs stored in NVS. Use default partition name 'nvs' for listing the stored data.
- Example: `nvs_list nvs` - This command lists all namespaces and their stored key-value pairs in the 'nvs' partition.
- Example: `nvs_list nvs -n storage -t i8`
- `nvs_erase_namespace <namespace>` - Erases specified namespace
- Example: `nvs_erase_namespace storage`
2. System Commands:
- `help` - List all commands
- `free` - Get the current size of free heap memory
- `restart` - Software reset of the chip
- `version` - Get the chip info together with ESP-IDF version used in the application
- `heap` - Get minimum size of free heap memory that was available during program execution
### Example Output
``` bash
...
NVS Console Example
-------------------
Type 'help' to get the list of commands.
Use UP/DOWN arrows to navigate through command history.
Press TAB when typing command name to auto-complete.
Press Ctrl+C to exit the console.
nvs> version
IDF Version:v5.5-dev-2627-g2cbfce97768-dirt
Chip info:
model:ESP32
cores:2
feature:/802.11bgn/BLE/BT/External-Flash:2 MB
revision number:0
nvs> free
298172
nvs> heap
min heap size: 298156
nvs> nvs_list nvs
namespace 'storage_1', key 'u8_key', type 'u8'
namespace 'storage_1', key 'i8_key', type 'i8'
namespace 'storage_1', key 'u16_key', type 'u16'
namespace 'storage_2', key 'u32_key', type 'u32'
namespace 'storage_2', key 'i32_key', type 'i32'
namespace 'storage_2', key 'str_key', type 'str'
nvs> nvs_namespace storage_1
I (85497) cmd_nvs: Namespace set to 'storage_1'
nvs> nvs_get i8_key i8
-128
nvs> nvs_set i8_key i8 -v -50
I (233297) cmd_nvs: Value stored under key 'i8_key'
nvs> nvs_get i8_key i8
-50
nvs>
...
```

View File

@ -0,0 +1,10 @@
idf_component_register(SRCS "nvs_console_main.c"
"console_settings.c"
REQUIRES cmd_nvs cmd_system esp_driver_uart fatfs
INCLUDE_DIRS ".")
# Create a NVS image from the contents of the `nvs_data` CSV file
# that fits the partition named 'nvs'. FLASH_IN_PROJECT indicates that
# the generated image should be flashed when the entire project is flashed to
# the target with 'idf.py -p PORT flash'.
nvs_create_partition_image(nvs ../nvs_data.csv FLASH_IN_PROJECT)

View File

@ -0,0 +1,125 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include "esp_system.h"
#include "esp_log.h"
#include "esp_console.h"
#include "esp_vfs_dev.h"
#include "driver/uart.h"
#include "driver/uart_vfs.h"
#include "linenoise/linenoise.h"
#include "argtable3/argtable3.h"
#include "console_settings.h"
#define CONSOLE_MAX_CMDLINE_ARGS 8
#define CONSOLE_MAX_CMDLINE_LENGTH 256
#define CONSOLE_PROMPT_MAX_LEN (32)
static const char *TAG = "console_settings";
static char prompt[CONSOLE_PROMPT_MAX_LEN];
void initialize_console_peripheral(void)
{
/* Drain stdout before reconfiguring it */
fflush(stdout);
fsync(fileno(stdout));
/* Disable buffering on stdin */
setvbuf(stdin, NULL, _IONBF, 0);
/* Minicom, screen, idf_monitor send CR when ENTER key is pressed */
uart_vfs_dev_port_set_rx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CR);
/* Move the caret to the beginning of the next line on '\n' */
uart_vfs_dev_port_set_tx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CRLF);
/* Configure UART */
const uart_config_t uart_config = {
.baud_rate = CONFIG_ESP_CONSOLE_UART_BAUDRATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2
.source_clk = UART_SCLK_REF_TICK,
#else
.source_clk = UART_SCLK_XTAL,
#endif
};
/* Install UART driver for interrupt-driven reads and writes */
ESP_ERROR_CHECK(uart_driver_install(CONFIG_ESP_CONSOLE_UART_NUM,
256, 0, 0, NULL, 0));
ESP_ERROR_CHECK(uart_param_config(CONFIG_ESP_CONSOLE_UART_NUM, &uart_config));
/* Tell VFS to use UART driver */
uart_vfs_dev_use_driver(CONFIG_ESP_CONSOLE_UART_NUM);
}
void initialize_console_library(const char *history_path)
{
/* Initialize the console */
esp_console_config_t console_config = {
.max_cmdline_args = CONSOLE_MAX_CMDLINE_ARGS,
.max_cmdline_length = CONSOLE_MAX_CMDLINE_LENGTH,
#if CONFIG_LOG_COLORS
.hint_color = atoi(LOG_COLOR_CYAN)
#endif
};
ESP_ERROR_CHECK(esp_console_init(&console_config));
/* Configure linenoise line completion library */
linenoiseSetMultiLine(1);
/* Tell linenoise where to get command completions and hints */
linenoiseSetCompletionCallback(&esp_console_get_completion);
linenoiseSetHintsCallback((linenoiseHintsCallback*) &esp_console_get_hint);
/* Set command history size */
linenoiseHistorySetMaxLen(100);
/* Set command maximum length */
linenoiseSetMaxLineLen(console_config.max_cmdline_length);
/* Don't return empty lines */
linenoiseAllowEmpty(false);
#if CONFIG_CONSOLE_STORE_HISTORY
/* Load command history from filesystem */
if (history_path) {
linenoiseHistoryLoad(history_path);
}
#endif
/* Figure out if the terminal supports escape sequences */
int probe_status = linenoiseProbe();
if (probe_status) { /* zero indicates success */
ESP_LOGW(TAG, "Your terminal application does not support escape sequences.\n"
"Line editing and history features are disabled.\n"
"On Windows, try using Windows Terminal or Putty instead.");
linenoiseSetDumbMode(1);
}
}
char *setup_prompt(const char *prompt_str)
{
const char *prompt_temp = "esp>";
if (prompt_str) {
prompt_temp = prompt_str;
}
/* Set command line prompt */
if (linenoiseIsDumbMode()) {
/* Since the terminal doesn't support escape sequences,
* don't use color codes in the prompt */
snprintf(prompt, CONSOLE_PROMPT_MAX_LEN - 1, "%s ", prompt_temp);
} else {
snprintf(prompt, CONSOLE_PROMPT_MAX_LEN - 1, LOG_COLOR_I "%s " LOG_RESET_COLOR, prompt_temp);
}
return prompt;
}

View File

@ -0,0 +1,48 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Initialize console peripheral type
*
* Console peripheral is based on sdkconfig settings
*
* UART CONFIG_ESP_CONSOLE_UART_DEFAULT
* USB_OTG CONFIG_ESP_CONSOLE_USB_CDC
* USB_SERIAL_JTAG CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
*/
void initialize_console_peripheral(void);
/**
* @brief Initialize linenoise and esp console
*
* This function initialize linenoise library and esp_console component,
* also checks if the terminal supports escape sequences
*
* @param history_path Path to store command history
*/
void initialize_console_library(const char *history_path);
/**
* @brief Initialize console prompt
*
* This function adds color code to the prompt (if the console supports escape sequences)
*
* @param prompt_str Prompt in form of string eg esp32s3>
*
* @return
* - pointer to initialized prompt
*/
char *setup_prompt(const char *prompt_str);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,133 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include <string.h>
#include "esp_system.h"
#include "esp_log.h"
#include "esp_console.h"
#include "esp_vfs_fat.h"
#include "linenoise/linenoise.h"
#include "argtable3/argtable3.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "cmd_nvs.h"
#include "cmd_system.h"
#include "console_settings.h"
static const char* TAG = "nvs_console";
#define PROMPT_STR "nvs"
/* Console command history can be stored to and loaded from a file.
* The easiest way to do this is to use FATFS filesystem on top of
* wear_levelling library.
*/
#if CONFIG_CONSOLE_STORE_HISTORY
#define MOUNT_PATH "/data"
#define HISTORY_PATH MOUNT_PATH "/history.txt"
static void initialize_filesystem(void)
{
static wl_handle_t wl_handle;
const esp_vfs_fat_mount_config_t mount_config = {
.max_files = 4,
.format_if_mount_failed = true
};
esp_err_t err = esp_vfs_fat_spiflash_mount_rw_wl(MOUNT_PATH, "storage", &mount_config, &wl_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to mount FATFS (%s)", esp_err_to_name(err));
return;
}
}
#else
#define HISTORY_PATH NULL
#endif // CONFIG_CONSOLE_STORE_HISTORY
static void initialize_nvs(void)
{
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
err = nvs_flash_init();
}
ESP_ERROR_CHECK(err);
}
void app_main(void)
{
initialize_nvs();
#if CONFIG_CONSOLE_STORE_HISTORY
initialize_filesystem();
ESP_LOGI(TAG, "Command history enabled");
#else
ESP_LOGI(TAG, "Command history disabled");
#endif
/* Initialize console peripheral and library */
initialize_console_peripheral();
initialize_console_library(HISTORY_PATH);
/* Register commands */
esp_console_register_help_command();
register_system_common();
register_nvs();
/* Set up prompt */
const char* prompt = setup_prompt(PROMPT_STR ">");
/* Clear screen */
printf("\033[2J\033[1;1H");
printf("\n"
"NVS Console Example\n"
"-------------------\n"
"Type 'help' to get the list of commands.\n"
"Use UP/DOWN arrows to navigate through command history.\n"
"Press TAB when typing command name to auto-complete.\n"
"Press Ctrl+C to exit the console.\n\n");
if (linenoiseIsDumbMode()) {
printf("\n"
"Your terminal application does not support escape sequences.\n"
"Line editing and history features are disabled.\n"
"On Windows, try using Windows Terminal or Putty instead.\n\n");
}
/* Main loop */
while(true) {
char* line = linenoise(prompt);
if (line == NULL) {
continue;
}
/* Add the command to the history if not empty */
if (strlen(line) > 0) {
linenoiseHistoryAdd(line);
#if CONFIG_CONSOLE_STORE_HISTORY
linenoiseHistorySave(HISTORY_PATH);
#endif
}
/* Try to run the command */
int ret;
esp_err_t err = esp_console_run(line, &ret);
if (err == ESP_ERR_NOT_FOUND) {
printf("Unrecognized command\n");
} else if (err == ESP_ERR_INVALID_ARG) {
// command was empty
} else if (err == ESP_OK && ret != ESP_OK) {
printf("Command returned non-zero error code: 0x%x (%s)\n", ret, esp_err_to_name(ret));
} else if (err != ESP_OK) {
printf("Internal error: %s\n", esp_err_to_name(err));
}
linenoiseFree(line);
}
ESP_LOGE(TAG, "Error or end-of-input, terminating console");
esp_console_deinit();
}

View File

@ -0,0 +1,15 @@
# Sample csv file
key,type,encoding,value
storage_1,namespace,,
u8_key,data,u8,255
i8_key,data,i8,-128
u16_key,data,u16,65535
storage_2,namespace,,
u32_key,data,u32,4294967295
i32_key,data,i32,-2147483648
str_key,data,string,"Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Fusce quis risus justo.
Suspendisse egestas in nisi sit amet auctor.
Pellentesque rhoncus dictum sodales.
In justo erat, viverra at interdum eget, interdum vel dui."
Can't render this file because it has a wrong number of fields in line 2.

View File

@ -0,0 +1,22 @@
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
from time import sleep
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_nvs_console(dut: Dut) -> None:
# Wait until the console prompt appears
dut.expect('nvs> ')
# Write CLI command "version"
dut.write('version')
sleep(0.5)
# Check if following strings are present in the "version" command output
dut.expect('IDF Version')
dut.expect('Chip info')

View File

@ -14,7 +14,7 @@ Example also shows how to implement diagnostics if read / write operation was su
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).
If not done already, consider checking simpler example *storage/nvs_rw_value*, that has been used as a starting point for preparing this one.
If not done already, consider checking simpler example *storage/nvs/nvs_rw_value*, that has been used as a starting point for preparing this one.
## How to use example
@ -36,34 +36,27 @@ See the Getting Started Guide for full steps to configure and use ESP-IDF to bui
## Example Output
First run:
```
Restart counter = 0
Run time:
Nothing saved yet!
...
I (288) main_task: Calling app_main()
I (298) nvs_blob_example: Saving test data blob...
I (308) nvs_blob_example:
Reading updated blob data:
I (308) nvs_blob_example: Reading test data blob:
I (308) nvs_blob_example: ID: 123
I (308) nvs_blob_example: Name: Test Sample
I (308) nvs_blob_example: Values: 3.140, 2.718, -0.000, 0.000
I (318) nvs_blob_example: Flags: 0xABCD1234
I (318) nvs_blob_example: Counts: -100, 100
I (328) nvs_blob_example: Active: true
I (328) nvs_blob_example:
Reading array data blob:
I (338) nvs_blob_example: Array[0] = 30
I (338) nvs_blob_example: Array[1] = 20
I (338) nvs_blob_example:
Blob operations completed. Monitoring GPIO for reset...
I (348) gpio: GPIO[0]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
...
```
At this point, press "Boot" button and hold it for a second. The board will perform software restart, printing:
```
Restarting...
```
After booting again, restart counter and run time array will be printed:
```
Restart counter = 1
Run time:
1: 5110
```
After pressing "Boot" once more:
```
Restart counter = 2
Run time:
1: 5110
2: 5860
```
To reset the counter and run time array, erase the contents of flash memory using `idf.py erase-flash`, then upload the program again as described above.
To reset nvs data, erase the contents of flash memory using `idf.py erase-flash`, then upload the program again as described above.

View File

@ -0,0 +1,214 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* Non-Volatile Storage (NVS) Read and Write a Blob - 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_system.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "nvs.h"
#include "driver/gpio.h"
#define STORAGE_NAMESPACE "storage"
static const char *TAG = "nvs_blob_example";
/* Test data structure to demonstrate different data types in blob */
typedef struct {
uint8_t id;
char name[32];
float values[2];
uint32_t flags;
int16_t counts[2];
bool active;
} test_data_t;
/* Save test data as a blob in NVS */
esp_err_t save_test_data(void)
{
nvs_handle_t my_handle;
esp_err_t err;
// Create sample test data
test_data_t test_data = {
.id = 123,
.name = "Test Sample",
.values = {3.14f, 2.718f},
.flags = 0xABCD1234,
.counts = {-100, 100},
.active = true
};
// Open NVS handle
err = nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, &my_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) opening NVS handle!", esp_err_to_name(err));
return err;
}
// Write blob
ESP_LOGI(TAG, "Saving test data blob...");
err = nvs_set_blob(my_handle, "test_data", &test_data, sizeof(test_data_t));
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to write test data blob!");
nvs_close(my_handle);
return err;
}
// Commit
err = nvs_commit(my_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to commit data");
}
nvs_close(my_handle);
return err;
}
/* Example of storing and appending array data as a blob */
esp_err_t save_array_data(void)
{
nvs_handle_t my_handle;
esp_err_t err;
// Open
err = nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, &my_handle);
if (err != ESP_OK) return err;
// First, get the size of existing data (if any)
size_t required_size = 0;
err = nvs_get_blob(my_handle, "array_data", NULL, &required_size);
if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) {
ESP_LOGE(TAG, "Error (%s) reading array size!", esp_err_to_name(err));
nvs_close(my_handle);
return err;
}
// Allocate memory and read existing data
uint32_t* array_data = malloc(required_size + sizeof(uint32_t));
if (required_size > 0) {
err = nvs_get_blob(my_handle, "array_data", array_data, &required_size);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) reading array data!", esp_err_to_name(err));
free(array_data);
nvs_close(my_handle);
return err;
}
}
// Append new value
required_size += sizeof(uint32_t);
array_data[required_size / sizeof(uint32_t) - 1] = xTaskGetTickCount() * portTICK_PERIOD_MS;
// Save updated array
err = nvs_set_blob(my_handle, "array_data", array_data, required_size);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) saving array data!", esp_err_to_name(err));
}
free(array_data);
// Commit
err = nvs_commit(my_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) committing data!", esp_err_to_name(err));
}
nvs_close(my_handle);
return err;
}
/* Read and display all saved blobs */
esp_err_t read_stored_blobs(void)
{
nvs_handle_t my_handle;
esp_err_t err;
err = nvs_open(STORAGE_NAMESPACE, NVS_READONLY, &my_handle);
if (err != ESP_OK) return err;
// 1. Read test data blob
ESP_LOGI(TAG, "Reading test data blob:");
test_data_t test_data;
size_t test_data_size = sizeof(test_data_t);
err = nvs_get_blob(my_handle, "test_data", &test_data, &test_data_size);
if (err == ESP_OK) {
ESP_LOGI(TAG, "ID: %d", test_data.id);
ESP_LOGI(TAG, "Name: %s", test_data.name);
ESP_LOGI(TAG, "Values: %.3f, %.3f, %.3f, %.3f",
test_data.values[0], test_data.values[1],
test_data.values[2], test_data.values[3]);
ESP_LOGI(TAG, "Flags: 0x%08" PRIX32, test_data.flags);
ESP_LOGI(TAG, "Counts: %d, %d", test_data.counts[0], test_data.counts[1]);
ESP_LOGI(TAG, "Active: %s", test_data.active ? "true" : "false");
} else if (err == ESP_ERR_NVS_NOT_FOUND) {
ESP_LOGW(TAG, "Test data not found!");
}
// 2. Read array data blob
ESP_LOGI(TAG, "\nReading array data blob:");
size_t required_size = 0;
err = nvs_get_blob(my_handle, "array_data", NULL, &required_size);
if (err == ESP_OK) {
uint32_t* array_data = malloc(required_size);
err = nvs_get_blob(my_handle, "array_data", array_data, &required_size);
if (err == ESP_OK) {
for (int i = 0; i < required_size / sizeof(uint32_t); i++) {
ESP_LOGI(TAG, "Array[%d] = %" PRIu32, i, array_data[i]);
}
}
free(array_data);
} else if (err == ESP_ERR_NVS_NOT_FOUND) {
ESP_LOGW(TAG, "Array data not found!");
}
nvs_close(my_handle);
return ESP_OK;
}
void app_main(void)
{
// Initialize NVS
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
err = nvs_flash_init();
}
ESP_ERROR_CHECK(err);
// Save new test data
err = save_test_data();
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) saving test data!", esp_err_to_name(err));
}
// Save new array data
err = save_array_data();
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) saving array data!", esp_err_to_name(err));
}
// Read updated data
ESP_LOGI(TAG, "\nReading updated blob data:");
err = read_stored_blobs();
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) reading updated data!", esp_err_to_name(err));
}
ESP_LOGI(TAG, "\nBlob operations completed.");
}

View File

@ -0,0 +1,19 @@
# SPDX-FileCopyrightText: 2022-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_rw_blob(dut: Dut) -> None:
# Save and read test data
dut.expect('Saving test data blob...', timeout=20)
# Save array data
# Read updated data
dut.expect('Reading updated blob data:', timeout=20)
dut.expect('Reading test data blob:', timeout=20)
# Verify array data reading
dut.expect('Reading array data blob:', timeout=20)
dut.expect('Blob operations completed.', timeout=20)

View File

@ -13,7 +13,7 @@ Example also shows how to check if read / write operation was successful, or cer
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).
Check another example *storage/nvs_rw_blob*, which shows how to read and write variable length binary data (blob).
Check another example *storage/nvs/nvs_rw_blob*, which shows how to read and write variable length binary data (blob).
## How to use example
@ -35,50 +35,32 @@ See the Getting Started Guide for full steps to configure and use ESP-IDF to bui
## Example Output
First run:
```
Opening Non-Volatile Storage (NVS) handle... Done
Reading restart counter from NVS ... The value is not initialized yet!
Updating restart counter in NVS ... Done
Committing updates in NVS ... Done
Restarting in 10 seconds...
Restarting in 9 seconds...
Restarting in 8 seconds...
Restarting in 7 seconds...
Restarting in 6 seconds...
Restarting in 5 seconds...
Restarting in 4 seconds...
Restarting in 3 seconds...
Restarting in 2 seconds...
Restarting in 1 seconds...
Restarting in 0 seconds...
Restarting now.
...
I (296) nvs_example:
Opening Non-Volatile Storage (NVS) handle...
I (296) nvs_example:
Writing counter to NVS...
I (306) nvs_example:
Reading counter from NVS...
I (306) nvs_example: Read counter = 42
I (306) nvs_example:
Writing string to NVS...
I (306) nvs_example:
Reading string from NVS...
I (306) nvs_example: Read string: Hello from NVS!
I (316) nvs_example:
Finding keys in NVS...
I (316) nvs_example: Key: 'message', Type: str
I (316) nvs_example: Key: 'counter', Type: i32
I (326) nvs_example:
Deleting key from NVS...
I (336) nvs_example:
Committing updates in NVS...
I (336) nvs_example: NVS handle closed.
I (336) nvs_example: Returned to app_main
I (346) main_task: Returned from app_main()
...
```
Subsequent runs:
```
Opening Non-Volatile Storage (NVS) handle... Done
Reading restart counter from NVS ... Done
Restart counter = 1
Updating restart counter in NVS ... Done
Committing updates in NVS ... Done
Restarting in 10 seconds...
Restarting in 9 seconds...
Restarting in 8 seconds...
Restarting in 7 seconds...
Restarting in 6 seconds...
Restarting in 5 seconds...
Restarting in 4 seconds...
Restarting in 3 seconds...
Restarting in 2 seconds...
Restarting in 1 seconds...
Restarting in 0 seconds...
Restarting now.
```
Restart counter will increment on each run.
To reset the counter, erase the contents of flash memory using `idf.py erase-flash`, then upload the program again as described above.
To reset nvs data, erase the contents of flash memory using `idf.py erase-flash`, then upload the program again as described above.

View File

@ -0,0 +1,161 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* Non-Volatile Storage (NVS) Read and Write a Value - 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_system.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "nvs.h"
static const char *TAG = "nvs_example";
typedef struct {
nvs_type_t type;
const char *str;
} type_str_pair_t;
static const type_str_pair_t type_str_pair[] = {
{ NVS_TYPE_I8, "i8" },
{ NVS_TYPE_U8, "u8" },
{ NVS_TYPE_U16, "u16" },
{ NVS_TYPE_I16, "i16" },
{ NVS_TYPE_U32, "u32" },
{ NVS_TYPE_I32, "i32" },
{ NVS_TYPE_U64, "u64" },
{ NVS_TYPE_I64, "i64" },
{ NVS_TYPE_STR, "str" },
{ NVS_TYPE_BLOB, "blob" },
{ NVS_TYPE_ANY, "any" },
};
static const size_t TYPE_STR_PAIR_SIZE = sizeof(type_str_pair) / sizeof(type_str_pair[0]);
static const char *type_to_str(nvs_type_t type)
{
for (int i = 0; i < TYPE_STR_PAIR_SIZE; i++) {
const type_str_pair_t *p = &type_str_pair[i];
if (p->type == type) {
return p->str;
}
}
return "Unknown";
}
void app_main(void)
{
// Initialize NVS
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
// NVS partition was truncated and needs to be erased
// Retry nvs_flash_init
ESP_ERROR_CHECK(nvs_flash_erase());
err = nvs_flash_init();
}
ESP_ERROR_CHECK(err);
// Open NVS handle
ESP_LOGI(TAG, "\nOpening Non-Volatile Storage (NVS) handle...");
nvs_handle_t my_handle;
err = nvs_open("storage", NVS_READWRITE, &my_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Error (%s) opening NVS handle!", esp_err_to_name(err));
return;
}
// Store and read an integer value
int32_t counter = 42;
ESP_LOGI(TAG, "\nWriting counter to NVS...");
err = nvs_set_i32(my_handle, "counter", counter);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to write counter!");
}
// Read back the value
int32_t read_counter = 0;
ESP_LOGI(TAG, "\nReading counter from NVS...");
err = nvs_get_i32(my_handle, "counter", &read_counter);
switch (err) {
case ESP_OK:
ESP_LOGI(TAG, "Read counter = %" PRIu32, read_counter);
break;
case ESP_ERR_NVS_NOT_FOUND:
ESP_LOGW(TAG, "The value is not initialized yet!");
break;
default:
ESP_LOGE(TAG, "Error (%s) reading!", esp_err_to_name(err));
}
// Store and read a string
ESP_LOGI(TAG, "\nWriting string to NVS...");
err = nvs_set_str(my_handle, "message", "Hello from NVS!");
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to write string!");
}
// Read back the string
size_t required_size = 0;
ESP_LOGI(TAG, "\nReading string from NVS...");
err = nvs_get_str(my_handle, "message", NULL, &required_size);
if (err == ESP_OK) {
char* message = malloc(required_size);
err = nvs_get_str(my_handle, "message", message, &required_size);
if (err == ESP_OK) {
ESP_LOGI(TAG, "Read string: %s", message);
}
free(message);
}
// Find keys in NVS
ESP_LOGI(TAG, "\nFinding keys in NVS...");
nvs_iterator_t it = NULL;
esp_err_t res = nvs_entry_find("nvs", "storage", NVS_TYPE_ANY, &it);
while(res == ESP_OK) {
nvs_entry_info_t info;
nvs_entry_info(it, &info);
const char *type_str = type_to_str(info.type);
ESP_LOGI(TAG, "Key: '%s', Type: %s", info.key, type_str);
res = nvs_entry_next(&it);
}
nvs_release_iterator(it);
// Delete a key from NVS
ESP_LOGI(TAG, "\nDeleting key from NVS...");
err = nvs_erase_key(my_handle, "counter");
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to erase key!");
}
// Commit changes
// After setting any values, nvs_commit() must be called to ensure changes are written
// to flash storage. Implementations may write to storage at other times,
// but this is not guaranteed.
ESP_LOGI(TAG, "\nCommitting updates in NVS...");
err = nvs_commit(my_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to commit NVS changes!");
}
// Close
nvs_close(my_handle);
ESP_LOGI(TAG, "NVS handle closed.");
ESP_LOGI(TAG, "Returned to app_main");
}

View File

@ -0,0 +1,23 @@
# SPDX-FileCopyrightText: 2022-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_rw_value(dut: Dut) -> None:
dut.serial.erase_flash()
dut.serial.flash()
dut.expect('Opening Non-Volatile Storage \\(NVS\\) handle...', timeout=20)
dut.expect('Writing counter to NVS...', timeout=20)
dut.expect('Reading counter from NVS...', timeout=20)
dut.expect('Writing string to NVS...', timeout=20)
dut.expect('Reading string from NVS...', timeout=20)
dut.expect('Finding keys in NVS...', timeout=20)
dut.expect('Deleting key from NVS...', timeout=20)
dut.expect('Committing updates in NVS...', timeout=20)
dut.expect('NVS handle closed.', timeout=20)
dut.expect('Returned to app_main', timeout=20)

View File

@ -1,3 +1,8 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
/* Non-Volatile Storage (NVS) Read and Write a Value - Example
For other examples please check:

View File

@ -5,7 +5,7 @@
(See the README.md file in the upper level 'examples' directory for more information about examples.)
This example demonstrates how to use the NVS image generation tool [nvs_partition_gen.py](../../../components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py) to automatically create a NVS partition image from the contents of a CSV file during build, with an option of automatically flashing the created image on invocation of `idf.py -p PORT flash`. For more information, see description of `nvs_partition_gen.py` on the ESP-IDF Programming Guide under API Reference > Storage API > NVS Partition Generator Utility.
This example demonstrates how to use the NVS image generation tool [nvs_partition_gen.py](../../../../components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py) to automatically create a NVS partition image from the contents of a CSV file during build, with an option of automatically flashing the created image on invocation of `idf.py -p PORT flash`. For more information, see description of `nvs_partition_gen.py` on the ESP-IDF Programming Guide under API Reference > Storage API > NVS Partition Generator Utility.
The following gives an overview of the example:

View File

Can't render this file because it has a wrong number of fields in line 2.

View File

@ -1,196 +0,0 @@
/* Non-Volatile Storage (NVS) Read and Write a Blob - 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_system.h"
#include "nvs_flash.h"
#include "nvs.h"
#include "driver/gpio.h"
#define STORAGE_NAMESPACE "storage"
#if CONFIG_IDF_TARGET_ESP32C3
#define BOOT_MODE_PIN GPIO_NUM_9
#else
#define BOOT_MODE_PIN GPIO_NUM_0
#endif //CONFIG_IDF_TARGET_ESP32C3
/* Save the number of module restarts in NVS
by first reading and then incrementing
the number that has been saved previously.
Return an error if anything goes wrong
during this process.
*/
esp_err_t save_restart_counter(void)
{
nvs_handle_t my_handle;
esp_err_t err;
// Open
err = nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, &my_handle);
if (err != ESP_OK) return err;
// Read
int32_t restart_counter = 0; // value will default to 0, if not set yet in NVS
err = nvs_get_i32(my_handle, "restart_conter", &restart_counter);
if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err;
// Write
restart_counter++;
err = nvs_set_i32(my_handle, "restart_conter", restart_counter);
if (err != ESP_OK) return err;
// Commit written value.
// After setting any values, nvs_commit() must be called to ensure changes are written
// to flash storage. Implementations may write to storage at other times,
// but this is not guaranteed.
err = nvs_commit(my_handle);
if (err != ESP_OK) return err;
// Close
nvs_close(my_handle);
return ESP_OK;
}
/* Save new run time value in NVS
by first reading a table of previously saved values
and then adding the new value at the end of the table.
Return an error if anything goes wrong
during this process.
*/
esp_err_t save_run_time(void)
{
nvs_handle_t my_handle;
esp_err_t err;
// Open
err = nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, &my_handle);
if (err != ESP_OK) return err;
// Read the size of memory space required for blob
size_t required_size = 0; // value will default to 0, if not set yet in NVS
err = nvs_get_blob(my_handle, "run_time", NULL, &required_size);
if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err;
// Read previously saved blob if available
uint32_t* run_time = malloc(required_size + sizeof(uint32_t));
if (required_size > 0) {
err = nvs_get_blob(my_handle, "run_time", run_time, &required_size);
if (err != ESP_OK) {
free(run_time);
return err;
}
}
// Write value including previously saved blob if available
required_size += sizeof(uint32_t);
run_time[required_size / sizeof(uint32_t) - 1] = xTaskGetTickCount() * portTICK_PERIOD_MS;
err = nvs_set_blob(my_handle, "run_time", run_time, required_size);
free(run_time);
if (err != ESP_OK) return err;
// Commit
err = nvs_commit(my_handle);
if (err != ESP_OK) return err;
// Close
nvs_close(my_handle);
return ESP_OK;
}
/* Read from NVS and print restart counter
and the table with run times.
Return an error if anything goes wrong
during this process.
*/
esp_err_t print_what_saved(void)
{
nvs_handle_t my_handle;
esp_err_t err;
// Open
err = nvs_open(STORAGE_NAMESPACE, NVS_READWRITE, &my_handle);
if (err != ESP_OK) return err;
// Read restart counter
int32_t restart_counter = 0; // value will default to 0, if not set yet in NVS
err = nvs_get_i32(my_handle, "restart_conter", &restart_counter);
if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err;
printf("Restart counter = %" PRIu32 "\n", restart_counter);
// Read run time blob
size_t required_size = 0; // value will default to 0, if not set yet in NVS
// obtain required memory space to store blob being read from NVS
err = nvs_get_blob(my_handle, "run_time", NULL, &required_size);
if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err;
printf("Run time:\n");
if (required_size == 0) {
printf("Nothing saved yet!\n");
} else {
uint32_t* run_time = malloc(required_size);
err = nvs_get_blob(my_handle, "run_time", run_time, &required_size);
if (err != ESP_OK) {
free(run_time);
return err;
}
for (int i = 0; i < required_size / sizeof(uint32_t); i++) {
printf("%d: %" PRIu32 "\n", i + 1, run_time[i]);
}
free(run_time);
}
// Close
nvs_close(my_handle);
return ESP_OK;
}
void app_main(void)
{
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
// NVS partition was truncated and needs to be erased
// Retry nvs_flash_init
ESP_ERROR_CHECK(nvs_flash_erase());
err = nvs_flash_init();
}
ESP_ERROR_CHECK( err );
err = print_what_saved();
if (err != ESP_OK) printf("Error (%s) reading data from NVS!\n", esp_err_to_name(err));
err = save_restart_counter();
if (err != ESP_OK) printf("Error (%s) saving restart counter to NVS!\n", esp_err_to_name(err));
gpio_reset_pin(BOOT_MODE_PIN);
gpio_set_direction(BOOT_MODE_PIN, GPIO_MODE_INPUT);
/* Read the status of GPIO0. If GPIO0 is LOW for longer than 1000 ms,
then save module's run time and restart it
*/
while (1) {
if (gpio_get_level(BOOT_MODE_PIN) == 0) {
vTaskDelay(1000 / portTICK_PERIOD_MS);
if(gpio_get_level(BOOT_MODE_PIN) == 0) {
err = save_run_time();
if (err != ESP_OK) printf("Error (%s) saving run time blob to NVS!\n", esp_err_to_name(err));
printf("Restarting...\n");
fflush(stdout);
esp_restart();
}
}
vTaskDelay(200 / portTICK_PERIOD_MS);
}
}

View File

@ -1,41 +0,0 @@
# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
import logging
import random
import re
import time
from typing import List
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_rw_blob(dut: Dut) -> None:
def expect_start_msg(index: int) -> None:
dut.expect('Restart counter = {}'.format(index), timeout=10)
dut.expect('Run time:', timeout=10)
expect_start_msg(0)
dut.expect('Nothing saved yet!', timeout=5)
nvs_store: List[str] = []
for i in range(1, 10):
time.sleep(random.uniform(0.1, 2)) # in order to randomize the runtimes stored in NVS
try:
# Pulling pin low using DTR
dut.serial.proc.setDTR(True)
dut.expect('Restarting...', timeout=5) # the application waits for a second
finally:
dut.serial.proc.setDTR(False)
expect_start_msg(i)
for store_item in nvs_store:
dut.expect(store_item.encode(), timeout=10)
logging.info('Received: {}'.format(', '.join(nvs_store)))
new_runtime = (dut.expect(re.compile(str.encode('{}: (\\d+)'.format(i))), timeout=10)[0]).decode()
nvs_store.append(new_runtime)
logging.info('loop {} has finished with runtime {}'.format(i, new_runtime))

View File

@ -1,86 +0,0 @@
/* Non-Volatile Storage (NVS) Read and Write a Value - 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_system.h"
#include "nvs_flash.h"
#include "nvs.h"
void app_main(void)
{
// Initialize NVS
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
// NVS partition was truncated and needs to be erased
// Retry nvs_flash_init
ESP_ERROR_CHECK(nvs_flash_erase());
err = nvs_flash_init();
}
ESP_ERROR_CHECK( err );
// Open
printf("\n");
printf("Opening Non-Volatile Storage (NVS) handle... ");
nvs_handle_t my_handle;
err = nvs_open("storage", NVS_READWRITE, &my_handle);
if (err != ESP_OK) {
printf("Error (%s) opening NVS handle!\n", esp_err_to_name(err));
} else {
printf("Done\n");
// Read
printf("Reading restart counter from NVS ... ");
int32_t restart_counter = 0; // value will default to 0, if not set yet in NVS
err = nvs_get_i32(my_handle, "restart_counter", &restart_counter);
switch (err) {
case ESP_OK:
printf("Done\n");
printf("Restart counter = %" PRIu32 "\n", restart_counter);
break;
case ESP_ERR_NVS_NOT_FOUND:
printf("The value is not initialized yet!\n");
break;
default :
printf("Error (%s) reading!\n", esp_err_to_name(err));
}
// Write
printf("Updating restart counter in NVS ... ");
restart_counter++;
err = nvs_set_i32(my_handle, "restart_counter", restart_counter);
printf((err != ESP_OK) ? "Failed!\n" : "Done\n");
// Commit written value.
// After setting any values, nvs_commit() must be called to ensure changes are written
// to flash storage. Implementations may write to storage at other times,
// but this is not guaranteed.
printf("Committing updates in NVS ... ");
err = nvs_commit(my_handle);
printf((err != ESP_OK) ? "Failed!\n" : "Done\n");
// Close
nvs_close(my_handle);
}
printf("\n");
// Restart module
for (int i = 10; i >= 0; i--) {
printf("Restarting in %d seconds...\n", i);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
printf("Restarting now.\n");
fflush(stdout);
esp_restart();
}

View File

@ -1,23 +0,0 @@
# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0
import logging
from itertools import zip_longest
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_rw_value(dut: Dut) -> None:
dut.serial.erase_flash()
dut.serial.flash()
for i, counter_state in zip_longest(range(4), ('The value is not initialized yet!',), fillvalue='Done'):
dut.expect('Opening Non-Volatile Storage \\(NVS\\) handle... Done', timeout=20)
dut.expect('Reading restart counter from NVS ... {}'.format(counter_state), timeout=20)
dut.expect('Restart counter = {}'.format(i) if int(i) > 0 else '', timeout=20)
dut.expect('Updating restart counter in NVS ... Done', timeout=20)
dut.expect('Committing updates in NVS ... Done', timeout=20)
dut.expect('Restarting in 10 seconds...', timeout=20)
logging.info('loop {} has finished'.format(i))

View File

@ -908,9 +908,6 @@ examples/protocols/static_ip/main/static_ip_example_main.c
examples/provisioning/wifi_prov_mgr/main/app_main.c
examples/security/flash_encryption/main/flash_encrypt_main.c
examples/storage/custom_flash_driver/main/main.c
examples/storage/nvs_rw_blob/main/nvs_blob_example_main.c
examples/storage/nvs_rw_value/main/nvs_value_example_main.c
examples/storage/nvs_rw_value_cxx/main/nvs_value_example_main.cpp
examples/storage/partition_api/partition_find/main/main.c
examples/storage/partition_api/partition_mmap/main/main.c
examples/storage/partition_api/partition_ops/main/main.c