forked from espressif/esp-idf
Merge branch 'contrib/github_pr_11926' into 'master'
Support NVS image generation from CMake (GitHub PR) Closes IDFGH-10702 and IDFGH-10542 See merge request espressif/esp-idf!25102
This commit is contained in:
64
components/nvs_flash/project_include.cmake
Normal file
64
components/nvs_flash/project_include.cmake
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
# nvs_create_partition_image
|
||||||
|
#
|
||||||
|
# Create a NVS image of the specified CSV on the host during build and
|
||||||
|
# optionally have the created image flashed using `idf.py flash`
|
||||||
|
function(nvs_create_partition_image partition csv)
|
||||||
|
set(options FLASH_IN_PROJECT)
|
||||||
|
set(one VERSION)
|
||||||
|
set(multi DEPENDS)
|
||||||
|
cmake_parse_arguments(arg "${options}" "${one}" "${multi}" "${ARGN}")
|
||||||
|
|
||||||
|
# Default to version 2
|
||||||
|
if(NOT DEFINED arg_VERSION)
|
||||||
|
set(arg_VERSION 2)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
idf_build_get_property(idf_path IDF_PATH)
|
||||||
|
set(nvs_partition_gen_py
|
||||||
|
${PYTHON}
|
||||||
|
${idf_path}/components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py
|
||||||
|
)
|
||||||
|
|
||||||
|
get_filename_component(csv_full_path ${csv} ABSOLUTE)
|
||||||
|
|
||||||
|
partition_table_get_partition_info(size "--partition-name ${partition}" "size")
|
||||||
|
partition_table_get_partition_info(offset "--partition-name ${partition}" "offset")
|
||||||
|
|
||||||
|
if("${size}" AND "${offset}")
|
||||||
|
set(image_file ${CMAKE_BINARY_DIR}/${partition}.bin)
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${image_file}
|
||||||
|
COMMAND ${nvs_partition_gen_py} generate --version ${arg_VERSION} ${csv_full_path} ${image_file} ${size}
|
||||||
|
MAIN_DEPENDENCY ${csv_full_path}
|
||||||
|
DEPENDS ${arg_DEPENDS}
|
||||||
|
COMMENT "Generating NVS partition image for ${partition} from ${csv}"
|
||||||
|
)
|
||||||
|
|
||||||
|
add_custom_target(nvs_${partition}_bin ALL DEPENDS ${image_file})
|
||||||
|
|
||||||
|
set_property(
|
||||||
|
DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
|
||||||
|
APPEND
|
||||||
|
PROPERTY ADDITIONAL_CLEAN_FILES ${image_file}
|
||||||
|
)
|
||||||
|
|
||||||
|
idf_component_get_property(main_args esptool_py FLASH_ARGS)
|
||||||
|
idf_component_get_property(sub_args esptool_py FLASH_SUB_ARGS)
|
||||||
|
esptool_py_flash_target(${partition}-flash "${main_args}" "${sub_args}" ALWAYS_PLAINTEXT)
|
||||||
|
esptool_py_flash_to_partition(${partition}-flash "${partition}" "${image_file}")
|
||||||
|
|
||||||
|
add_dependencies(${partition}-flash nvs_${partition}_bin)
|
||||||
|
|
||||||
|
if(arg_FLASH_IN_PROJECT)
|
||||||
|
esptool_py_flash_to_partition(flash "${partition}" "${image_file}")
|
||||||
|
add_dependencies(flash nvs_${partition}_bin)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
set(message
|
||||||
|
"Failed to create NVS image for partition '${partition}'. "
|
||||||
|
"Check project configuration if using the correct partition table file."
|
||||||
|
)
|
||||||
|
fail_at_build_time(nvs_${partition}_bin "${message}")
|
||||||
|
endif()
|
||||||
|
endfunction()
|
@@ -92,6 +92,40 @@ NVS Partition Generator Utility
|
|||||||
|
|
||||||
This utility helps generate NVS partition binary files which can be flashed separately on a dedicated partition via a flashing utility. Key-value pairs to be flashed onto the partition can be provided via a CSV file. For more details, please refer to :doc:`nvs_partition_gen`.
|
This utility helps generate NVS partition binary files which can be flashed separately on a dedicated partition via a flashing utility. Key-value pairs to be flashed onto the partition can be provided via a CSV file. For more details, please refer to :doc:`nvs_partition_gen`.
|
||||||
|
|
||||||
|
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(<partition> <csv> [FLASH_IN_PROJECT] [DEPENDS dep dep dep ...])
|
||||||
|
|
||||||
|
**Positional Arguments**:
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
:header-rows: 1
|
||||||
|
|
||||||
|
* - Parameter
|
||||||
|
- Description
|
||||||
|
* - ``partition``
|
||||||
|
- Name of the NVS parition
|
||||||
|
* - ``csv``
|
||||||
|
- Path to CSV file to parse
|
||||||
|
|
||||||
|
|
||||||
|
**Optional Arguments**:
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
:header-rows: 1
|
||||||
|
|
||||||
|
* - Parameter
|
||||||
|
- Description
|
||||||
|
* - ``FLASH_IN_PROJECT``
|
||||||
|
- Name of the NVS parition
|
||||||
|
* - ``DEPENDS``
|
||||||
|
- Specify files on which the command depends
|
||||||
|
|
||||||
|
|
||||||
|
If ``FLASH_IN_PROJECT`` is not specified, the image will still be generated, but you will have to flash it manually using ``idf.py <partition>-flash`` (e.g., if your parition name is ``nvs``, then use ``idf.py nvs-flash``).
|
||||||
|
|
||||||
|
``nvs_create_partition_image`` must be called from one of the component ``CMakeLists.txt`` files. Currently, only non-encrypted partitions are supported.
|
||||||
|
|
||||||
Application Example
|
Application Example
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|
@@ -92,6 +92,40 @@ NVS 分区生成程序
|
|||||||
|
|
||||||
NVS 分区生成程序帮助生成 NVS 分区二进制文件,可使用烧录程序将二进制文件单独烧录至特定分区。烧录至分区上的键值对由 CSV 文件提供,详情请参考 :doc:`nvs_partition_gen`。
|
NVS 分区生成程序帮助生成 NVS 分区二进制文件,可使用烧录程序将二进制文件单独烧录至特定分区。烧录至分区上的键值对由 CSV 文件提供,详情请参考 :doc:`nvs_partition_gen`。
|
||||||
|
|
||||||
|
可以直接使用函数 ``nvs_create_partition_image`` 通过 CMake 创建分区二进制文件,无需手动调用 ``nvs_partition_gen.py`` 工具::
|
||||||
|
|
||||||
|
nvs_create_partition_image(<partition> <csv> [FLASH_IN_PROJECT] [DEPENDS dep dep dep ...])
|
||||||
|
|
||||||
|
**位置参数**:
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
:header-rows: 1
|
||||||
|
|
||||||
|
* - 参数
|
||||||
|
- 描述
|
||||||
|
* - ``partition``
|
||||||
|
- NVS 分区名
|
||||||
|
* - ``csv``
|
||||||
|
- 解析的 CSV 文件路径
|
||||||
|
|
||||||
|
|
||||||
|
**可选参数**:
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
:header-rows: 1
|
||||||
|
|
||||||
|
* - 参数
|
||||||
|
- 描述
|
||||||
|
* - ``FLASH_IN_PROJECT``
|
||||||
|
- NVS 分区名
|
||||||
|
* - ``DEPENDS``
|
||||||
|
- 指定命令依赖的文件
|
||||||
|
|
||||||
|
|
||||||
|
在没有指定 ``FLASH_IN_PROJECT`` 的情况下,也支持生成分区镜像,不过此时需要使用 ``idf.py <partition>-flash`` 手动进行烧录。举个例子,如果分区名为 ``nvs``,则需使用的命令为 ``idf.py nvs-flash``。
|
||||||
|
|
||||||
|
目前,仅支持从组件中的 ``CMakeLists.txt`` 文件调用 ``nvs_create_partition_image``,且此选项仅适用于非加密分区。
|
||||||
|
|
||||||
应用示例
|
应用示例
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|
@@ -61,6 +61,16 @@ examples/storage/nvs_rw_value_cxx:
|
|||||||
temporary: true
|
temporary: true
|
||||||
reason: lack of runners
|
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:
|
examples/storage/partition_api/partition_find:
|
||||||
disable:
|
disable:
|
||||||
- if: IDF_TARGET == "esp32c2"
|
- if: IDF_TARGET == "esp32c2"
|
||||||
|
6
examples/storage/nvsgen/CMakeLists.txt
Normal file
6
examples/storage/nvsgen/CMakeLists.txt
Normal file
@@ -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)
|
54
examples/storage/nvsgen/README.md
Normal file
54
examples/storage/nvsgen/README.md
Normal file
@@ -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.
|
8
examples/storage/nvsgen/main/CMakeLists.txt
Normal file
8
examples/storage/nvsgen/main/CMakeLists.txt
Normal file
@@ -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)
|
89
examples/storage/nvsgen/main/nvsgen_example_main.c
Normal file
89
examples/storage/nvsgen/main/nvsgen_example_main.c
Normal file
@@ -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 <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 = "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");
|
||||||
|
}
|
13
examples/storage/nvsgen/nvs_data.csv
Normal file
13
examples/storage/nvsgen/nvs_data.csv
Normal file
@@ -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."
|
Can't render this file because it has a wrong number of fields in line 2.
|
6
examples/storage/nvsgen/partitions_example.csv
Normal file
6
examples/storage/nvsgen/partitions_example.csv
Normal file
@@ -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,
|
|
11
examples/storage/nvsgen/pytest_nvsgen_example.py
Normal file
11
examples/storage/nvsgen/pytest_nvsgen_example.py
Normal file
@@ -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)
|
3
examples/storage/nvsgen/sdkconfig.defaults
Normal file
3
examples/storage/nvsgen/sdkconfig.defaults
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||||
|
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv"
|
||||||
|
CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv"
|
Reference in New Issue
Block a user