From b988242b07dfb834837c2281b7b19b9d2dcad735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20M=C3=BAdry?= Date: Fri, 4 Aug 2023 10:46:58 +0200 Subject: [PATCH] feat(examples): Add storage/nvsgen example Showcases the use of `nvs_create_partition_image` CMake function Remove mentions of NVS multipage blob version 1 so it's not encouraged --- docs/en/api-reference/storage/nvs_flash.rst | 8 +- examples/storage/.build-test-rules.yml | 10 +++ examples/storage/nvsgen/CMakeLists.txt | 6 ++ examples/storage/nvsgen/README.md | 54 +++++++++++ examples/storage/nvsgen/main/CMakeLists.txt | 8 ++ .../storage/nvsgen/main/nvsgen_example_main.c | 89 +++++++++++++++++++ examples/storage/nvsgen/nvs_data.csv | 13 +++ .../storage/nvsgen/partitions_example.csv | 6 ++ .../storage/nvsgen/pytest_nvsgen_example.py | 11 +++ examples/storage/nvsgen/sdkconfig.defaults | 3 + 10 files changed, 201 insertions(+), 7 deletions(-) create mode 100644 examples/storage/nvsgen/CMakeLists.txt create mode 100644 examples/storage/nvsgen/README.md create mode 100644 examples/storage/nvsgen/main/CMakeLists.txt create mode 100644 examples/storage/nvsgen/main/nvsgen_example_main.c create mode 100644 examples/storage/nvsgen/nvs_data.csv create mode 100644 examples/storage/nvsgen/partitions_example.csv create mode 100644 examples/storage/nvsgen/pytest_nvsgen_example.py create mode 100644 examples/storage/nvsgen/sdkconfig.defaults diff --git a/docs/en/api-reference/storage/nvs_flash.rst b/docs/en/api-reference/storage/nvs_flash.rst index 475479cca4..c4242ab42b 100644 --- a/docs/en/api-reference/storage/nvs_flash.rst +++ b/docs/en/api-reference/storage/nvs_flash.rst @@ -94,7 +94,7 @@ This utility helps generate NVS partition binary files which can be flashed sepa Instead of calling the ``nvs_partition_gen.py`` tool manually, the creation of the partition binary files can also be done directly from CMake using the function ``nvs_create_partition_image``:: - nvs_create_partition_image( [FLASH_IN_PROJECT] [VERSION 1 | 2] [DEPENDS dep dep dep ...]) + nvs_create_partition_image( [FLASH_IN_PROJECT] [DEPENDS dep dep dep ...]) **Positional Arguments**: @@ -118,12 +118,6 @@ Instead of calling the ``nvs_partition_gen.py`` tool manually, the creation of t - Description * - ``FLASH_IN_PROJECT`` - Name of the NVS parition - * - ``VERSION {1,2}`` - - Set multipage blob version (Default: Version 2) - - Version 1 = support disabled - - Version 2 = support enabled * - ``DEPENDS`` - Specify files on which the command depends diff --git a/examples/storage/.build-test-rules.yml b/examples/storage/.build-test-rules.yml index 7dced03546..d06ccaf471 100644 --- a/examples/storage/.build-test-rules.yml +++ b/examples/storage/.build-test-rules.yml @@ -61,6 +61,16 @@ examples/storage/nvs_rw_value_cxx: temporary: true reason: lack of runners +examples/storage/nvsgen: + disable: + - if: IDF_TARGET == "esp32c2" + temporary: true + reason: target esp32c2 is not supported yet + disable_test: + - if: IDF_TARGET in ["esp32s2", "esp32s3", "esp32c3", "esp32c6", "esp32h2"] + temporary: true + reason: lack of runners, should be same for every target + examples/storage/partition_api/partition_find: disable: - if: IDF_TARGET == "esp32c2" diff --git a/examples/storage/nvsgen/CMakeLists.txt b/examples/storage/nvsgen/CMakeLists.txt new file mode 100644 index 0000000000..3bdfd31f78 --- /dev/null +++ b/examples/storage/nvsgen/CMakeLists.txt @@ -0,0 +1,6 @@ +# 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) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(nvsgen) diff --git a/examples/storage/nvsgen/README.md b/examples/storage/nvsgen/README.md new file mode 100644 index 0000000000..81e4dd021f --- /dev/null +++ b/examples/storage/nvsgen/README.md @@ -0,0 +1,54 @@ +| Supported Targets | ESP32 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | + +# NVS Partition Image Generation on Build Example + +(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. + +The following gives an overview of the example: + +1. There is a file `nvs_data.csv` from which the NVS partition image will be created. + +2. The function `nvs_create_partition_image` is used to specify that a NVS image should be created during build for the `nvs` partition. It is called from [the main component's CMakeLists.txt](./main/CMakeLists.txt). `FLASH_IN_PROJECT` specifies that the created image should be flashed on invocation of `idf.py -p PORT flash` together with app, bootloader, partition table, etc. For both build systems, the image is created on the example's build directory with the output filename `nvs.bin`. + +3. Upon invocation of `idf.py -p PORT flash monitor`, application loads and finds there is already a valid and pre-filled NVS partition in the `nvs` partition with values same as those in `nvs_data.csv` file. The application is then able to read those values. + +## How to use example + +### Build and flash + +To run the example, type the following command: + +```CMake +idf.py -p PORT flash monitor +``` + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Example output + +Here is the example's console output: + +``` +... +I (357) example: Opening Non-Volatile Storage (NVS) handle +I (357) example: Done +I (357) example: Reading values from NVS +255 +-128 +65535 +4294967295 +-2147483648 +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. +I (387) example: Reading values from NVS done - all OK +``` + +The logic of the example is contained in a [single source file](./main/nvsgen_example_main.c), and it should be relatively simple to match points in its execution with the log outputs above. diff --git a/examples/storage/nvsgen/main/CMakeLists.txt b/examples/storage/nvsgen/main/CMakeLists.txt new file mode 100644 index 0000000000..6c3be52671 --- /dev/null +++ b/examples/storage/nvsgen/main/CMakeLists.txt @@ -0,0 +1,8 @@ +idf_component_register(SRCS "nvsgen_example_main.c" + 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) diff --git a/examples/storage/nvsgen/main/nvsgen_example_main.c b/examples/storage/nvsgen/main/nvsgen_example_main.c new file mode 100644 index 0000000000..4ce28e4a2b --- /dev/null +++ b/examples/storage/nvsgen/main/nvsgen_example_main.c @@ -0,0 +1,89 @@ +/* Non-Volatile Storage (NVS) Image Generation on Build Example + * + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include +#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 = "example"; + +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 the pre-filled NVS partition called "nvs" + ESP_LOGI(TAG, "Opening Non-Volatile Storage (NVS) handle"); + nvs_handle_t my_handle; + err = nvs_open_from_partition("nvs", "storage", NVS_READONLY, &my_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Error (%s) opening NVS handle!\n", esp_err_to_name(err)); + return; + } + ESP_LOGI(TAG, "The NVS handle successfully opened"); + + // Read values + ESP_LOGI(TAG, "Reading values from NVS"); + + uint8_t u8_val = 0; + err = nvs_get_u8(my_handle, "u8_key", &u8_val); + ESP_ERROR_CHECK(err); + printf("%"PRIu8"\n", u8_val); + assert(u8_val == 255); + + int8_t i8_val = 0; + err = nvs_get_i8(my_handle, "i8_key", &i8_val); + ESP_ERROR_CHECK(err); + printf("%"PRIi8"\n", i8_val); + assert(i8_val == -128); + + uint16_t u16_val = 0; + err = nvs_get_u16(my_handle, "u16_key", &u16_val); + ESP_ERROR_CHECK(err); + printf("%"PRIu16"\n", u16_val); + assert(u16_val == 65535); + + uint32_t u32_val = 0; + err = nvs_get_u32(my_handle, "u32_key", &u32_val); + ESP_ERROR_CHECK(err); + printf("%"PRIu32"\n", u32_val); + assert(u32_val == 4294967295); + + int32_t i32_val = 0; + err = nvs_get_i32(my_handle, "i32_key", &i32_val); + ESP_ERROR_CHECK(err); + printf("%"PRIi32"\n", i32_val); + assert(i32_val == -2147483648); + + size_t str_len = 0; + err = nvs_get_str(my_handle, "str_key", NULL, &str_len); + ESP_ERROR_CHECK(err); + assert(str_len == 222); + + char* str_val = (char*) malloc(str_len); + err = nvs_get_str(my_handle, "str_key", str_val, &str_len); + ESP_ERROR_CHECK(err); + printf("%s\n", str_val); + assert(str_val[0] == 'L'); + free(str_val); + + // Close + nvs_close(my_handle); + ESP_LOGI(TAG, "Reading values from NVS done - all OK"); +} diff --git a/examples/storage/nvsgen/nvs_data.csv b/examples/storage/nvsgen/nvs_data.csv new file mode 100644 index 0000000000..8e0cc35695 --- /dev/null +++ b/examples/storage/nvsgen/nvs_data.csv @@ -0,0 +1,13 @@ +# Sample csv file +key,type,encoding,value +storage,namespace,, +u8_key,data,u8,255 +i8_key,data,i8,-128 +u16_key,data,u16,65535 +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." diff --git a/examples/storage/nvsgen/partitions_example.csv b/examples/storage/nvsgen/partitions_example.csv new file mode 100644 index 0000000000..40000b73b5 --- /dev/null +++ b/examples/storage/nvsgen/partitions_example.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x6000, +phy_init, data, phy, 0xf000, 0x1000, +factory, app, factory, 0x10000, 1M, diff --git a/examples/storage/nvsgen/pytest_nvsgen_example.py b/examples/storage/nvsgen/pytest_nvsgen_example.py new file mode 100644 index 0000000000..cc3360a5c5 --- /dev/null +++ b/examples/storage/nvsgen/pytest_nvsgen_example.py @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Unlicense OR CC0-1.0 + +import pytest +from pytest_embedded import Dut + + +@pytest.mark.esp32 +def test_nvsgen_example(dut: Dut) -> None: + dut.expect('Reading values from NVS', timeout=10) + dut.expect('Reading values from NVS done - all OK', timeout=10) diff --git a/examples/storage/nvsgen/sdkconfig.defaults b/examples/storage/nvsgen/sdkconfig.defaults new file mode 100644 index 0000000000..b9bb0c0a5d --- /dev/null +++ b/examples/storage/nvsgen/sdkconfig.defaults @@ -0,0 +1,3 @@ +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv"