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
-38
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
+1
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.
@@ -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:
@@ -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
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>
...
```
@@ -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)
@@ -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;
}
@@ -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
@@ -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();
}
@@ -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."
1 # Sample csv file
2 key type encoding value
3 storage_1 namespace
4 u8_key data u8 255
5 i8_key data i8 -128
6 u16_key data u16 65535
7 storage_2 namespace
8 u32_key data u32 4294967295
9 i32_key data i32 -2147483648
10 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.
@@ -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.
@@ -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.");
}
@@ -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.
@@ -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");
}
@@ -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:
@@ -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))