Merge branch 'feature/storage_nvs_bootloader' into 'master'

feat(storage/nvs): NVS bootloader support

See merge request espressif/esp-idf!31753
This commit is contained in:
Radek Tandler
2024-11-15 00:12:18 +08:00
41 changed files with 1980 additions and 70 deletions

View File

@@ -12,6 +12,8 @@ The examples are grouped into sub-directories by category. Each category directo
* `custom_flash_driver` example demonstrates how to implement your own flash chip driver by overriding the default driver.
* `emmc` example demonstrates how to use an eMMC chip with an ESP device.
* `ext_flash_fatfs` example demonstrates how to use FATFS partition with external SPI FLASH chip.
* `fatfsgen` example demonstrates how to use FATFS partition generator
* `nvs_bootloader` example demonstrates how to read data from NVS in the bootloader code.
* `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).

View File

@@ -0,0 +1,11 @@
# For more information about build system see
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
# The following five 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)
idf_build_set_property(__BUILD_COMPONENT_DEPGRAPH_ENABLED 1)
project(main)

View File

@@ -0,0 +1,125 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | -------- |
# NVS Bootloader
The purpose of this example is to show how to use the simplified, read-only API of NVS flash that can be used as a part of bootloader
## Usage of this example:
Simply compile it:
```
idf.py build
```
Then flash it and open the monitor with the following command:
```
idf.py flash monitor
```
If everything went well, the console output should contain three blocks of log messages.
For detailed explanation, please see the comments in the source code file `nvs_bootloader_example.c`
First block shows indication of invalid parameters.
```
I (765) nvs_bootloader_example_utils: Showing request data only
I (765) nvs_bootloader_example_utils: ## Namespace Key DT
I (766) nvs_bootloader_example_utils: 0 sunny_day u8 U8
I (766) nvs_bootloader_example_utils: 1 too_long_sunny_day u8 I8
I (766) nvs_bootloader_example_utils: 2 sunny_day too_long_dark_key I32
I (767) nvs_bootloader_example_utils: 3 clowny_day blobeee BLOB
I (767) nvs_bootloader_example_utils: 4 sunny_day string_10_chars STR
I (767) nvs_bootloader_example_utils: 5 sunny_day string_10_chars STR
E (769) nvs_bootloader_example: Invalid arguments passed to the read function
I (770) nvs_bootloader_example_utils: Request data with invalid arguments. Function returned ESP_ERR_INVALID_ARG
I (770) nvs_bootloader_example_utils: ## Result Namespace Key DT
I (771) nvs_bootloader_example_utils: 0 ESP_ERR_NVS_NOT_FOUND sunny_day u8 U8
E (772) nvs_bootloader_example_utils: 1 ESP_ERR_NVS_INVALID_NAME too_long_sunny_day u8 I8
E (772) nvs_bootloader_example_utils: 2 ESP_ERR_NVS_KEY_TOO_LONG sunny_day too_long_dark_key I32
E (772) nvs_bootloader_example_utils: 3 ESP_ERR_INVALID_ARG clowny_day blobeee BLOB
E (773) nvs_bootloader_example_utils: 4 ESP_ERR_INVALID_SIZE sunny_day string_10_chars STR
E (773) nvs_bootloader_example_utils: 5 ESP_ERR_INVALID_SIZE sunny_day string_10_chars STR
```
Second block shows indication of inconsistencies detected during the data reading. There can be mix of correctly read entries and entries with errors.
```
I (779) nvs_bootloader_example_utils: Showing request data only
I (779) nvs_bootloader_example_utils: ## Namespace Key DT
I (779) nvs_bootloader_example_utils: 0 sunny_day u8 I8
I (780) nvs_bootloader_example_utils: 1 sunny_day i32_ I32
I (780) nvs_bootloader_example_utils: 2 clowny_day i8 I8
I (780) nvs_bootloader_example_utils: 3 sunny_day string_10_chars STR
I (780) nvs_bootloader_example_utils: 4 sunny_day u32 U32
I (781) nvs_bootloader_example_utils: 5 sunny_day u32 U32
I (1227) nvs_bootloader_example: Data read from NVS partition
I (1227) nvs_bootloader_example_utils: Result data. Function returned ESP_OK
I (1227) nvs_bootloader_example_utils: ## Result Namespace Key DT Value
E (1228) nvs_bootloader_example_utils: 0 ESP_ERR_NVS_TYPE_MISMATCH sunny_day u8 I8
E (1228) nvs_bootloader_example_utils: 1 ESP_ERR_NVS_NOT_FOUND sunny_day i32_ I32
E (1228) nvs_bootloader_example_utils: 2 ESP_ERR_NVS_NOT_FOUND clowny_day i8 I8
E (1229) nvs_bootloader_example_utils: 3 ESP_ERR_INVALID_SIZE sunny_day string_10_chars STR
I (1229) nvs_bootloader_example_utils: 4 ESP_OK sunny_day u32 U32 4294967295
E (1229) nvs_bootloader_example_utils: 5 ESP_ERR_NVS_NOT_FOUND sunny_day u32 U32
```
Final block of log messages shows the successful reading of various data
```
I (1230) nvs_bootloader_example_utils: Showing request data only
I (1230) nvs_bootloader_example_utils: ## Namespace Key DT
I (1230) nvs_bootloader_example_utils: 0 sunny_day u8 U8
I (1231) nvs_bootloader_example_utils: 1 sunny_day i32 I32
I (1231) nvs_bootloader_example_utils: 2 cloudy_day i8 I8
I (1231) nvs_bootloader_example_utils: 3 sunny_day u32 U32
I (1231) nvs_bootloader_example_utils: 4 sunny_day i8 I8
I (1232) nvs_bootloader_example_utils: 5 sunny_day u16 U16
I (1232) nvs_bootloader_example_utils: 6 sunny_day i16 I16
I (1232) nvs_bootloader_example_utils: 7 sunny_day string_10_chars STR
I (1665) nvs_bootloader_example: Data read from NVS partition
I (1665) nvs_bootloader_example_utils: Result data. Function returned ESP_OK
I (1666) nvs_bootloader_example_utils: ## Result Namespace Key DT Value
I (1666) nvs_bootloader_example_utils: 0 ESP_OK sunny_day u8 U8 255
I (1666) nvs_bootloader_example_utils: 1 ESP_OK sunny_day i32 I32 -2147483648
I (1667) nvs_bootloader_example_utils: 2 ESP_OK cloudy_day i8 I8 -13
I (1667) nvs_bootloader_example_utils: 3 ESP_OK sunny_day u32 U32 4294967295
I (1668) nvs_bootloader_example_utils: 4 ESP_OK sunny_day i8 I8 -128
I (1668) nvs_bootloader_example_utils: 5 ESP_OK sunny_day u16 U16 65535
I (1668) nvs_bootloader_example_utils: 6 ESP_OK sunny_day i16 I16 -20000
I (1669) nvs_bootloader_example_utils: 7 ESP_OK sunny_day string_10_chars STR Text_67890
```
At the end the firmware starts regular application and just prints the message:
```
I (3212) main_task: Calling app_main()
User application is loaded and running.
I (3212) main_task: Returned from app_main()
```
## Organisation of this example
The code demonstrating the use of nvs in bootloader is in `bootloader_components/nvs_bootloader_example`. This code is executed during bootloader run.
Bootloader hooks technique is used to extend the default bootloader. The main function demonstrating the functionality is `bootloader_after_init()`
in the `src/nvs_bootloader_example`
Below is a short explanation of files in the project folder.
```
├── CMakeLists.txt
├── main
│   ├── CMakeLists.txt
│   └── main.c Regular application, just prints message and ends
├── bootloader_components
│   └── nvs_bootloader_example
│   ├── CMakeLists.txt
│   ├── nvs_bootloader_example_utils.c Supplementary functions for easier logging the content of request / response data
│   └── nvs_bootloader_example.c Implementation of main test. All supplenentary information in the form of comments can be found here.
├── nvs_data.csv Initial content of the nvs partition. Data to be read by the nvs_bootloader_read are here
└── README.md This is the file you are currently reading
```
The example creates request/response array `read_list[]`, populates it with identifiers of the data to be read.
Function `nvs_bootloader_read()` tries to find respective data in the partition (here `"nvs"`) and if the data is found, it populates the request/response array with data. For nvs entries either not found or not matching are indicated in response array as well.
Function `log_nvs_bootloader_read_list()`is used before and after reading from nvs to show request/response data to the console.

View File

@@ -0,0 +1,10 @@
idf_component_register( SRCS "src/nvs_bootloader_example.c" "src/nvs_bootloader_example_utils.c"
INCLUDE_DIRS "include"
REQUIRES "nvs_flash" )
# We need to force GCC to integrate this static library into the
# bootloader link. Indeed, by default, as the hooks in the bootloader are weak,
# the linker would just ignore the symbols in the extra. (i.e. not strictly
# required)
# To do so, we need to define the symbol (function) `bootloader_hooks_include`
# within hooks.c source file.

View File

@@ -0,0 +1,15 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "nvs_bootloader.h"
// logs the result of the call to the nvs_bootloader_read function
void log_nvs_bootloader_read_list(const esp_err_t return_code, const nvs_bootloader_read_list_t read_list[], const size_t read_list_count);
void log_nvs_bootloader_read_list_OK(const nvs_bootloader_read_list_t read_list[], const size_t read_list_count);
void log_nvs_bootloader_read_list_INVALID_ARG(const nvs_bootloader_read_list_t read_list[], const size_t read_list_count);
void log_nvs_bootloader_read_list_NOT_FINISHED(const nvs_bootloader_read_list_t read_list[], const size_t read_list_count);

View File

@@ -0,0 +1,131 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#define LOG_LOCAL_LEVEL ESP_LOG_INFO
#include "esp_log.h"
#include "nvs_bootloader.h"
#include "nvs_bootloader_example_utils.h"
static const char* TAG = "nvs_bootloader_example";
// Function used to tell the linker to include this file
// with all its symbols.
//
void bootloader_hooks_include(void){
}
// not used in this example
void bootloader_before_init(void) {
}
void log_request_call_read_evaluate_output(const char* nvs_partition_label, nvs_bootloader_read_list_t read_list[], const size_t read_list_count) {
// log the request structure before the read to see the requested keys and namespaces
// with the ESP_ERR_NOT_FINISHED return code we are just telling the log function to show the request data and omit printing the result data
// it is useful for debugging the request structure
log_nvs_bootloader_read_list(ESP_ERR_NOT_FINISHED, read_list, read_list_count);
// call the read function
esp_err_t ret = nvs_bootloader_read(nvs_partition_label, read_list_count, read_list);
// Error code ESP_OK means that the read function was successful and individual, per record results are stored in the read_list
if (ret == ESP_OK) {
ESP_LOGI(TAG, "Data read from NVS partition");
// log the request structure after the read to see the results
// some records may indicate problems with the read i.e. not found, type mismatch, etc.
log_nvs_bootloader_read_list(ret, read_list, read_list_count);
// Error code ESP_ERR_INVALID_ARG means that the read function was called with invalid arguments
} else if(ret == ESP_ERR_INVALID_ARG) {
ESP_LOGE(TAG, "Invalid arguments passed to the function");
// some records may indicate problems with the input parameters
// application developer may evaluate the read_list to see what went wrong with input parameters
log_nvs_bootloader_read_list(ret, read_list, read_list_count);
// Other error codes mean that the read function failed to read the data from the NVS partition
// The read list doesn't contain any useful data in this case
} else {
ESP_LOGE(TAG, "Failed to read NVS partition ret = %04x", ret);
}
}
// function hook called at the end of the bootloader standard code
// this is the 'main' function of the example
void bootloader_after_init(void) {
ESP_LOGI(TAG, "Before reading from NVS partition");
// we are going to read from the default nvs partition labelled 'nvs'
const char* nvs_partition_label = "nvs";
#define STR_BUFF_LEN 10+1 // 10 characters + null terminator
char str_buff[STR_BUFF_LEN];
// --- This is the request structure for the read function showing validation errors - function will return ESP_ERR_INVALID_ARG ---
nvs_bootloader_read_list_t bad_read_list_indicate_problems[] = {
{ .namespace_name = "sunny_day", .key_name = "u8", .value_type = NVS_TYPE_U8 }, // ESP_ERR_NVS_NOT_FOUND
// this is correct request, not found is expected default result code
{ .namespace_name = "too_long_sunny_day", .key_name = "u8", .value_type = NVS_TYPE_I8 }, // ESP_ERR_NVS_INVALID_NAME
// too long namespace name
{ .namespace_name = "sunny_day", .key_name = "too_long_dark_key", .value_type = NVS_TYPE_I32 }, // ESP_ERR_NVS_KEY_TOO_LONG
// too long key name
{ .namespace_name = "clowny_day", .key_name = "blobeee", .value_type = NVS_TYPE_BLOB }, // ESP_ERR_INVALID_ARG
// not supported data type
{ .namespace_name = "sunny_day", .key_name = "string_10_chars", .value_type = NVS_TYPE_STR, .value.str_val = { .buff_ptr = str_buff, .buff_len = 0 } },
// ESP_ERR_INVALID_SIZE
// buffer size is 0
{ .namespace_name = "sunny_day", .key_name = "string_10_chars", .value_type = NVS_TYPE_STR, .value.str_val = { .buff_ptr = NULL, .buff_len = 10 } }
// ESP_ERR_INVALID_SIZE
// buffer pointer is invalid
};
size_t bad_read_list_indicate_problems_count = sizeof(bad_read_list_indicate_problems) / sizeof(bad_read_list_indicate_problems[0]);
log_request_call_read_evaluate_output(nvs_partition_label, bad_read_list_indicate_problems, bad_read_list_indicate_problems_count);
// --- This is the request structure for the read function showing runtime errors - function will return ESP_OK ---
// but some records will have result_code set to ESP_ERR_NVS_NOT_FOUND, ESP_ERR_NVS_TYPE_MISMATCH, ESP_ERR_INVALID_SIZE
nvs_bootloader_read_list_t good_read_list_bad_results[] = {
{ .namespace_name = "sunny_day", .key_name = "u8", .value_type = NVS_TYPE_I8 }, // ESP_ERR_NVS_TYPE_MISMATCH
// data in the partition is of different type (NVS_TYPE_U8)
{ .namespace_name = "sunny_day", .key_name = "i32_", .value_type = NVS_TYPE_I32 }, // ESP_ERR_NVS_NOT_FOUND
// data in the partition won't be found, because there is a typo in the key name
{ .namespace_name = "clowny_day", .key_name = "i8", .value_type = NVS_TYPE_I8 }, // ESP_ERR_NVS_NOT_FOUND
// data in the partition won't be found, because there is typo in namespace name
{ .namespace_name = "sunny_day", .key_name = "string_10_chars", .value_type = NVS_TYPE_STR, .value.str_val = { .buff_ptr = str_buff, .buff_len = 2 } },
// ESP_ERR_INVALID_SIZE
// buffer is too small
{ .namespace_name = "sunny_day", .key_name = "u32", .value_type = NVS_TYPE_U32 }, // ESP_OK
// this value will be read correctly
{ .namespace_name = "sunny_day", .key_name = "u32", .value_type = NVS_TYPE_U32 } // ESP_ERR_NVS_NOT_FOUND
// this value won't be read as function doesn't support duplicate readings
};
size_t good_read_list_bad_results_count = sizeof(good_read_list_bad_results) / sizeof(good_read_list_bad_results[0]);
log_request_call_read_evaluate_output(nvs_partition_label, good_read_list_bad_results, good_read_list_bad_results_count);
// --- This is the request structure for the read function showing all records found---
// function will return ESP_OK, all individual records will have result_code set to ESP_OK
// Order of the requested keys and namespaces is not important
// We mix different types of data to demonstrate the usage of different data types and also mix-in different namespaces
// For NVS_TYPE_I* and NVS_TYPE_U* the value field is used directly to store the read value
// For NVS_TYPE_STR the value field is a structure with a pointer to the buffer and the buffer length is povided
// In this case, the buffer is a stack allocated array of 10 characters plus space for the null terminator
nvs_bootloader_read_list_t good_read_list[] = {
{ .namespace_name = "sunny_day", .key_name = "u8", .value_type = NVS_TYPE_U8 },
{ .namespace_name = "sunny_day", .key_name = "i32", .value_type = NVS_TYPE_I32 },
{ .namespace_name = "cloudy_day", .key_name = "i8", .value_type = NVS_TYPE_I8 }, // mixed in different namespace
{ .namespace_name = "sunny_day", .key_name = "u16", .value_type = NVS_TYPE_U16 },
{ .namespace_name = "sunny_day", .key_name = "string_10_chars", .value_type = NVS_TYPE_STR, .value.str_val = { .buff_ptr = str_buff, .buff_len = STR_BUFF_LEN } }
};
size_t good_read_list_count = sizeof(good_read_list) / sizeof(good_read_list[0]);
log_request_call_read_evaluate_output(nvs_partition_label, good_read_list, good_read_list_count);
ESP_LOGI(TAG, "Finished bootloader part");
}

View File

@@ -0,0 +1,216 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#define LOG_LOCAL_LEVEL ESP_LOG_INFO
#include "esp_log.h"
#include "nvs_bootloader_example_utils.h"
static const char* TAG = "nvs_bootloader_example_utils";
static const char* result_code_to_string(const esp_err_t result_code);
/* logs the result of the call to the nvs_bootloader_read function */
void log_nvs_bootloader_read_list(const esp_err_t return_code, const nvs_bootloader_read_list_t read_list[], const size_t read_list_count)
{
switch (return_code) {
// read list contains data after successful read
case ESP_OK: {
ESP_LOGI(TAG, "Result data. Return code: %s", result_code_to_string(return_code));
log_nvs_bootloader_read_list_OK(read_list, read_list_count);
break;
}
// read list contains data failing validation
case ESP_ERR_INVALID_ARG: {
ESP_LOGI(TAG, "Request data with invalid arguments. Return code: %s", result_code_to_string(return_code));
log_nvs_bootloader_read_list_INVALID_ARG(read_list, read_list_count);
break;
}
// request data only
case ESP_ERR_NOT_FINISHED: {
ESP_LOGI(TAG, "Showing request data only. Return code: %s", result_code_to_string(return_code));
log_nvs_bootloader_read_list_NOT_FINISHED(read_list, read_list_count);
break;
}
default: {
ESP_LOGI(TAG, "Function returned: %04x %s", return_code, result_code_to_string(return_code));
break;
}
}
}
static const char* result_code_to_string(const esp_err_t result_code)
{
switch (result_code) {
case ESP_ERR_NVS_NOT_FOUND: return "ESP_ERR_NVS_NOT_FOUND";
case ESP_OK: return "ESP_OK";
case ESP_ERR_NVS_TYPE_MISMATCH: return "ESP_ERR_NVS_TYPE_MISMATCH";
case ESP_ERR_INVALID_ARG: return "ESP_ERR_INVALID_ARG";
case ESP_ERR_NVS_INVALID_NAME: return "ESP_ERR_NVS_INVALID_NAME";
case ESP_ERR_NVS_KEY_TOO_LONG: return "ESP_ERR_NVS_KEY_TOO_LONG";
case ESP_ERR_INVALID_SIZE: return "ESP_ERR_INVALID_SIZE";
default: return "Unknown";
}
}
static const char* value_type_to_string(const nvs_type_t value_type)
{
switch (value_type) {
case NVS_TYPE_U8: return "U8";
case NVS_TYPE_U16: return "U16";
case NVS_TYPE_U32: return "U32";
case NVS_TYPE_U64: return "U64";
case NVS_TYPE_I8: return "I8";
case NVS_TYPE_I16: return "I16";
case NVS_TYPE_I32: return "I32";
case NVS_TYPE_I64: return "I64";
case NVS_TYPE_STR: return "STR";
case NVS_TYPE_BLOB: return "BLOB";
default: return "Unknown";
}
}
void log_nvs_bootloader_read_list_OK(const nvs_bootloader_read_list_t read_list[], const size_t read_list_count)
{
bool i_type, u_type = false;
uint64_t u64_val = 0;
int64_t i64_val = 0;
ESP_LOGI(TAG, "## Result Namespace Key DT Value");
for (size_t i = 0; i < read_list_count; i++) {
switch (read_list[i].result_code) {
case ESP_OK: {
switch (read_list[i].value_type) {
case NVS_TYPE_U8: {
u64_val = read_list[i].value.u8_val;
u_type = true;
break;
}
case NVS_TYPE_U16: {
u64_val = read_list[i].value.u16_val;
u_type = true;
break;
}
case NVS_TYPE_U32: {
u64_val = read_list[i].value.u32_val;
u_type = true;
break;
}
case NVS_TYPE_U64: {
u64_val = read_list[i].value.u64_val;
u_type = true;
break;
}
case NVS_TYPE_I8: {
i64_val = read_list[i].value.i8_val;
i_type = true;
break;
}
case NVS_TYPE_I16: {
i64_val = read_list[i].value.i16_val;
i_type = true;
break;
}
case NVS_TYPE_I32: {
i64_val = read_list[i].value.i32_val;
i_type = true;
break;
}
case NVS_TYPE_I64: {
i64_val = read_list[i].value.i64_val;
i_type = true;
break;
}
case NVS_TYPE_STR: {
ESP_LOGI(TAG, "%2d %-25s %-16s %-16s %-3s %s",
i,
result_code_to_string(read_list[i].result_code),
read_list[i].namespace_name,
read_list[i].key_name,
value_type_to_string(read_list[i].value_type),
read_list[i].value.str_val.buff_ptr);
continue;
}
default: {
u64_val = 0;
i64_val = 0;
break;
}
}
if (i_type) {
ESP_LOGI(TAG, "%2d %-25s %-16s %-16s %-3s %lld",
i,
result_code_to_string(read_list[i].result_code),
read_list[i].namespace_name,
read_list[i].key_name,
value_type_to_string(read_list[i].value_type),
i64_val);
} else if (u_type) {
ESP_LOGI(TAG, "%2d %-25s %-16s %-16s %-3s %llu",
i,
result_code_to_string(read_list[i].result_code),
read_list[i].namespace_name,
read_list[i].key_name,
value_type_to_string(read_list[i].value_type),
u64_val);
}
i_type = false;
u_type = false;
break;
}
default: {
// data was not found or other error
ESP_LOGE(TAG, "%2d %-25s %-16s %-16s %-3s",
i,
result_code_to_string(read_list[i].result_code),
read_list[i].namespace_name,
read_list[i].key_name,
value_type_to_string(read_list[i].value_type));
break;
}
}
}
}
void log_nvs_bootloader_read_list_INVALID_ARG(const nvs_bootloader_read_list_t read_list[], const size_t read_list_count)
{
ESP_LOGI(TAG, "## Result Namespace Key DT");
for (size_t i = 0; i < read_list_count; i++) {
if (read_list[i].result_code != ESP_ERR_NVS_NOT_FOUND) {
ESP_LOGE(TAG, "%2d %-25s %-16s %-16s %-3s",
i,
result_code_to_string(read_list[i].result_code),
read_list[i].namespace_name,
read_list[i].key_name,
value_type_to_string(read_list[i].value_type));
} else {
ESP_LOGI(TAG, "%2d %-25s %-16s %-16s %-3s",
i,
result_code_to_string(read_list[i].result_code),
read_list[i].namespace_name,
read_list[i].key_name,
value_type_to_string(read_list[i].value_type));
}
}
}
void log_nvs_bootloader_read_list_NOT_FINISHED(const nvs_bootloader_read_list_t read_list[], const size_t read_list_count)
{
ESP_LOGI(TAG, "## Namespace Key DT");
for (size_t i = 0; i < read_list_count; i++) {
ESP_LOGI(TAG, "%2d %-16s %-16s %-3s",
i,
read_list[i].namespace_name,
read_list[i].key_name,
value_type_to_string(read_list[i].value_type));
}
}

View File

@@ -0,0 +1,3 @@
idf_component_register(SRCS "bootloader_hooks_example_main.c"
INCLUDE_DIRS ".")
nvs_create_partition_image(nvs ../nvs_data.csv FLASH_IN_PROJECT)

View File

@@ -0,0 +1,16 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
void app_main(void)
{
/**
* Nothing special is done here, everything interesting in this example
* is done in the custom bootloader code, located in:
* `bootloader_components/nvs_bootloader_test/nvs_bootloader_test.c`
*/
printf("User application is loaded and running.\n");
}

View File

@@ -0,0 +1,15 @@
# Sample csv file
# Defines sunny_day namespace holding data for example
# string entry contains 10 characters and terminating zero
key,type,encoding,value
sunny_day,namespace,,
u8,data,u8,255
i8,data,i8,-128
u16,data,u16,65535
i16,data,i16,-20000
u32,data,u32,4294967295
i32,data,i32,-2147483648
string_10_chars,data,string,"Text_67890"
# defines cloudy_day namespace to show it can be read at once with sunny_day one
cloudy_day,namespace,,
i8,data,i8,-13
Can't render this file because it has a wrong number of fields in line 4.

View File

@@ -0,0 +1,13 @@
# SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
@pytest.mark.supported_targets
@pytest.mark.generic
def test_nvs_bootloader_example(dut: Dut) -> None:
# Expect to read hooks messages and data from NVS partition
dut.expect_exact('Before reading from NVS partition')
dut.expect_exact('Finished bootloader part')
dut.expect_exact('User application is loaded and running.')

View File

@@ -0,0 +1,4 @@
# The size of bootloader has been increased, so the partition table offset needs adjustment
CONFIG_PARTITION_TABLE_OFFSET=0xA000
CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y
CONFIG_BOOTLOADER_LOG_LEVEL=2