mirror of
https://github.com/espressif/esp-idf.git
synced 2025-05-17 15:37:36 +08:00
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:
@ -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.
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
---------
|
||||
|
@ -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 中靠后的区域,这样可以增加引导加载程序的可用空间。
|
||||
|
@ -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 分区。
|
||||
|
||||
|
@ -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
|
||||
|
@ -37,7 +37,7 @@
|
||||
应用示例
|
||||
-----------
|
||||
|
||||
代码示例请参阅 ESP-IDF 示例 :example:`storage` 目录下的 :example:`storage/nvs_bootloader`。
|
||||
代码示例请参阅 ESP-IDF 示例 :example:`storage/nvs` 目录下的 :example:`storage/nvs/nvs_bootloader`。
|
||||
|
||||
本节演示了如何在输入/输出结构中准备数据,以支持不同的数据类型、命名空间和键。此外,还包含从 NVS 读取字符串数据的示例。
|
||||
|
||||
|
@ -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 句柄类。
|
||||
|
||||
内部实现
|
||||
---------
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
47
examples/storage/nvs/.build-test-rules.yml
Normal file
47
examples/storage/nvs/.build-test-rules.yml
Normal 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
|
@ -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:
|
Can't render this file because it has a wrong number of fields in line 4.
|
8
examples/storage/nvs/nvs_console/CMakeLists.txt
Normal file
8
examples/storage/nvs/nvs_console/CMakeLists.txt
Normal 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)
|
104
examples/storage/nvs/nvs_console/README.md
Normal file
104
examples/storage/nvs/nvs_console/README.md
Normal 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>
|
||||
...
|
||||
```
|
10
examples/storage/nvs/nvs_console/main/CMakeLists.txt
Normal file
10
examples/storage/nvs/nvs_console/main/CMakeLists.txt
Normal 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)
|
125
examples/storage/nvs/nvs_console/main/console_settings.c
Normal file
125
examples/storage/nvs/nvs_console/main/console_settings.c
Normal 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;
|
||||
}
|
48
examples/storage/nvs/nvs_console/main/console_settings.h
Normal file
48
examples/storage/nvs/nvs_console/main/console_settings.h
Normal 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
|
133
examples/storage/nvs/nvs_console/main/nvs_console_main.c
Normal file
133
examples/storage/nvs/nvs_console/main/nvs_console_main.c
Normal 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();
|
||||
}
|
15
examples/storage/nvs/nvs_console/nvs_data.csv
Normal file
15
examples/storage/nvs/nvs_console/nvs_data.csv
Normal 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.
|
22
examples/storage/nvs/nvs_console/pytest_nvs_console.py
Normal file
22
examples/storage/nvs/nvs_console/pytest_nvs_console.py
Normal 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')
|
@ -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.
|
214
examples/storage/nvs/nvs_rw_blob/main/nvs_blob_example_main.c
Normal file
214
examples/storage/nvs/nvs_rw_blob/main/nvs_blob_example_main.c
Normal 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.");
|
||||
}
|
19
examples/storage/nvs/nvs_rw_blob/pytest_nvs_rw_blob.py
Normal file
19
examples/storage/nvs/nvs_rw_blob/pytest_nvs_rw_blob.py
Normal 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)
|
@ -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.
|
161
examples/storage/nvs/nvs_rw_value/main/nvs_value_example_main.c
Normal file
161
examples/storage/nvs/nvs_rw_value/main/nvs_value_example_main.c
Normal 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");
|
||||
|
||||
}
|
23
examples/storage/nvs/nvs_rw_value/pytest_nvs_rw_value.py
Normal file
23
examples/storage/nvs/nvs_rw_value/pytest_nvs_rw_value.py
Normal 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)
|
@ -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:
|
@ -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:
|
||||
|
Can't render this file because it has a wrong number of fields in line 2.
|
@ -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);
|
||||
}
|
||||
}
|
@ -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))
|
@ -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();
|
||||
}
|
@ -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))
|
@ -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
|
||||
|
Reference in New Issue
Block a user