mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-08 04:02:27 +00:00
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:
@@ -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).
|
||||
|
11
examples/storage/nvs_bootloader/CMakeLists.txt
Normal file
11
examples/storage/nvs_bootloader/CMakeLists.txt
Normal 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)
|
125
examples/storage/nvs_bootloader/README.md
Normal file
125
examples/storage/nvs_bootloader/README.md
Normal 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.
|
@@ -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.
|
@@ -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);
|
@@ -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");
|
||||
}
|
@@ -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));
|
||||
}
|
||||
}
|
3
examples/storage/nvs_bootloader/main/CMakeLists.txt
Normal file
3
examples/storage/nvs_bootloader/main/CMakeLists.txt
Normal 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)
|
@@ -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");
|
||||
}
|
15
examples/storage/nvs_bootloader/nvs_data.csv
Normal file
15
examples/storage/nvs_bootloader/nvs_data.csv
Normal 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.
|
13
examples/storage/nvs_bootloader/pytest_nvs_bootloader.py
Normal file
13
examples/storage/nvs_bootloader/pytest_nvs_bootloader.py
Normal 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.')
|
4
examples/storage/nvs_bootloader/sdkconfig.defaults
Normal file
4
examples/storage/nvs_bootloader/sdkconfig.defaults
Normal 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
|
Reference in New Issue
Block a user