mirror of
https://gitee.com/beecue/fastbee.git
synced 2025-12-19 17:35:54 +08:00
添加智能灯固件代码
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
# Storage Examples
|
||||
|
||||
Storage and management of user and system data in module’s flash and on external memory / devices.
|
||||
|
||||
See the [README.md](../README.md) file in the upper level [examples](../) directory for more information about examples.
|
||||
@@ -0,0 +1,7 @@
|
||||
# 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.5)
|
||||
|
||||
# external SPI flash driver not currently supported for ESP32-S2
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(ext_flash_fatfs)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := ext_flash_fatfs
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
| Supported Targets | ESP32 |
|
||||
| ----------------- | ----- |
|
||||
|
||||
# FAT FS on External Flash example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
This example is similar to the [wear levelling](../wear_levelling/README.md) example, except that it uses an external SPI Flash chip. This can be useful if you need to add more storage to a module with only 4 MB flash size.
|
||||
|
||||
The flow of the example is as follows:
|
||||
|
||||
1. Initialize the SPI bus and configure the pins. In this example, VSPI peripheral is used. The pins chosen in this example correspond to IOMUX pins for the VSPI peripheral. If the pin assignment is changed, SPI driver will instead connect the peripheral to the pins using the GPIO Matrix.
|
||||
|
||||
2. Initialize the SPI flash chip. This involves creating a run-time object which describes the flash chip (`esp_flash_t`), probing the flash chip, and configuring it for the selected read mode. By default this example uses DIO mode, which only requires 4 pins (MOSI, MISO, SCLK, CS). For modes such as QIO and QOUT, additional pins must be connected.
|
||||
|
||||
3. Register the entire area of the Flash chip as a *partition* (`esp_partition_t`). This allows other components (FATFS, SPIFFS, NVS, etc) to use the storage provided by the external flash chip.
|
||||
|
||||
4. Do some read and write operations using C standard library functions: create a file, write to it, open it for reading, print the contents to the console.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware required
|
||||
|
||||
This example needs an SPI NOR Flash chip connected to the ESP32. The SPI Flash chip must have 3.3V logic levels. The example has been tested with Winbond W25Q32 SPI Flash chip.
|
||||
|
||||
Use the following pin assignments:
|
||||
|
||||
ESP32 pin | SPI bus signal | SPI Flash pin
|
||||
--------------|----------------|----------------
|
||||
GPIO23 | MOSI | DI
|
||||
GPIO19 | MISO | DO
|
||||
GPIO18 | SCLK | CLK
|
||||
GPIO5 | CS | CMD
|
||||
unconnected | | WP
|
||||
unconnected | | HOLD
|
||||
GND | | GND
|
||||
VCC | | VCC
|
||||
|
||||
### Build and flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(Replace PORT with serial port name.)
|
||||
|
||||
(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 a typical example console output.
|
||||
|
||||
```
|
||||
I (328) example: Initializing external SPI Flash
|
||||
I (338) example: Pin assignments:
|
||||
I (338) example: MOSI: 23 MISO: 19 SCLK: 18 CS: 5
|
||||
I (348) spi_flash: detected chip: generic
|
||||
I (348) spi_flash: flash io: dio
|
||||
I (348) example: Initialized external Flash, size=4096 KB, ID=0xef4016
|
||||
I (358) example: Adding external Flash as a partition, label="storage", size=4096 KB
|
||||
I (368) example: Mounting FAT filesystem
|
||||
I (378) example: FAT FS: 4024 kB total, 4020 kB free
|
||||
I (378) example: Opening file
|
||||
I (958) example: File written
|
||||
I (958) example: Reading file
|
||||
I (958) example: Read from file: 'Written using ESP-IDF v4.0-dev-1301-g0a1160468'
|
||||
```
|
||||
@@ -0,0 +1,19 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import ttfw_idf
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_ExtFlash')
|
||||
def test_examples_storage_ext_flash_fatfs(env, extra_data):
|
||||
dut = env.get_dut('ext_flash_fatfs', 'examples/storage/ext_flash_fatfs', dut_class=ttfw_idf.ESP32DUT)
|
||||
dut.start_app()
|
||||
|
||||
dut.expect('Initialized external Flash')
|
||||
dut.expect('partition \'nvs\'')
|
||||
dut.expect('partition \'storage\'')
|
||||
dut.expect('File written')
|
||||
dut.expect('Read from file: \'Written using ESP-IDF')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_examples_storage_ext_flash_fatfs()
|
||||
@@ -0,0 +1 @@
|
||||
idf_component_register(SRCS "ext_flash_fatfs_example_main.c")
|
||||
@@ -0,0 +1,4 @@
|
||||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
@@ -0,0 +1,192 @@
|
||||
/* Example of FAT filesystem on external Flash.
|
||||
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.
|
||||
|
||||
This sample shows how to store files inside a FAT filesystem.
|
||||
FAT filesystem is stored in a partition inside SPI flash, using the
|
||||
flash wear levelling library.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "esp_flash.h"
|
||||
#include "esp_flash_spi_init.h"
|
||||
#include "esp_partition.h"
|
||||
#include "esp_vfs.h"
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "esp_system.h"
|
||||
|
||||
static const char *TAG = "example";
|
||||
|
||||
// Handle of the wear levelling library instance
|
||||
static wl_handle_t s_wl_handle = WL_INVALID_HANDLE;
|
||||
|
||||
// Mount path for the partition
|
||||
const char *base_path = "/extflash";
|
||||
|
||||
static esp_flash_t* example_init_ext_flash(void);
|
||||
static const esp_partition_t* example_add_partition(esp_flash_t* ext_flash, const char* partition_label);
|
||||
static void example_list_data_partitions(void);
|
||||
static bool example_mount_fatfs(const char* partition_label);
|
||||
static void example_get_fatfs_usage(size_t* out_total_bytes, size_t* out_free_bytes);
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
// Set up SPI bus and initialize the external SPI Flash chip
|
||||
esp_flash_t* flash = example_init_ext_flash();
|
||||
if (flash == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add the entire external flash chip as a partition
|
||||
const char *partition_label = "storage";
|
||||
example_add_partition(flash, partition_label);
|
||||
|
||||
// List the available partitions
|
||||
example_list_data_partitions();
|
||||
|
||||
// Initialize FAT FS in the partition
|
||||
if (!example_mount_fatfs(partition_label)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Print FAT FS size information
|
||||
size_t bytes_total, bytes_free;
|
||||
example_get_fatfs_usage(&bytes_total, &bytes_free);
|
||||
ESP_LOGI(TAG, "FAT FS: %d kB total, %d kB free", bytes_total / 1024, bytes_free / 1024);
|
||||
|
||||
// Create a file in FAT FS
|
||||
ESP_LOGI(TAG, "Opening file");
|
||||
FILE *f = fopen("/extflash/hello.txt", "wb");
|
||||
if (f == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to open file for writing");
|
||||
return;
|
||||
}
|
||||
fprintf(f, "Written using ESP-IDF %s\n", esp_get_idf_version());
|
||||
fclose(f);
|
||||
ESP_LOGI(TAG, "File written");
|
||||
|
||||
// Open file for reading
|
||||
ESP_LOGI(TAG, "Reading file");
|
||||
f = fopen("/extflash/hello.txt", "rb");
|
||||
if (f == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to open file for reading");
|
||||
return;
|
||||
}
|
||||
char line[128];
|
||||
fgets(line, sizeof(line), f);
|
||||
fclose(f);
|
||||
// strip newline
|
||||
char *pos = strchr(line, '\n');
|
||||
if (pos) {
|
||||
*pos = '\0';
|
||||
}
|
||||
ESP_LOGI(TAG, "Read from file: '%s'", line);
|
||||
}
|
||||
|
||||
static esp_flash_t* example_init_ext_flash(void)
|
||||
{
|
||||
const spi_bus_config_t bus_config = {
|
||||
.mosi_io_num = VSPI_IOMUX_PIN_NUM_MOSI,
|
||||
.miso_io_num = VSPI_IOMUX_PIN_NUM_MISO,
|
||||
.sclk_io_num = VSPI_IOMUX_PIN_NUM_CLK,
|
||||
.quadwp_io_num = -1,
|
||||
.quadhd_io_num = -1,
|
||||
};
|
||||
|
||||
const esp_flash_spi_device_config_t device_config = {
|
||||
.host_id = VSPI_HOST,
|
||||
.cs_id = 0,
|
||||
.cs_io_num = VSPI_IOMUX_PIN_NUM_CS,
|
||||
.io_mode = SPI_FLASH_DIO,
|
||||
.speed = ESP_FLASH_40MHZ
|
||||
};
|
||||
|
||||
ESP_LOGI(TAG, "Initializing external SPI Flash");
|
||||
ESP_LOGI(TAG, "Pin assignments:");
|
||||
ESP_LOGI(TAG, "MOSI: %2d MISO: %2d SCLK: %2d CS: %2d",
|
||||
bus_config.mosi_io_num, bus_config.miso_io_num,
|
||||
bus_config.sclk_io_num, device_config.cs_io_num
|
||||
);
|
||||
|
||||
// Initialize the SPI bus
|
||||
ESP_ERROR_CHECK(spi_bus_initialize(VSPI_HOST, &bus_config, 1));
|
||||
|
||||
// Add device to the SPI bus
|
||||
esp_flash_t* ext_flash;
|
||||
ESP_ERROR_CHECK(spi_bus_add_flash_device(&ext_flash, &device_config));
|
||||
|
||||
// Probe the Flash chip and initialize it
|
||||
esp_err_t err = esp_flash_init(ext_flash);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to initialize external Flash: %s (0x%x)", esp_err_to_name(err), err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Print out the ID and size
|
||||
uint32_t id;
|
||||
ESP_ERROR_CHECK(esp_flash_read_id(ext_flash, &id));
|
||||
ESP_LOGI(TAG, "Initialized external Flash, size=%d KB, ID=0x%x", ext_flash->size / 1024, id);
|
||||
|
||||
return ext_flash;
|
||||
}
|
||||
|
||||
static const esp_partition_t* example_add_partition(esp_flash_t* ext_flash, const char* partition_label)
|
||||
{
|
||||
ESP_LOGI(TAG, "Adding external Flash as a partition, label=\"%s\", size=%d KB", partition_label, ext_flash->size / 1024);
|
||||
const esp_partition_t* fat_partition;
|
||||
ESP_ERROR_CHECK(esp_partition_register_external(ext_flash, 0, ext_flash->size, partition_label, ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, &fat_partition));
|
||||
return fat_partition;
|
||||
}
|
||||
|
||||
static void example_list_data_partitions(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Listing data partitions:");
|
||||
esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, NULL);
|
||||
|
||||
for (; it != NULL; it = esp_partition_next(it)) {
|
||||
const esp_partition_t *part = esp_partition_get(it);
|
||||
ESP_LOGI(TAG, "- partition '%s', subtype %d, offset 0x%x, size %d kB",
|
||||
part->label, part->subtype, part->address, part->size / 1024);
|
||||
}
|
||||
|
||||
esp_partition_iterator_release(it);
|
||||
}
|
||||
|
||||
static bool example_mount_fatfs(const char* partition_label)
|
||||
{
|
||||
ESP_LOGI(TAG, "Mounting FAT filesystem");
|
||||
const esp_vfs_fat_mount_config_t mount_config = {
|
||||
.max_files = 4,
|
||||
.format_if_mount_failed = true,
|
||||
.allocation_unit_size = CONFIG_WL_SECTOR_SIZE
|
||||
};
|
||||
esp_err_t err = esp_vfs_fat_spiflash_mount(base_path, partition_label, &mount_config, &s_wl_handle);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to mount FATFS (%s)", esp_err_to_name(err));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void example_get_fatfs_usage(size_t* out_total_bytes, size_t* out_free_bytes)
|
||||
{
|
||||
FATFS *fs;
|
||||
size_t free_clusters;
|
||||
int res = f_getfree("0:", &free_clusters, &fs);
|
||||
assert(res == FR_OK);
|
||||
size_t total_sectors = (fs->n_fatent - 2) * fs->csize;
|
||||
size_t free_sectors = free_clusters * fs->csize;
|
||||
|
||||
// assuming the total size is < 4GiB, should be true for SPI Flash
|
||||
if (out_total_bytes != NULL) {
|
||||
*out_total_bytes = total_sectors * fs->ssize;
|
||||
}
|
||||
if (out_free_bytes != NULL) {
|
||||
*out_free_bytes = free_sectors * fs->ssize;
|
||||
}
|
||||
}
|
||||
@@ -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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(nvs-rw-blob)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := nvs-rw-blob
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
# Non-Volatile Storage (NVS) Read and Write Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
This example demonstrates how to read and write a single integer value and a blob (binary large object) using NVS to preserve them between ESP32 module restarts.
|
||||
|
||||
* value - tracks number of ESP32 module soft and hard restarts.
|
||||
* blob - contains a table with module run times. The table is read from NVS to dynamically allocated RAM. New run time is added to the table on each manually triggered soft restart and written back to NVS. Triggering is done by pulling down GPIO0.
|
||||
|
||||
Example also shows how to implement diagnostics if read / write operation was successful.
|
||||
|
||||
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.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware required
|
||||
|
||||
This example can be run on most common development boards which have an active button connected to GPIO0. On most boards, this button is labeled as "Boot". When pressed, the button connects GPIO0 to ground.
|
||||
|
||||
### Build and flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
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
|
||||
|
||||
First run:
|
||||
```
|
||||
Restart counter = 0
|
||||
Run time:
|
||||
Nothing saved yet!
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "nvs_blob_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,5 @@
|
||||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
/* 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 "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"
|
||||
|
||||
/* 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 = %d\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: %d\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_pad_select_gpio(GPIO_NUM_0);
|
||||
gpio_set_direction(GPIO_NUM_0, GPIO_MODE_DEF_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(GPIO_NUM_0) == 0) {
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
if(gpio_get_level(GPIO_NUM_0) == 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
from tiny_test_fw import Utility
|
||||
import random
|
||||
import re
|
||||
import time
|
||||
import ttfw_idf
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_GENERIC')
|
||||
def test_examples_nvs_rw_blob(env, extra_data):
|
||||
|
||||
dut = env.get_dut('nvs_rw_blob', 'examples/storage/nvs_rw_blob')
|
||||
dut.start_app()
|
||||
|
||||
def expect_start_msg(index):
|
||||
dut.expect_all('Restart counter = {}'.format(index),
|
||||
'Run time:',
|
||||
timeout=10)
|
||||
|
||||
expect_start_msg(0)
|
||||
dut.expect('Nothing saved yet!', timeout=5)
|
||||
|
||||
nvs_store = []
|
||||
for i in range(1, 10):
|
||||
time.sleep(random.uniform(0.1, 2)) # in order to randomize the runtimes stored in NVS
|
||||
try:
|
||||
# Pulling GPIO0 low using DTR
|
||||
dut.port_inst.setDTR(True)
|
||||
dut.expect('Restarting...', timeout=5) # the application waits for a second
|
||||
finally:
|
||||
dut.port_inst.setDTR(False)
|
||||
|
||||
expect_start_msg(i)
|
||||
|
||||
dut.expect_all(*nvs_store, timeout=10)
|
||||
Utility.console_log('Received: {}'.format(', '.join(nvs_store)))
|
||||
|
||||
new_runtime = dut.expect(re.compile(r'{}: (\d+)'.format(i)), timeout=10)[0]
|
||||
nvs_store.append('{}: {}'.format(i, new_runtime))
|
||||
Utility.console_log('loop {} has finished with runtime {}'.format(i, new_runtime))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_examples_nvs_rw_blob()
|
||||
@@ -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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(nvs-rw-value)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := nvs-rw-value
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
# Non-Volatile Storage (NVS) Read and Write Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
This example demonstrates how to read and write a single integer value using NVS.
|
||||
|
||||
In this example, value which is saved holds the number of ESP32 module restarts. Since it is written to NVS, the value is preserved between restarts.
|
||||
|
||||
Example also shows how to check if read / write operation was successful, or certain value is not initialized in NVS. Diagnostic is provided in plain text to help track program flow and capture any issues on the way.
|
||||
|
||||
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).
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware required
|
||||
|
||||
This example does not require any special hardware, and can be run on any common development board.
|
||||
|
||||
### Build and flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
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
|
||||
|
||||
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.
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "nvs_value_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,5 @@
|
||||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/* 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 "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 = %d\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();
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
from tiny_test_fw import Utility
|
||||
import ttfw_idf
|
||||
|
||||
try:
|
||||
from itertools import izip_longest as zip_longest
|
||||
except ImportError:
|
||||
# Python 3
|
||||
from itertools import zip_longest
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_GENERIC')
|
||||
def test_examples_nvs_rw_value(env, extra_data):
|
||||
|
||||
dut = env.get_dut('nvs_rw_value', 'examples/storage/nvs_rw_value')
|
||||
dut.start_app()
|
||||
|
||||
for i, counter_state in zip_longest(range(4), ('The value is not initialized yet!', ), fillvalue='Done'):
|
||||
dut.expect_all('Opening Non-Volatile Storage (NVS) handle... Done',
|
||||
'Reading restart counter from NVS ... {}'.format(counter_state),
|
||||
'Restart counter = {}'.format(i) if i > 0 else '',
|
||||
'Updating restart counter in NVS ... Done',
|
||||
'Committing updates in NVS ... Done',
|
||||
'Restarting in 10 seconds...',
|
||||
timeout=20)
|
||||
Utility.console_log('loop {} has finished'.format(i))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_examples_nvs_rw_value()
|
||||
@@ -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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(nvs-rw-value-cxx)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := nvs-rw-value-cxx
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
# Non-Volatile Storage (NVS) C++ Read and Write Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
This example demonstrates how to read and write a single integer value using NVS.
|
||||
It is essentially the same as the nvs_rw_value example. The only difference is that it uses the C++ NVS handle API.
|
||||
Please see [nvs_rw_value README](../nvs_rw_value/README.md) for more details about this example.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware required
|
||||
|
||||
This example does not require any special hardware, and can be run on any common development board.
|
||||
|
||||
### Build and flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
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
|
||||
|
||||
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.
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "nvs_value_example_main.cpp"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,5 @@
|
||||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/* 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 "nvs_flash.h"
|
||||
#include "nvs.h"
|
||||
#include "nvs_handle.hpp"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_system.h"
|
||||
|
||||
extern "C" 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... ");
|
||||
esp_err_t result;
|
||||
// Handle will automatically close when going out of scope or when it's reset.
|
||||
std::shared_ptr<nvs::NVSHandle> handle = nvs::open_nvs_handle("storage", NVS_READWRITE, &result);
|
||||
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 = handle->get_item("restart_counter", restart_counter);
|
||||
switch (err) {
|
||||
case ESP_OK:
|
||||
printf("Done\n");
|
||||
printf("Restart counter = %d\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 = handle->set_item("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 = handle->commit();
|
||||
printf((err != ESP_OK) ? "Failed!\n" : "Done\n");
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
from tiny_test_fw import Utility
|
||||
import ttfw_idf
|
||||
|
||||
try:
|
||||
from itertools import izip_longest as zip_longest
|
||||
except ImportError:
|
||||
# Python 3
|
||||
from itertools import zip_longest
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_GENERIC')
|
||||
def test_examples_nvs_rw_value_cxx(env, extra_data):
|
||||
|
||||
dut = env.get_dut('nvs_rw_value_cxx', 'examples/storage/nvs_rw_value_cxx')
|
||||
dut.start_app()
|
||||
|
||||
for i, counter_state in zip_longest(range(4), ('The value is not initialized yet!', ), fillvalue='Done'):
|
||||
dut.expect_all('Opening Non-Volatile Storage (NVS) handle... Done',
|
||||
'Reading restart counter from NVS ... {}'.format(counter_state),
|
||||
'Restart counter = {}'.format(i) if i > 0 else '',
|
||||
'Updating restart counter in NVS ... Done',
|
||||
'Committing updates in NVS ... Done',
|
||||
'Restarting in 10 seconds...',
|
||||
timeout=20)
|
||||
Utility.console_log('loop {} has finished'.format(i))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_examples_nvs_rw_value_cxx()
|
||||
@@ -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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(partition_find)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := partition_find
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
# Finding Partitions Example
|
||||
|
||||
This example demonstrates how to use the partition API functions `esp_partition_find` and `esp_partition_find_first` to search the device partition table.
|
||||
These functions return the matching partitions given a set of constraints. By constraints we simply mean
|
||||
properties that returned results should match - notably partition type, subtype and label/name. In the case of `esp_partition_find_first`, the first matching instance is returned;
|
||||
for `esp_partition_find`, an iterator is returned iterating over matching instances.
|
||||
|
||||
# Example Flow
|
||||
|
||||
The example uses a [custom partition table](./partitions_example.csv). The first part uses `esp_partition_find_first` to search for partition instances
|
||||
using two constraints - type and subtype. However, the partition table contains partitions with same type/subtype combination. In order to differentiate these partitions,
|
||||
a third constraint - the label, is specified.
|
||||
|
||||
The second part shows how to iterate over partitions that match certain constraints, manually checking the properties of each iterated partition.
|
||||
The iterator is obtained using `esp_partition_find` and is released after its use to avoid memory leaks.
|
||||
|
||||
### Output
|
||||
|
||||
```
|
||||
I (310) example: Printing partition table csv file contents for reference...
|
||||
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
|
||||
nvs, data, nvs, 0x9000, 0x6000,
|
||||
phy_init, data, phy, 0xf000, 0x1000,
|
||||
factory, app, factory, 0x10000, 1M,
|
||||
storage1, data, fat, , 0x40000,
|
||||
storage2, data, fat, , 0x40000,
|
||||
|
||||
I (350) example: ----------------Find partitions---------------
|
||||
I (350) example: Find partition with type ESP_PARTITION_TYPE_DATA, subtype ESP_PARTITION_SUBTYPE_DATA_NVS, label NULL (unspecified)...
|
||||
I (370) example: found partition 'nvs' at offset 0x9000 with size 0x6000
|
||||
I (370) example: Find partition with type ESP_PARTITION_TYPE_DATA, subtype ESP_PARTITION_SUBTYPE_DATA_PHY, label NULL (unspecified)...
|
||||
I (390) example: found partition 'phy_init' at offset 0xf000 with size 0x1000
|
||||
I (390) example: Find partition with type ESP_PARTITION_TYPE_APP, subtype ESP_PARTITION_SUBTYPE_APP_FACTORY, label NULL (unspecified)...
|
||||
I (410) example: found partition 'factory' at offset 0x10000 with size 0x100000
|
||||
I (410) example: Find partition with type ESP_PARTITION_TYPE_DATA, subtype ESP_PARTITION_SUBTYPE_DATA_FAT, label NULL (unspecified)...
|
||||
I (430) example: found partition 'storage1' at offset 0x110000 with size 0x40000
|
||||
I (440) example: Find second FAT partition by specifying the label
|
||||
I (440) example: Find partition with type ESP_PARTITION_TYPE_DATA, subtype ESP_PARTITION_SUBTYPE_DATA_FAT, label storage2...
|
||||
I (450) example: found partition 'storage2' at offset 0x150000 with size 0x40000
|
||||
I (460) example: ----------------Iterate through partitions---------------
|
||||
I (470) example: Iterating through app partitions...
|
||||
I (480) example: found partition 'factory' at offset 0x10000 with size 0x100000
|
||||
I (480) example: Iterating through data partitions...
|
||||
I (490) example: found partition 'nvs' at offset 0x9000 with size 0x6000
|
||||
I (500) example: found partition 'phy_init' at offset 0xf000 with size 0x1000
|
||||
I (500) example: found partition 'storage1' at offset 0x110000 with size 0x40000
|
||||
I (510) example: found partition 'storage2' at offset 0x150000 with size 0x40000
|
||||
I (520) example: Example end
|
||||
```
|
||||
|
||||
# Others
|
||||
|
||||
Detailed functional description of partition API is provided in [documentation](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/storage/spi_flash.html).
|
||||
|
||||
See the README.md file in the upper level 'examples' directory for more information about examples.
|
||||
@@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "main.c"
|
||||
INCLUDE_DIRS "."
|
||||
EMBED_TXTFILES ../partitions_example.csv)
|
||||
@@ -0,0 +1,10 @@
|
||||
#
|
||||
# Main component makefile.
|
||||
#
|
||||
# This Makefile can be left empty. By default, it will take the sources in the
|
||||
# src/ directory, compile them and link them into lib(subdirectory_name).a
|
||||
# in the build directory. This behaviour is entirely configurable,
|
||||
# please read the ESP-IDF documents if you need to do this.
|
||||
#
|
||||
|
||||
COMPONENT_EMBED_TXTFILES := ../partitions_example.csv
|
||||
@@ -0,0 +1,120 @@
|
||||
/* Finding Partitions Example
|
||||
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 <string.h>
|
||||
#include <assert.h>
|
||||
#include "esp_partition.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
static const char *TAG = "example";
|
||||
|
||||
// Get the string name of type enum values used in this example
|
||||
static const char* get_type_str(esp_partition_type_t type)
|
||||
{
|
||||
switch(type) {
|
||||
case ESP_PARTITION_TYPE_APP:
|
||||
return "ESP_PARTITION_TYPE_APP";
|
||||
case ESP_PARTITION_TYPE_DATA:
|
||||
return "ESP_PARTITION_TYPE_DATA";
|
||||
default:
|
||||
return "UNKNOWN_PARTITION_TYPE"; // type not used in this example
|
||||
}
|
||||
}
|
||||
|
||||
// Get the string name of subtype enum values used in this example
|
||||
static const char* get_subtype_str(esp_partition_subtype_t subtype)
|
||||
{
|
||||
switch(subtype) {
|
||||
case ESP_PARTITION_SUBTYPE_DATA_NVS:
|
||||
return "ESP_PARTITION_SUBTYPE_DATA_NVS";
|
||||
case ESP_PARTITION_SUBTYPE_DATA_PHY:
|
||||
return "ESP_PARTITION_SUBTYPE_DATA_PHY";
|
||||
case ESP_PARTITION_SUBTYPE_APP_FACTORY:
|
||||
return "ESP_PARTITION_SUBTYPE_APP_FACTORY";
|
||||
case ESP_PARTITION_SUBTYPE_DATA_FAT:
|
||||
return "ESP_PARTITION_SUBTYPE_DATA_FAT";
|
||||
default:
|
||||
return "UNKNOWN_PARTITION_SUBTYPE"; // subtype not used in this example
|
||||
}
|
||||
}
|
||||
|
||||
// Find the partition using given parameters
|
||||
static void find_partition(esp_partition_type_t type, esp_partition_subtype_t subtype, const char* name)
|
||||
{
|
||||
ESP_LOGI(TAG, "Find partition with type %s, subtype %s, label %s...", get_type_str(type), get_subtype_str(subtype),
|
||||
name == NULL ? "NULL (unspecified)" : name);
|
||||
const esp_partition_t * part = esp_partition_find_first(type, subtype, name);
|
||||
ESP_LOGI(TAG, "\tfound partition '%s' at offset 0x%x with size 0x%x", part->label, part->address, part->size);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
/*
|
||||
* This example uses the partition table from ../partitions_example.csv. For reference, its contents are as follows:
|
||||
*
|
||||
* nvs, data, nvs, 0x9000, 0x6000,
|
||||
* phy_init, data, phy, 0xf000, 0x1000,
|
||||
* factory, app, factory, 0x10000, 1M,
|
||||
* storage1, data, fat, , 0x40000,
|
||||
* storage2, data, fat, , 0x40000,
|
||||
*
|
||||
* Display the partition table to the user for reference.
|
||||
*/
|
||||
extern const char csv_start[] asm("_binary_partitions_example_csv_start");
|
||||
extern const char csv_end[] asm("_binary_partitions_example_csv_end");
|
||||
|
||||
ESP_LOGI(TAG, "Printing partition table csv file contents for reference...\n\n%.*s", csv_end - csv_start + 1, csv_start);
|
||||
|
||||
/* First Part - Finding partitions using esp_partition_find_first. */
|
||||
|
||||
ESP_LOGI(TAG, "----------------Find partitions---------------");
|
||||
|
||||
// Find partitions using esp_partition_find_first(). This returns the first partition matching the passed constraints.
|
||||
find_partition(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, NULL);
|
||||
find_partition(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_PHY, NULL);
|
||||
find_partition(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL);
|
||||
|
||||
find_partition(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, NULL);
|
||||
|
||||
ESP_LOGI(TAG, "Find second FAT partition by specifying the label");
|
||||
// In case of multiple matches, `esp_partition_find_first` returns the first match.
|
||||
find_partition(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, "storage2");
|
||||
|
||||
/* Second Part - Iterating over partitions */
|
||||
|
||||
ESP_LOGI(TAG, "----------------Iterate through partitions---------------");
|
||||
|
||||
esp_partition_iterator_t it;
|
||||
|
||||
ESP_LOGI(TAG, "Iterating through app partitions...");
|
||||
it = esp_partition_find(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_ANY, NULL);
|
||||
|
||||
// Loop through all matching partitions, in this case, all with the type 'data' until partition with desired
|
||||
// label is found. Verify if its the same instance as the one found before.
|
||||
for (; it != NULL; it = esp_partition_next(it)) {
|
||||
const esp_partition_t *part = esp_partition_get(it);
|
||||
ESP_LOGI(TAG, "\tfound partition '%s' at offset 0x%x with size 0x%x", part->label, part->address, part->size);
|
||||
}
|
||||
// Release the partition iterator to release memory allocated for it
|
||||
esp_partition_iterator_release(it);
|
||||
|
||||
ESP_LOGI(TAG, "Iterating through data partitions...");
|
||||
it = esp_partition_find(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, NULL);
|
||||
|
||||
// Loop through all matching partitions, in this case, all with the type 'data' until partition with desired
|
||||
// label is found. Verify if its the same instance as the one found before.
|
||||
for (; it != NULL; it = esp_partition_next(it)) {
|
||||
const esp_partition_t *part = esp_partition_get(it);
|
||||
ESP_LOGI(TAG, "\tfound partition '%s' at offset 0x%x with size 0x%x", part->label, part->address, part->size);
|
||||
}
|
||||
|
||||
// Release the partition iterator to release memory allocated for it
|
||||
esp_partition_iterator_release(it);
|
||||
|
||||
ESP_LOGI(TAG, "Example end");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import ttfw_idf
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_GENERIC')
|
||||
def test_examples_partition_find(env, extra_data):
|
||||
|
||||
dut = env.get_dut('partition_find', 'examples/storage/partition_api/partition_find')
|
||||
dut.start_app()
|
||||
|
||||
def expect_partition(name, offset, size):
|
||||
dut.expect("found partition '{}' at offset {:#x} with size {:#x}".format(name, offset, size), timeout=5)
|
||||
|
||||
def expect_find_partition(_type, subtype, label, name, offset, size):
|
||||
dut.expect('Find partition with type {}, subtype {}, label {}'.format(_type, subtype, label), timeout=5)
|
||||
expect_partition(name, offset, size)
|
||||
|
||||
dut.expect('----------------Find partitions---------------', timeout=20)
|
||||
expect_find_partition('ESP_PARTITION_TYPE_DATA', 'ESP_PARTITION_SUBTYPE_DATA_NVS', 'NULL',
|
||||
'nvs', 0x9000, 0x6000)
|
||||
expect_find_partition('ESP_PARTITION_TYPE_DATA', 'ESP_PARTITION_SUBTYPE_DATA_PHY', 'NULL',
|
||||
'phy_init', 0xf000, 0x1000)
|
||||
expect_find_partition('ESP_PARTITION_TYPE_APP', 'ESP_PARTITION_SUBTYPE_APP_FACTORY', 'NULL',
|
||||
'factory', 0x10000, 0x100000)
|
||||
expect_find_partition('ESP_PARTITION_TYPE_DATA', 'ESP_PARTITION_SUBTYPE_DATA_FAT', 'NULL',
|
||||
'storage1', 0x110000, 0x40000)
|
||||
|
||||
dut.expect('Find second FAT partition by specifying the label', timeout=5)
|
||||
expect_find_partition('ESP_PARTITION_TYPE_DATA', 'ESP_PARTITION_SUBTYPE_DATA_FAT', 'storage2',
|
||||
'storage2', 0x150000, 0x40000)
|
||||
|
||||
dut.expect_all('----------------Iterate through partitions---------------',
|
||||
'Iterating through app partitions...', timeout=5)
|
||||
expect_partition('factory', 0x10000, 0x100000)
|
||||
|
||||
dut.expect('Iterating through data partitions...', timeout=5)
|
||||
expect_partition('nvs', 0x9000, 0x6000)
|
||||
expect_partition('phy_init', 0xf000, 0x1000)
|
||||
expect_partition('storage1', 0x110000, 0x40000)
|
||||
expect_partition('storage2', 0x150000, 0x40000)
|
||||
|
||||
dut.expect('Example end', timeout=5)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_examples_partition_find()
|
||||
@@ -0,0 +1,7 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
|
||||
nvs, data, nvs, 0x9000, 0x6000,
|
||||
phy_init, data, phy, 0xf000, 0x1000,
|
||||
factory, app, factory, 0x10000, 1M,
|
||||
storage1, data, fat, , 0x40000,
|
||||
storage2, data, fat, , 0x40000,
|
||||
|
@@ -0,0 +1,3 @@
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv"
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv"
|
||||
@@ -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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(partition_mmap)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := partition_mmap
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# Partition Memory Map Example
|
||||
|
||||
This example demonstrates how to use `esp_partition_mmap` to configure MMU and map a partition into memory address space for read operations.
|
||||
|
||||
# Example Flow
|
||||
|
||||
The example uses a [custom partition table](./partitions_example.csv), with a data partition `storage` used for demonstration. Before mapping this partition to memory,
|
||||
data is written to the partition used for verification.
|
||||
|
||||
The partition API function `esp_partition_mmap` is used to get a pointer to the mapped memory region and a handle to the mapping. The pointer is used to transparently read back the
|
||||
verification data written previously. Once the data written and read are verified to be the same, the function `spi_flash_munmap` is used to release the mapping.
|
||||
|
||||
### Output
|
||||
```
|
||||
I (309) example: Written sample data to partition: ESP-IDF Partition Memory Map Example
|
||||
I (309) example: Mapped partition to data memory address 0x3f410000
|
||||
I (319) example: Read sample data from partition using mapped memory: ESP-IDF Partition Memory Map Example
|
||||
I (329) example: Data matches
|
||||
I (329) example: Unmapped partition from data memory
|
||||
I (339) example: Example end
|
||||
```
|
||||
|
||||
# Others
|
||||
|
||||
Detailed functional description of partition API is provided in [documentation](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/storage/spi_flash.html).
|
||||
|
||||
See the README.md file in the upper level 'examples' directory for more information about examples.
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,8 @@
|
||||
#
|
||||
# Main component makefile.
|
||||
#
|
||||
# This Makefile can be left empty. By default, it will take the sources in the
|
||||
# src/ directory, compile them and link them into lib(subdirectory_name).a
|
||||
# in the build directory. This behaviour is entirely configurable,
|
||||
# please read the ESP-IDF documents if you need to do this.
|
||||
#
|
||||
@@ -0,0 +1,58 @@
|
||||
/* Finding Partitions Example
|
||||
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 <string.h>
|
||||
#include <assert.h>
|
||||
#include "esp_partition.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
static const char *TAG = "example";
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
/*
|
||||
* This example uses the partition table from ../partitions_example.csv. For reference, its contents are as follows:
|
||||
*
|
||||
* nvs, data, nvs, 0x9000, 0x6000,
|
||||
* phy_init, data, phy, 0xf000, 0x1000,
|
||||
* factory, app, factory, 0x10000, 1M,
|
||||
* storage, data, , , 0x40000,
|
||||
*/
|
||||
|
||||
// Find the partition map in the partition table
|
||||
const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage");
|
||||
assert(partition != NULL);
|
||||
|
||||
static const char store_data[] = "ESP-IDF Partition Memory Map Example";
|
||||
|
||||
// Prepare data to be read later using the mapped address
|
||||
ESP_ERROR_CHECK(esp_partition_erase_range(partition, 0, partition->size));
|
||||
ESP_ERROR_CHECK(esp_partition_write(partition, 0, store_data, sizeof(store_data)));
|
||||
ESP_LOGI(TAG, "Written sample data to partition: %s", store_data);
|
||||
|
||||
const void *map_ptr;
|
||||
spi_flash_mmap_handle_t map_handle;
|
||||
|
||||
// Map the partition to data memory
|
||||
ESP_ERROR_CHECK(esp_partition_mmap(partition, 0, partition->size, SPI_FLASH_MMAP_DATA, &map_ptr, &map_handle));
|
||||
ESP_LOGI(TAG, "Mapped partition to data memory address %p", map_ptr);
|
||||
|
||||
// Read back the written verification data using the mapped memory pointer
|
||||
char read_data[sizeof(store_data)];
|
||||
memcpy(read_data, map_ptr, sizeof(read_data));
|
||||
ESP_LOGI(TAG, "Read sample data from partition using mapped memory: %s", (char*) read_data);
|
||||
|
||||
assert(strcmp(store_data, read_data) == 0);
|
||||
ESP_LOGI(TAG, "Data matches");
|
||||
|
||||
// Unmap mapped memory
|
||||
spi_flash_munmap(map_handle);
|
||||
ESP_LOGI(TAG, "Unmapped partition from data memory");
|
||||
|
||||
ESP_LOGI(TAG, "Example end");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import re
|
||||
import ttfw_idf
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_GENERIC')
|
||||
def test_examples_partition_mmap(env, extra_data):
|
||||
|
||||
dut = env.get_dut('partition_mmap', 'examples/storage/partition_api/partition_mmap')
|
||||
dut.start_app()
|
||||
|
||||
# ESP_ERROR_CHECK or assert will cause abort on error and "Example end" won't be received
|
||||
dut.expect_all('Written sample data to partition: ESP-IDF Partition Memory Map Example',
|
||||
re.compile(r'Mapped partition to data memory address \S+'),
|
||||
'Read sample data from partition using mapped memory: ESP-IDF Partition Memory Map Example',
|
||||
'Data matches',
|
||||
'Unmapped partition from data memory',
|
||||
'Example end',
|
||||
timeout=20)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_examples_partition_mmap()
|
||||
@@ -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
|
||||
nvs, data, nvs, 0x9000, 0x6000,
|
||||
phy_init, data, phy, 0xf000, 0x1000,
|
||||
factory, app, factory, 0x10000, 1M,
|
||||
storage, data, , , 0x40000,
|
||||
|
@@ -0,0 +1,3 @@
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv"
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv"
|
||||
@@ -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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(partition_ops)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := partition_ops
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# Partition Read, Write, Erase Example
|
||||
|
||||
This example demonstrates how to perform partition read, write and erase operations using API functions `esp_partition_read`, `esp_partition_write` and `esp_partition_erase`.
|
||||
|
||||
# Example Flow
|
||||
|
||||
The example uses a [custom partition table](./partitions_example.csv), with a data partition `storage` used as the demo partition. For the most part the example code is well-commented so users should be able to follow along the code easily. Nevertheless, this section provides an overview of the code flow.
|
||||
|
||||
The partition table is searched for the `storage` partition. Once found, the entire partition is erased using `esp_partition_erase_range`. Sample data is written using `esp_partition_write`
|
||||
and read back using `esp_partition_read`, verifying the read and written data match. The partition is erased once again using `esp_partition_erase_range`, limited to the sector the sample data was written to.
|
||||
|
||||
### Output
|
||||
|
||||
```
|
||||
I (588) example: Written data: ESP-IDF Partition Operations Example (Read, Erase, Write)
|
||||
I (588) example: Read data: ESP-IDF Partition Operations Example (Read, Erase, Write)
|
||||
I (638) example: Erased data
|
||||
I (638) example: Example end
|
||||
```
|
||||
|
||||
# Others
|
||||
|
||||
Detailed functional description of partition API is provided in [documentation](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/storage/spi_flash.html).
|
||||
|
||||
See the README.md file in the upper level 'examples' directory for more information about examples.
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,8 @@
|
||||
#
|
||||
# Main component makefile.
|
||||
#
|
||||
# This Makefile can be left empty. By default, it will take the sources in the
|
||||
# src/ directory, compile them and link them into lib(subdirectory_name).a
|
||||
# in the build directory. This behaviour is entirely configurable,
|
||||
# please read the ESP-IDF documents if you need to do this.
|
||||
#
|
||||
@@ -0,0 +1,59 @@
|
||||
/* Finding Partitions Example
|
||||
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 <string.h>
|
||||
#include <assert.h>
|
||||
#include "esp_partition.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
static const char *TAG = "example";
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
/*
|
||||
* This example uses the partition table from ../partitions_example.csv. For reference, its contents are as follows:
|
||||
*
|
||||
* nvs, data, nvs, 0x9000, 0x6000,
|
||||
* phy_init, data, phy, 0xf000, 0x1000,
|
||||
* factory, app, factory, 0x10000, 1M,
|
||||
* storage, data, , , 0x40000,
|
||||
*/
|
||||
|
||||
// Find the partition map in the partition table
|
||||
const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage");
|
||||
assert(partition != NULL);
|
||||
|
||||
static char store_data[] = "ESP-IDF Partition Operations Example (Read, Erase, Write)";
|
||||
static char read_data[sizeof(store_data)];
|
||||
|
||||
// Erase entire partition
|
||||
memset(read_data, 0xFF, sizeof(read_data));
|
||||
ESP_ERROR_CHECK(esp_partition_erase_range(partition, 0, partition->size));
|
||||
|
||||
// Write the data, starting from the beginning of the partition
|
||||
ESP_ERROR_CHECK(esp_partition_write(partition, 0, store_data, sizeof(store_data)));
|
||||
ESP_LOGI(TAG, "Written data: %s", store_data);
|
||||
|
||||
// Read back the data, checking that read data and written data match
|
||||
ESP_ERROR_CHECK(esp_partition_read(partition, 0, read_data, sizeof(read_data)));
|
||||
assert(memcmp(store_data, read_data, sizeof(read_data)) == 0);
|
||||
ESP_LOGI(TAG, "Read data: %s", read_data);
|
||||
|
||||
// Erase the area where the data was written. Erase size shoud be a multiple of SPI_FLASH_SEC_SIZE
|
||||
// and also be SPI_FLASH_SEC_SIZE aligned
|
||||
ESP_ERROR_CHECK(esp_partition_erase_range(partition, 0, SPI_FLASH_SEC_SIZE));
|
||||
|
||||
// Read back the data (should all now be 0xFF's)
|
||||
memset(store_data, 0xFF, sizeof(read_data));
|
||||
ESP_ERROR_CHECK(esp_partition_read(partition, 0, read_data, sizeof(read_data)));
|
||||
assert(memcmp(store_data, read_data, sizeof(read_data)) == 0);
|
||||
|
||||
ESP_LOGI(TAG, "Erased data");
|
||||
|
||||
ESP_LOGI(TAG, "Example end");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import ttfw_idf
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_GENERIC')
|
||||
def test_examples_partition_ops(env, extra_data):
|
||||
|
||||
dut = env.get_dut('partition_ops', 'examples/storage/partition_api/partition_ops')
|
||||
dut.start_app()
|
||||
|
||||
# ESP_ERROR_CHECK or assert will cause abort on error and "Example end" won't be received
|
||||
dut.expect_all('Written data: ESP-IDF Partition Operations Example (Read, Erase, Write)',
|
||||
'Read data: ESP-IDF Partition Operations Example (Read, Erase, Write)',
|
||||
'Erased data',
|
||||
'Example end',
|
||||
timeout=20)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_examples_partition_ops()
|
||||
@@ -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
|
||||
nvs, data, nvs, 0x9000, 0x6000,
|
||||
phy_init, data, phy, 0xf000, 0x1000,
|
||||
factory, app, factory, 0x10000, 1M,
|
||||
storage, data, , , 0x40000,
|
||||
|
@@ -0,0 +1,3 @@
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv"
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv"
|
||||
@@ -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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(parttool)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := parttool
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
# Partitions Tool Example
|
||||
|
||||
This example demonstrates common operations the partitions tool [parttool.py](../../../components/partition_table/parttool.py) allows the user to perform:
|
||||
|
||||
- reading, writing and erasing partitions,
|
||||
- retrieving info on a certain partition,
|
||||
- dumping the entire partition table
|
||||
|
||||
Users taking a look at this example should focus on the contents of the Python script [parttool_example.py](parttool_example.py) or shell script [parttool_example.sh](parttool_example.sh). The scripts contain
|
||||
programmatic invocation of the tool's functions via the Python API and command-line interface, respectively. Note
|
||||
that on Windows, the shell script example requires a POSIX-compatible environment via MSYS2/Git Bash/WSL etc.
|
||||
|
||||
The example performs the operations mentioned above in a straightforward manner: it performs writes to partitions and then verifies correct content
|
||||
by reading it back. For partitions, contents are compared to the originally written file. For the partition table, contents are verified against the partition table CSV
|
||||
file. An erased partition's contents is compared to a generated blank file.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Before running either of the example scripts, it is necessary to build and flash the firmware using the usual means:
|
||||
|
||||
Make:
|
||||
```bash
|
||||
make build flash
|
||||
```
|
||||
|
||||
CMake:
|
||||
```bash
|
||||
idf.py build flash
|
||||
```
|
||||
|
||||
### Running [parttool_example.py](parttool_example.py)
|
||||
|
||||
The example can be executed by running the script [parttool_example.py](parttool_example.py) or [parttool_example.sh](parttool_example.sh).
|
||||
|
||||
Python script:
|
||||
```bash
|
||||
python parttool_example.py
|
||||
```
|
||||
|
||||
Shell script:
|
||||
```
|
||||
./parttool_example.sh
|
||||
```
|
||||
|
||||
The script searches for valid target devices connected to the host and performs the operations on the first one it finds. To perform the operations on a specific device, specify the port it is attached to during script invocation:
|
||||
|
||||
Python script:
|
||||
```bash
|
||||
python parttool_example.py --port /dev/ttyUSB2
|
||||
```
|
||||
|
||||
Shell script:
|
||||
```
|
||||
./parttool_example.sh /dev/ttyUSB2
|
||||
```
|
||||
|
||||
## Example output
|
||||
|
||||
Running the script produces the following output:
|
||||
|
||||
```
|
||||
Checking if device app binary matches built binary
|
||||
Found data partition at offset 0x110000 with size 0x10000
|
||||
Writing to data partition
|
||||
Reading data partition
|
||||
Erasing data partition
|
||||
Reading data partition
|
||||
|
||||
Partition tool operations performed successfully!
|
||||
```
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
from __future__ import print_function
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
import ttfw_idf
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_GENERIC')
|
||||
def test_examples_parttool(env, extra_data):
|
||||
dut = env.get_dut('parttool', 'examples/storage/parttool', dut_class=ttfw_idf.ESP32DUT)
|
||||
dut.start_app(False)
|
||||
|
||||
# Verify factory firmware
|
||||
dut.expect("Partitions Tool Example")
|
||||
dut.expect("Example end")
|
||||
|
||||
# Close connection to DUT
|
||||
dut.receive_thread.exit()
|
||||
dut.port_inst.close()
|
||||
|
||||
# Run the example python script
|
||||
script_path = os.path.join(os.getenv("IDF_PATH"), "examples", "storage", "parttool", "parttool_example.py")
|
||||
|
||||
binary_path = ""
|
||||
for flash_file in dut.app.flash_files:
|
||||
if "parttool.bin" in flash_file[1]:
|
||||
binary_path = flash_file[1]
|
||||
break
|
||||
|
||||
subprocess.check_call([sys.executable, script_path, "--binary", binary_path])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_examples_parttool()
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "parttool_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,4 @@
|
||||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
@@ -0,0 +1,22 @@
|
||||
/* Partitions Tool Example
|
||||
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 <string.h>
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
static const char *TAG = "example";
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Partitions Tool Example");
|
||||
ESP_LOGI(TAG, "Example end");
|
||||
}
|
||||
@@ -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
|
||||
nvs, data, nvs, 0x9000, 0x6000,
|
||||
phy_init, data, phy, 0xf000, 0x1000,
|
||||
factory, app, factory, 0x10000, 1M,
|
||||
storage, data, spiffs, , 0x10000,
|
||||
|
@@ -0,0 +1,106 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Demonstrates the use of parttool.py, a tool for performing partition level
|
||||
# operations.
|
||||
#
|
||||
# Copyright 2018 Espressif Systems (Shanghai) PTE LTD
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http:#www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
|
||||
PARTITION_TABLE_DIR = os.path.join("components", "partition_table", "")
|
||||
|
||||
|
||||
def assert_file_same(file1, file2, err):
|
||||
with open(file1, "rb") as f1:
|
||||
with open(file2, "rb") as f2:
|
||||
f1 = f1.read()
|
||||
f2 = f2.read()
|
||||
|
||||
if len(f1) < len(f2):
|
||||
f2 = f2[:len(f1)]
|
||||
else:
|
||||
f1 = f1[:len(f2)]
|
||||
|
||||
if not f1 == f2:
|
||||
raise Exception(err)
|
||||
|
||||
|
||||
def main():
|
||||
COMPONENTS_PATH = os.path.expandvars(os.path.join("$IDF_PATH", "components"))
|
||||
PARTTOOL_DIR = os.path.join(COMPONENTS_PATH, "partition_table")
|
||||
|
||||
sys.path.append(PARTTOOL_DIR)
|
||||
from parttool import PartitionName, PartitionType, ParttoolTarget
|
||||
from gen_empty_partition import generate_blanked_file
|
||||
|
||||
parser = argparse.ArgumentParser("ESP-IDF Partitions Tool Example")
|
||||
|
||||
parser.add_argument("--port", "-p", help="port where the device to perform operations on is connected")
|
||||
parser.add_argument("--binary", "-b", help="path to built example binary", default=os.path.join("build", "parttool.bin"))
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
target = ParttoolTarget(args.port)
|
||||
|
||||
# Read app partition and save the contents to a file. The app partition is identified
|
||||
# using type-subtype combination
|
||||
print("Checking if device app binary matches built binary")
|
||||
factory = PartitionType("app", "factory")
|
||||
target.read_partition(factory, "app.bin")
|
||||
assert_file_same(args.binary, "app.bin", "Device app binary does not match built binary")
|
||||
|
||||
# Retrieve info on data storage partition, this time identifying it by name.
|
||||
storage = PartitionName("storage")
|
||||
storage_info = target.get_partition_info(storage)
|
||||
print("Found data partition at offset 0x{:x} with size 0x{:x}".format(storage_info.offset, storage_info.size))
|
||||
|
||||
# Create a file whose contents will be written to the storage partition
|
||||
with open("write.bin", "wb") as f:
|
||||
# Create a file to write to the data partition with randomly generated content
|
||||
f.write(os.urandom(storage_info.size))
|
||||
|
||||
# Write the contents of the created file to storage partition
|
||||
print("Writing to data partition")
|
||||
target.write_partition(storage, "write.bin")
|
||||
|
||||
# Read back the contents of the storage partition
|
||||
print("Reading data partition")
|
||||
target.read_partition(storage, "read.bin")
|
||||
|
||||
assert_file_same("write.bin", "read.bin", "Read contents of storage partition does not match source file contents")
|
||||
|
||||
# Erase contents of the storage partition
|
||||
print("Erasing data partition")
|
||||
target.erase_partition(storage)
|
||||
|
||||
# Read back the erased data partition
|
||||
print("Reading data partition")
|
||||
target.read_partition(storage, "read.bin")
|
||||
|
||||
# Generate a file of all 0xFF
|
||||
generate_blanked_file(storage_info.size, "blank.bin")
|
||||
|
||||
assert_file_same("blank.bin", "read.bin", "Contents of storage partition not fully erased")
|
||||
|
||||
# Example end and cleanup
|
||||
print("\nPartition tool operations performed successfully!")
|
||||
clean_files = ["app.bin", "read.bin", "blank.bin", "write.bin"]
|
||||
for clean_file in clean_files:
|
||||
os.unlink(clean_file)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Demonstrates command-line interface of Partition Tool, parttool.py
|
||||
#
|
||||
#
|
||||
# $1 - serial port where target device to operate on is connnected to, by default the first found valid serial port
|
||||
# $2 - path to this example's built binary file (parttool.bin), by default $PWD/build/parttool.bin
|
||||
PORT=$1
|
||||
PARTTOOL_PY="python $IDF_PATH/components/partition_table/parttool.py -q"
|
||||
|
||||
if [[ "$PORT" != "" ]]; then
|
||||
PARTTOOL_PY="$PARTTOOL_PY --port $PORT"
|
||||
fi
|
||||
|
||||
GEN_EMPTY_PARTITION_PY="python $IDF_PATH/components/partition_table/gen_empty_partition.py"
|
||||
|
||||
BINARY=$2
|
||||
|
||||
if [[ "$BINARY" == "" ]]; then
|
||||
BINARY=build/parttool.bin
|
||||
fi
|
||||
|
||||
function assert_file_same()
|
||||
{
|
||||
sz_a=$(stat -c %s $1)
|
||||
sz_b=$(stat -c %s $2)
|
||||
sz=$((sz_a < sz_b ? sz_a : sz_b))
|
||||
res=$(cmp -s -n $sz $1 $2) ||
|
||||
(echo "!!!!!!!!!!!!!!!!!!!"
|
||||
echo "FAILURE: $3"
|
||||
echo "!!!!!!!!!!!!!!!!!!!")
|
||||
}
|
||||
|
||||
# Read app partition and save the contents to a file. The app partition is identified
|
||||
# using type-subtype combination
|
||||
echo "Checking if device app binary matches built binary"
|
||||
$PARTTOOL_PY read_partition --partition-type=app --partition-subtype=factory --output=app.bin
|
||||
assert_file_same app.bin $BINARY "Device app binary does not match built binary"
|
||||
|
||||
# Retrieve info on data storage partition, this time identifying it by name.
|
||||
offset=$($PARTTOOL_PY get_partition_info --partition-name=storage --info offset)
|
||||
size=$($PARTTOOL_PY get_partition_info --partition-name=storage --info size)
|
||||
echo "Found data partition at offset $offset with size $size"
|
||||
|
||||
# Create a file whose contents will be written to the storage partition
|
||||
head -c $(($size)) /dev/urandom > write.bin
|
||||
|
||||
# Write the contents of the created file to storage partition
|
||||
echo "Writing to data partition"
|
||||
$PARTTOOL_PY write_partition --partition-name=storage --input write.bin
|
||||
|
||||
# Read back the contents of the storage partition
|
||||
echo "Reading data partition"
|
||||
$PARTTOOL_PY read_partition --partition-name=storage --output read.bin
|
||||
|
||||
assert_file_same write.bin read.bin "Read contents of storage partition does not match source file contents"
|
||||
|
||||
# Erase contents of the storage partition
|
||||
echo "Erasing data partition"
|
||||
$PARTTOOL_PY erase_partition --partition-name=storage
|
||||
|
||||
# Read back the erased data partition
|
||||
echo "Reading data partition"
|
||||
$PARTTOOL_PY read_partition --partition-name=storage --output read.bin
|
||||
|
||||
# Generate a file of all 0xFF
|
||||
$GEN_EMPTY_PARTITION_PY $(($size)) blank.bin
|
||||
|
||||
assert_file_same read.bin blank.bin "Contents of storage partition not fully erased"
|
||||
|
||||
# Example end and cleanup
|
||||
printf "\nPartition tool operations performed successfully\n"
|
||||
rm -rf app.bin read.bin blank.bin write.bin
|
||||
@@ -0,0 +1,3 @@
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv"
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv"
|
||||
@@ -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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(sd_card)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := sd_card
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
# SD Card example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
This example demonstrates how to use an SD card with ESP32 or ESP32-S2. Example does the following steps:
|
||||
|
||||
1. Use an "all-in-one" `esp_vfs_fat_sdmmc_mount` function to:
|
||||
- initialize SDMMC peripheral,
|
||||
- probe and initialize the card connected to SD/MMC slot 1 (HS2_CMD, HS2_CLK, HS2_D0, HS2_D1, HS2_D2, HS2_D3 lines),
|
||||
- mount FAT filesystem using FATFS library (and format card, if the filesystem can not be mounted),
|
||||
- register FAT filesystem in VFS, enabling C standard library and POSIX functions to be used.
|
||||
2. Print information about the card, such as name, type, capacity, and maximum supported frequency.
|
||||
3. Create a file using `fopen` and write to it using `fprintf`.
|
||||
4. Rename the file. Before renaming, check if destination file already exists using `stat` function, and remove it using `unlink` function.
|
||||
5. Open renamed file for reading, read back the line, and print it to the terminal.
|
||||
|
||||
This example support SD (SDSC, SDHC, SDXC) cards and eMMC chips.
|
||||
|
||||
## Hardware
|
||||
|
||||
### Connections for ESP32
|
||||
|
||||
This example runs on ESP-WROVER-KIT boards without any extra modifications required, only the SD card needs to be inserted into the slot.
|
||||
|
||||
Other ESP32 development boards need to be connected to SD card as follows:
|
||||
|
||||
ESP32 pin | SD card pin | SPI pin | Notes
|
||||
--------------|-------------|---------|------------
|
||||
GPIO14 (MTMS) | CLK | SCK | 10k pullup in SD mode
|
||||
GPIO15 (MTDO) | CMD | MOSI | 10k pullup, both in SD and SPI modes
|
||||
GPIO2 | D0 | MISO | 10k pullup in SD mode, pull low to go into download mode (see Note about GPIO2 below!)
|
||||
GPIO4 | D1 | N/C | not used in 1-line SD mode; 10k pullup in 4-line SD mode
|
||||
GPIO12 (MTDI) | D2 | N/C | not used in 1-line SD mode; 10k pullup in 4-line SD mode (see Note about GPIO12 below!)
|
||||
GPIO13 (MTCK) | D3 | CS | not used in 1-line SD mode, but card's D3 pin must have a 10k pullup
|
||||
N/C | CD | | optional, not used in the example
|
||||
N/C | WP | | optional, not used in the example
|
||||
|
||||
This example doesn't utilize card detect (CD) and write protect (WP) signals from SD card slot.
|
||||
|
||||
With the given pinout for SPI mode, same connections between the SD card and ESP32 can be used to test both SD and SPI modes, provided that the appropriate pullups are in place.
|
||||
See [the document about pullup requirements](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/sd_pullup_requirements.html) for more details about pullup support and compatibility of modules and development boards.
|
||||
|
||||
In SPI mode, pins can be customized. See the initialization of ``spi_bus_config_t`` and ``sdspi_slot_config_t`` structures in the example code.
|
||||
|
||||
### Connections for ESP32-S2
|
||||
|
||||
Note that ESP32-S2 doesn't include SD Host peripheral and only supports SD over SPI. Therefore only SCK, MOSI, MISO, CS and ground pins need to be connected.
|
||||
|
||||
ESP32-S2 pin | SD card pin | SPI pin | Notes
|
||||
--------------|-------------|---------|------------
|
||||
GPIO14 | CLK | SCK | 10k pullup
|
||||
GPIO15 | CMD | MOSI | 10k pullup
|
||||
GPIO2 | D0 | MISO | 10k pullup
|
||||
GPIO13 | D3 | CS | 10k pullup
|
||||
N/C | CD | | optional, not used in the example
|
||||
N/C | WP | | optional, not used in the example
|
||||
|
||||
In SPI mode, pins can be customized. See the initialization of ``spi_bus_config_t`` and ``sdspi_slot_config_t`` structures in the example code.
|
||||
|
||||
### Note about GPIO2 (ESP32 only)
|
||||
|
||||
GPIO2 pin is used as a bootstrapping pin, and should be low to enter UART download mode. One way to do this is to connect GPIO0 and GPIO2 using a jumper, and then the auto-reset circuit on most development boards will pull GPIO2 low along with GPIO0, when entering download mode.
|
||||
|
||||
- Some boards have pulldown and/or LED on GPIO2. LED is usually ok, but pulldown will interfere with D0 signals and must be removed. Check the schematic of your development board for anything connected to GPIO2.
|
||||
|
||||
### Note about GPIO12 (ESP32 only)
|
||||
|
||||
GPIO12 is used as a bootstrapping pin to select output voltage of an internal regulator which powers the flash chip (VDD_SDIO). This pin has an internal pulldown so if left unconnected it will read low at reset (selecting default 3.3V operation). When adding a pullup to this pin for SD card operation, consider the following:
|
||||
|
||||
- For boards which don't use the internal regulator (VDD_SDIO) to power the flash, GPIO12 can be pulled high.
|
||||
- For boards which use 1.8V flash chip, GPIO12 needs to be pulled high at reset. This is fully compatible with SD card operation.
|
||||
- On boards which use the internal regulator and a 3.3V flash chip, GPIO12 must be low at reset. This is incompatible with SD card operation.
|
||||
* In most cases, external pullup can be omitted and an internal pullup can be enabled using a `gpio_pullup_en(GPIO_NUM_12);` call. Most SD cards work fine when an internal pullup on GPIO12 line is enabled. Note that if ESP32 experiences a power-on reset while the SD card is sending data, high level on GPIO12 can be latched into the bootstrapping register, and ESP32 will enter a boot loop until external reset with correct GPIO12 level is applied.
|
||||
* Another option is to burn the flash voltage selection efuses. This will permanently select 3.3V output voltage for the internal regulator, and GPIO12 will not be used as a bootstrapping pin. Then it is safe to connect a pullup resistor to GPIO12. This option is suggested for production use.
|
||||
|
||||
The following command can be used to program flash voltage selection efuses **to 3.3V**:
|
||||
|
||||
```sh
|
||||
components/esptool_py/esptool/espefuse.py set_flash_voltage 3.3V
|
||||
```
|
||||
|
||||
This command will burn the `XPD_SDIO_TIEH`, `XPD_SDIO_FORCE`, and `XPD_SDIO_REG` efuses. With all three burned to value 1, the internal VDD_SDIO flash voltage regulator is permanently enabled at 3.3V. See the technical reference manual for more details.
|
||||
|
||||
`espefuse.py` has a `--do-not-confirm` option if running from an automated flashing script.
|
||||
|
||||
## How to use example
|
||||
|
||||
SD card can be used in various modes. See below on choosing the mode to be used.
|
||||
|
||||
### 4-line and 1-line SD modes
|
||||
|
||||
By default, example code uses the following initializer for SDMMC slot configuration:
|
||||
|
||||
```c++
|
||||
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
|
||||
```
|
||||
|
||||
Among other things, this sets `slot_config.width = 0`, which means that SD/MMC driver will use the maximum bus width supported by the slot. For slot 1, it will switch to 4-line mode when initializing the card (initial communication always happens in 1-line mode). If some of the card's D1, D2, D3 pins are not connected to the ESP32, set `slot_config.width = 1` — then the SD/MMC driver will not attempt to switch to 4-line mode.
|
||||
|
||||
Note that even if card's D3 line is not connected to the ESP32, it still has to be pulled up, otherwise the card will go into SPI protocol mode.
|
||||
|
||||
### SPI mode
|
||||
|
||||
By default, the example uses SDMMC Host peripheral to access the card using SD protocol. To use SPI peripheral instead (and SPI protocol), uncomment ``#define USE_SPI_MODE`` line in the example code.
|
||||
|
||||
### Build and flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(Replace PORT with serial port name.)
|
||||
|
||||
(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 an example console output. In this case a 128MB SDSC card was connected, and `EXAMPLE_FORMAT_IF_MOUNT_FAILED` menuconfig option enabled. Card was unformatted, so the initial mount has failed. Card was then partitioned, formatted, and mounted again.
|
||||
|
||||
```
|
||||
I (336) example: Initializing SD card
|
||||
I (336) example: Using SDMMC peripheral
|
||||
I (336) gpio: GPIO[13]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
|
||||
W (596) vfs_fat_sdmmc: failed to mount card (13)
|
||||
W (596) vfs_fat_sdmmc: partitioning card
|
||||
W (596) vfs_fat_sdmmc: formatting card, allocation unit size=16384
|
||||
W (7386) vfs_fat_sdmmc: mounting again
|
||||
Name: XA0E5
|
||||
Type: SDHC/SDXC
|
||||
Speed: 20 MHz
|
||||
Size: 61068MB
|
||||
I (7386) example: Opening file
|
||||
I (7396) example: File written
|
||||
I (7396) example: Renaming file
|
||||
I (7396) example: Reading file
|
||||
I (7396) example: Read from file: 'Hello XA0E5!'
|
||||
I (7396) example: Card unmounted
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Failure to upload the example
|
||||
|
||||
```
|
||||
Connecting........_____....._____....._____....._____....._____....._____....._____
|
||||
|
||||
A fatal error occurred: Failed to connect to Espressif device: Invalid head of packet (0x34)
|
||||
```
|
||||
|
||||
Disconnect the SD card D0/MISO line from GPIO2 and try uploading again. Read "Note about GPIO2" above.
|
||||
|
||||
### Card fails to initialize with `sdmmc_init_sd_scr: send_scr (1) returned 0x107` error
|
||||
|
||||
Check connections between the card and the ESP32. For example, if you have disconnected GPIO2 to work around the flashing issue, connect it back and reset the ESP32 (using a button on the development board, or by pressing Ctrl-T Ctrl-R in IDF Monitor).
|
||||
|
||||
### Card fails to initialize with `sdmmc_check_scr: send_scr returned 0xffffffff` error
|
||||
|
||||
Connections between the card and the ESP32 are too long for the frequency used. Try using shorter connections, or try reducing the clock speed of SD interface.
|
||||
|
||||
### Failure to mount filesystem
|
||||
|
||||
```
|
||||
example: Failed to mount filesystem. If you want the card to be formatted, set the EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.
|
||||
```
|
||||
The example will be able to mount only cards formatted using FAT32 filesystem. If the card is formatted as exFAT or some other filesystem, you have an option to format it in the example code. Enable the `EXAMPLE_FORMAT_IF_MOUNT_FAILED` menuconfig option, then build and flash the example.
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "sd_card_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,9 @@
|
||||
menu "SD Card Example menu"
|
||||
|
||||
config EXAMPLE_FORMAT_IF_MOUNT_FAILED
|
||||
bool "Format the card if mount failed"
|
||||
default n
|
||||
help
|
||||
If this config item is set, format_if_mount_failed will be set to true and the card will be formatted if
|
||||
the mount has failed.
|
||||
endmenu
|
||||
@@ -0,0 +1,4 @@
|
||||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
@@ -0,0 +1,198 @@
|
||||
/* SD card and FAT filesystem example.
|
||||
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 <string.h>
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "driver/sdspi_host.h"
|
||||
#include "driver/spi_common.h"
|
||||
#include "sdmmc_cmd.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32
|
||||
#include "driver/sdmmc_host.h"
|
||||
#endif
|
||||
|
||||
static const char *TAG = "example";
|
||||
|
||||
#define MOUNT_POINT "/sdcard"
|
||||
|
||||
// This example can use SDMMC and SPI peripherals to communicate with SD card.
|
||||
// By default, SDMMC peripheral is used.
|
||||
// To enable SPI mode, uncomment the following line:
|
||||
|
||||
// #define USE_SPI_MODE
|
||||
|
||||
// ESP32-S2 doesn't have an SD Host peripheral, always use SPI:
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32S2
|
||||
#ifndef USE_SPI_MODE
|
||||
#define USE_SPI_MODE
|
||||
#endif // USE_SPI_MODE
|
||||
// on ESP32-S2, DMA channel must be the same as host id
|
||||
#define SPI_DMA_CHAN host.slot
|
||||
#endif //CONFIG_IDF_TARGET_ESP32S2
|
||||
|
||||
// DMA channel to be used by the SPI peripheral
|
||||
#ifndef SPI_DMA_CHAN
|
||||
#define SPI_DMA_CHAN 1
|
||||
#endif //SPI_DMA_CHAN
|
||||
|
||||
// When testing SD and SPI modes, keep in mind that once the card has been
|
||||
// initialized in SPI mode, it can not be reinitialized in SD mode without
|
||||
// toggling power to the card.
|
||||
|
||||
#ifdef USE_SPI_MODE
|
||||
// Pin mapping when using SPI mode.
|
||||
// With this mapping, SD card can be used both in SPI and 1-line SD mode.
|
||||
// Note that a pull-up on CS line is required in SD mode.
|
||||
#define PIN_NUM_MISO 2
|
||||
#define PIN_NUM_MOSI 15
|
||||
#define PIN_NUM_CLK 14
|
||||
#define PIN_NUM_CS 13
|
||||
#endif //USE_SPI_MODE
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_err_t ret;
|
||||
// Options for mounting the filesystem.
|
||||
// If format_if_mount_failed is set to true, SD card will be partitioned and
|
||||
// formatted in case when mounting fails.
|
||||
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
|
||||
#ifdef CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED
|
||||
.format_if_mount_failed = true,
|
||||
#else
|
||||
.format_if_mount_failed = false,
|
||||
#endif // EXAMPLE_FORMAT_IF_MOUNT_FAILED
|
||||
.max_files = 5,
|
||||
.allocation_unit_size = 16 * 1024
|
||||
};
|
||||
sdmmc_card_t* card;
|
||||
const char mount_point[] = MOUNT_POINT;
|
||||
ESP_LOGI(TAG, "Initializing SD card");
|
||||
|
||||
// Use settings defined above to initialize SD card and mount FAT filesystem.
|
||||
// Note: esp_vfs_fat_sdmmc/sdspi_mount is all-in-one convenience functions.
|
||||
// Please check its source code and implement error recovery when developing
|
||||
// production applications.
|
||||
#ifndef USE_SPI_MODE
|
||||
ESP_LOGI(TAG, "Using SDMMC peripheral");
|
||||
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
|
||||
|
||||
// This initializes the slot without card detect (CD) and write protect (WP) signals.
|
||||
// Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
|
||||
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
|
||||
|
||||
// To use 1-line SD mode, uncomment the following line:
|
||||
// slot_config.width = 1;
|
||||
|
||||
// GPIOs 15, 2, 4, 12, 13 should have external 10k pull-ups.
|
||||
// Internal pull-ups are not sufficient. However, enabling internal pull-ups
|
||||
// does make a difference some boards, so we do that here.
|
||||
gpio_set_pull_mode(15, GPIO_PULLUP_ONLY); // CMD, needed in 4- and 1- line modes
|
||||
gpio_set_pull_mode(2, GPIO_PULLUP_ONLY); // D0, needed in 4- and 1-line modes
|
||||
gpio_set_pull_mode(4, GPIO_PULLUP_ONLY); // D1, needed in 4-line mode only
|
||||
gpio_set_pull_mode(12, GPIO_PULLUP_ONLY); // D2, needed in 4-line mode only
|
||||
gpio_set_pull_mode(13, GPIO_PULLUP_ONLY); // D3, needed in 4- and 1-line modes
|
||||
|
||||
ret = esp_vfs_fat_sdmmc_mount(mount_point, &host, &slot_config, &mount_config, &card);
|
||||
#else
|
||||
ESP_LOGI(TAG, "Using SPI peripheral");
|
||||
|
||||
sdmmc_host_t host = SDSPI_HOST_DEFAULT();
|
||||
spi_bus_config_t bus_cfg = {
|
||||
.mosi_io_num = PIN_NUM_MOSI,
|
||||
.miso_io_num = PIN_NUM_MISO,
|
||||
.sclk_io_num = PIN_NUM_CLK,
|
||||
.quadwp_io_num = -1,
|
||||
.quadhd_io_num = -1,
|
||||
.max_transfer_sz = 4000,
|
||||
};
|
||||
ret = spi_bus_initialize(host.slot, &bus_cfg, SPI_DMA_CHAN);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to initialize bus.");
|
||||
return;
|
||||
}
|
||||
|
||||
// This initializes the slot without card detect (CD) and write protect (WP) signals.
|
||||
// Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
|
||||
sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
|
||||
slot_config.gpio_cs = PIN_NUM_CS;
|
||||
slot_config.host_id = host.slot;
|
||||
|
||||
ret = esp_vfs_fat_sdspi_mount(mount_point, &host, &slot_config, &mount_config, &card);
|
||||
#endif //USE_SPI_MODE
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
if (ret == ESP_FAIL) {
|
||||
ESP_LOGE(TAG, "Failed to mount filesystem. "
|
||||
"If you want the card to be formatted, set the EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to initialize the card (%s). "
|
||||
"Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Card has been initialized, print its properties
|
||||
sdmmc_card_print_info(stdout, card);
|
||||
|
||||
// Use POSIX and C standard library functions to work with files.
|
||||
// First create a file.
|
||||
ESP_LOGI(TAG, "Opening file");
|
||||
FILE* f = fopen(MOUNT_POINT"/hello.txt", "w");
|
||||
if (f == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to open file for writing");
|
||||
return;
|
||||
}
|
||||
fprintf(f, "Hello %s!\n", card->cid.name);
|
||||
fclose(f);
|
||||
ESP_LOGI(TAG, "File written");
|
||||
|
||||
// Check if destination file exists before renaming
|
||||
struct stat st;
|
||||
if (stat(MOUNT_POINT"/foo.txt", &st) == 0) {
|
||||
// Delete it if it exists
|
||||
unlink(MOUNT_POINT"/foo.txt");
|
||||
}
|
||||
|
||||
// Rename original file
|
||||
ESP_LOGI(TAG, "Renaming file");
|
||||
if (rename(MOUNT_POINT"/hello.txt", MOUNT_POINT"/foo.txt") != 0) {
|
||||
ESP_LOGE(TAG, "Rename failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// Open renamed file for reading
|
||||
ESP_LOGI(TAG, "Reading file");
|
||||
f = fopen(MOUNT_POINT"/foo.txt", "r");
|
||||
if (f == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to open file for reading");
|
||||
return;
|
||||
}
|
||||
char line[64];
|
||||
fgets(line, sizeof(line), f);
|
||||
fclose(f);
|
||||
// strip newline
|
||||
char* pos = strchr(line, '\n');
|
||||
if (pos) {
|
||||
*pos = '\0';
|
||||
}
|
||||
ESP_LOGI(TAG, "Read from file: '%s'", line);
|
||||
|
||||
// All done, unmount partition and disable SDMMC or SPI peripheral
|
||||
esp_vfs_fat_sdcard_unmount(mount_point, card);
|
||||
ESP_LOGI(TAG, "Card unmounted");
|
||||
#ifdef USE_SPI_MODE
|
||||
//deinitialize the bus after all devices are removed
|
||||
spi_bus_free(host.slot);
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
from tiny_test_fw import Utility
|
||||
import ttfw_idf
|
||||
import re
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='UT_T1_SDMODE')
|
||||
def test_examples_sd_card(env, extra_data):
|
||||
|
||||
dut = env.get_dut('sd_card', 'examples/storage/sd_card')
|
||||
dut.start_app()
|
||||
dut.expect('example: Initializing SD card', timeout=20)
|
||||
peripheral = dut.expect(re.compile(r'example: Using (\w+) peripheral'), timeout=5)[0]
|
||||
Utility.console_log('peripheral {} detected'.format(peripheral))
|
||||
assert peripheral in ('SDMMC', 'SPI')
|
||||
|
||||
# These lines are matched separately because of ASCII color codes in the output
|
||||
name = dut.expect(re.compile(r'Name: (\w+)'), timeout=5)[0]
|
||||
_type = dut.expect(re.compile(r'Type: (\S+)'), timeout=5)[0]
|
||||
speed = dut.expect(re.compile(r'Speed: (\S+)'), timeout=5)[0]
|
||||
size = dut.expect(re.compile(r'Size: (\S+)'), timeout=5)[0]
|
||||
|
||||
Utility.console_log('Card {} {} {}MHz {} found'.format(name, _type, speed, size))
|
||||
|
||||
dut.expect_all('Opening file',
|
||||
'File written',
|
||||
'Renaming file',
|
||||
'Reading file',
|
||||
"Read from file: 'Hello {}!".format(name),
|
||||
'Card unmounted',
|
||||
timeout=5)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_examples_sd_card()
|
||||
@@ -0,0 +1 @@
|
||||
CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED=y
|
||||
@@ -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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(semihost_vfs)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := semihost_vfs
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
# Semihosting VFS driver example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
This example demonstrates how to use semihosting VFS driver with ESP32. Example does the following steps:
|
||||
|
||||
1. Uses `esp_vfs_semihost_register` function to register exposed host directory in VFS, enabling C standard library and POSIX functions to be used.
|
||||
2. Redirects `stdout` from the UART to the file on the host using `freopen`.
|
||||
3. Prints several messages to the redirected.
|
||||
4. Switches back to UART `stdout` using `freopen`.
|
||||
5. Opens text file on the host.
|
||||
6. Reads the file and prints its content on stdout.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware and tools required
|
||||
|
||||
This example does not require any special hardware, and can be run on any common development board.
|
||||
This example requires [OpenOCD](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/jtag-debugging/index.html#run-openocd).
|
||||
NOTE: In order to run this example you need OpenOCD version `v0.10.0-esp32-20190313` or later.
|
||||
|
||||
Run OpenOCD using command:
|
||||
```
|
||||
bin/openocd -s share/openocd/scripts -c 'set ESP_SEMIHOST_BASEDIR '$IDF_PATH/examples/storage/semihost_vfs/data -f board/esp32-wrover-kit-3.3v.cfg
|
||||
```
|
||||
This command also configures OpenOCD to expose example project `data` subdirectory to the target's semihosting VFS driver.
|
||||
|
||||
### Build and flash
|
||||
|
||||
Replace PORT with serial port name:
|
||||
|
||||
```
|
||||
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
|
||||
|
||||
There are two types of outputs produced by example:
|
||||
1. File `esp32_stdout.txt` in the host directory mounted to the target:
|
||||
|
||||
```
|
||||
W (274) example: Switched to semihosted stdout
|
||||
Semihosted stdout write 0
|
||||
Semihosted stdout write 1
|
||||
Semihosted stdout write 2
|
||||
...
|
||||
Semihosted stdout write 98
|
||||
Semihosted stdout write 99
|
||||
W (274) example: Switch to UART stdout
|
||||
```
|
||||
|
||||
2. On the boards console:
|
||||
|
||||
```
|
||||
W (274) example: Switch to semihosted stdout
|
||||
W (274) example: Switched back to UART stdout
|
||||
I (274) example: Wrote 2798 bytes
|
||||
====================== HOST DATA START =========================
|
||||
The following are the graphical (non-control) characters defined by
|
||||
ISO 8859-1 (1987). Descriptions in words aren't all that helpful,
|
||||
but they're the best we can do in text. A graphics file illustrating
|
||||
the character set should be available from the same archive as this
|
||||
file.
|
||||
|
||||
Hex Description Hex Description
|
||||
|
||||
20 SPACE
|
||||
...
|
||||
7D RIGHT CURLY BRACKET FD SMALL LETTER Y WITH ACUTE
|
||||
7E TILDE FE SMALL LETTER THORN (Icelandic)
|
||||
FF SMALL LETTER Y WITH DIAERESIS
|
||||
====================== HOST DATA END =========================
|
||||
I (694) example: Read 6121 bytes
|
||||
```
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
The following are the graphical (non-control) characters defined by
|
||||
ISO 8859-1 (1987). Descriptions in words aren't all that helpful,
|
||||
but they're the best we can do in text. A graphics file illustrating
|
||||
the character set should be available from the same archive as this
|
||||
file.
|
||||
|
||||
Hex Description Hex Description
|
||||
|
||||
20 SPACE
|
||||
21 EXCLAMATION MARK A1 INVERTED EXCLAMATION MARK
|
||||
22 QUOTATION MARK A2 CENT SIGN
|
||||
23 NUMBER SIGN A3 POUND SIGN
|
||||
24 DOLLAR SIGN A4 CURRENCY SIGN
|
||||
25 PERCENT SIGN A5 YEN SIGN
|
||||
26 AMPERSAND A6 BROKEN BAR
|
||||
27 APOSTROPHE A7 SECTION SIGN
|
||||
28 LEFT PARENTHESIS A8 DIAERESIS
|
||||
29 RIGHT PARENTHESIS A9 COPYRIGHT SIGN
|
||||
2A ASTERISK AA FEMININE ORDINAL INDICATOR
|
||||
2B PLUS SIGN AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
|
||||
2C COMMA AC NOT SIGN
|
||||
2D HYPHEN-MINUS AD SOFT HYPHEN
|
||||
2E FULL STOP AE REGISTERED SIGN
|
||||
2F SOLIDUS AF OVERLINE
|
||||
30 DIGIT ZERO B0 DEGREE SIGN
|
||||
31 DIGIT ONE B1 PLUS-MINUS SIGN
|
||||
32 DIGIT TWO B2 SUPERSCRIPT TWO
|
||||
33 DIGIT THREE B3 SUPERSCRIPT THREE
|
||||
34 DIGIT FOUR B4 ACUTE ACCENT
|
||||
35 DIGIT FIVE B5 MICRO SIGN
|
||||
36 DIGIT SIX B6 PILCROW SIGN
|
||||
37 DIGIT SEVEN B7 MIDDLE DOT
|
||||
38 DIGIT EIGHT B8 CEDILLA
|
||||
39 DIGIT NINE B9 SUPERSCRIPT ONE
|
||||
3A COLON BA MASCULINE ORDINAL INDICATOR
|
||||
3B SEMICOLON BB RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
|
||||
3C LESS-THAN SIGN BC VULGAR FRACTION ONE QUARTER
|
||||
3D EQUALS SIGN BD VULGAR FRACTION ONE HALF
|
||||
3E GREATER-THAN SIGN BE VULGAR FRACTION THREE QUARTERS
|
||||
3F QUESTION MARK BF INVERTED QUESTION MARK
|
||||
40 COMMERCIAL AT C0 CAPITAL LETTER A WITH GRAVE
|
||||
41 CAPITAL LETTER A C1 CAPITAL LETTER A WITH ACUTE
|
||||
42 CAPITAL LETTER B C2 CAPITAL LETTER A WITH CIRCUMFLEX
|
||||
43 CAPITAL LETTER C C3 CAPITAL LETTER A WITH TILDE
|
||||
44 CAPITAL LETTER D C4 CAPITAL LETTER A WITH DIAERESIS
|
||||
45 CAPITAL LETTER E C5 CAPITAL LETTER A WITH RING ABOVE
|
||||
46 CAPITAL LETTER F C6 CAPITAL LETTER AE
|
||||
47 CAPITAL LETTER G C7 CAPITAL LETTER C WITH CEDILLA
|
||||
48 CAPITAL LETTER H C8 CAPITAL LETTER E WITH GRAVE
|
||||
49 CAPITAL LETTER I C9 CAPITAL LETTER E WITH ACUTE
|
||||
4A CAPITAL LETTER J CA CAPITAL LETTER E WITH CIRCUMFLEX
|
||||
4B CAPITAL LETTER K CB CAPITAL LETTER E WITH DIAERESIS
|
||||
4C CAPITAL LETTER L CC CAPITAL LETTER I WITH GRAVE
|
||||
4D CAPITAL LETTER M CD CAPITAL LETTER I WITH ACUTE
|
||||
4E CAPITAL LETTER N CE CAPITAL LETTER I WITH CIRCUMFLEX
|
||||
4F CAPITAL LETTER O CF CAPITAL LETTER I WITH DIAERESIS
|
||||
50 CAPITAL LETTER P D0 CAPITAL LETTER ETH (Icelandic)
|
||||
51 CAPITAL LETTER Q D1 CAPITAL LETTER N WITH TILDE
|
||||
52 CAPITAL LETTER R D2 CAPITAL LETTER O WITH GRAVE
|
||||
53 CAPITAL LETTER S D3 CAPITAL LETTER O WITH ACUTE
|
||||
54 CAPITAL LETTER T D4 CAPITAL LETTER O WITH CIRCUMFLEX
|
||||
55 CAPITAL LETTER U D5 CAPITAL LETTER O WITH TILDE
|
||||
56 CAPITAL LETTER V D6 CAPITAL LETTER O WITH DIAERESIS
|
||||
57 CAPITAL LETTER W D7 MULTIPLICATION SIGN
|
||||
58 CAPITAL LETTER X D8 CAPITAL LETTER O WITH STROKE
|
||||
59 CAPITAL LETTER Y D9 CAPITAL LETTER U WITH GRAVE
|
||||
5A CAPITAL LETTER Z DA CAPITAL LETTER U WITH ACUTE
|
||||
5B LEFT SQUARE BRACKET DB CAPITAL LETTER U WITH CIRCUMFLEX
|
||||
5C REVERSE SOLIDUS DC CAPITAL LETTER U WITH DIAERESIS
|
||||
5D RIGHT SQUARE BRACKET DD CAPITAL LETTER Y WITH ACUTE
|
||||
5E CIRCUMFLEX ACCENT DE CAPITAL LETTER THORN (Icelandic)
|
||||
5F LOW LINE DF SMALL LETTER SHARP S (German)
|
||||
60 GRAVE ACCENT E0 SMALL LETTER A WITH GRAVE
|
||||
61 SMALL LETTER A E1 SMALL LETTER A WITH ACUTE
|
||||
62 SMALL LETTER B E2 SMALL LETTER A WITH CIRCUMFLEX
|
||||
63 SMALL LETTER C E3 SMALL LETTER A WITH TILDE
|
||||
64 SMALL LETTER D E4 SMALL LETTER A WITH DIAERESIS
|
||||
65 SMALL LETTER E E5 SMALL LETTER A WITH RING ABOVE
|
||||
66 SMALL LETTER F E6 SMALL LETTER AE
|
||||
67 SMALL LETTER G E7 SMALL LETTER C WITH CEDILLA
|
||||
68 SMALL LETTER H E8 SMALL LETTER E WITH GRAVE
|
||||
69 SMALL LETTER I E9 SMALL LETTER E WITH ACUTE
|
||||
6A SMALL LETTER J EA SMALL LETTER E WITH CIRCUMFLEX
|
||||
6B SMALL LETTER K EB SMALL LETTER E WITH DIAERESIS
|
||||
6C SMALL LETTER L EC SMALL LETTER I WITH GRAVE
|
||||
6D SMALL LETTER M ED SMALL LETTER I WITH ACUTE
|
||||
6E SMALL LETTER N EE SMALL LETTER I WITH CIRCUMFLEX
|
||||
6F SMALL LETTER O EF SMALL LETTER I WITH DIAERESIS
|
||||
70 SMALL LETTER P F0 SMALL LETTER ETH (Icelandic)
|
||||
71 SMALL LETTER Q F1 SMALL LETTER N WITH TILDE
|
||||
72 SMALL LETTER R F2 SMALL LETTER O WITH GRAVE
|
||||
73 SMALL LETTER S F3 SMALL LETTER O WITH ACUTE
|
||||
74 SMALL LETTER T F4 SMALL LETTER O WITH CIRCUMFLEX
|
||||
75 SMALL LETTER U F5 SMALL LETTER O WITH TILDE
|
||||
76 SMALL LETTER V F6 SMALL LETTER O WITH DIAERESIS
|
||||
77 SMALL LETTER W F7 DIVISION SIGN
|
||||
78 SMALL LETTER X F8 SMALL LETTER O WITH STROKE
|
||||
79 SMALL LETTER Y F9 SMALL LETTER U WITH GRAVE
|
||||
7A SMALL LETTER Z FA SMALL LETTER U WITH ACUTE
|
||||
7B LEFT CURLY BRACKET FB SMALL LETTER U WITH CIRCUMFLEX
|
||||
7C VERTICAL LINE FC SMALL LETTER U WITH DIAERESIS
|
||||
7D RIGHT CURLY BRACKET FD SMALL LETTER Y WITH ACUTE
|
||||
7E TILDE FE SMALL LETTER THORN (Icelandic)
|
||||
FF SMALL LETTER Y WITH DIAERESIS
|
||||
@@ -0,0 +1,5 @@
|
||||
set(COMPONENT_SRCS )
|
||||
set(COMPONENT_ADD_INCLUDEDIRS ".")
|
||||
|
||||
idf_component_register(SRCS "semihost_vfs_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,4 @@
|
||||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
@@ -0,0 +1,92 @@
|
||||
/* SPIFFS filesystem example.
|
||||
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 <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_vfs_semihost.h"
|
||||
|
||||
static const char *TAG = "example";
|
||||
|
||||
#define STRINGIFY(s) STRINGIFY2(s)
|
||||
#define STRINGIFY2(s) #s
|
||||
|
||||
static uint8_t s_buf[512];
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
// Register host FS at '/host'. On the host file will be written/read in the current semihosting dir of OpenOCD
|
||||
esp_err_t ret = esp_vfs_semihost_register("/host", NULL);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to register semihost driver (%s)!", esp_err_to_name(ret));
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGW(TAG, "Switch to semihosted stdout");
|
||||
FILE *fout = freopen("/host/esp32_stdout.txt", "w", stdout);
|
||||
if (fout == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to reopen stdout (%d)!", errno);
|
||||
return;
|
||||
}
|
||||
// Increase file buffer to perform data transfers using larger chunks.
|
||||
// Every read/write triggers breakpoint, so transferring of small chunks is quite inefficient.
|
||||
setvbuf(fout, (char *)s_buf, _IOFBF, sizeof(s_buf));
|
||||
|
||||
// this will be printed to the file on host
|
||||
ESP_LOGW(TAG, "Switched to semihosted stdout");
|
||||
for (int i = 0; i < 100; i++) {
|
||||
// printf is also redirected and sends data to the file on host
|
||||
printf("Semihosted stdout write %d\n", i);
|
||||
}
|
||||
ESP_LOGW(TAG, "Switch to UART stdout");
|
||||
fflush(fout); // ensure that all data are sent to the host file
|
||||
// ftell can also be used, get file size before closing it in `freopen`
|
||||
int count = ftell(fout);
|
||||
stdout = freopen("/dev/uart/" STRINGIFY(CONFIG_ESP_CONSOLE_UART_NUM), "w", fout);
|
||||
if (stdout == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to reopen semihosted stdout (%d)!", errno);
|
||||
return;
|
||||
}
|
||||
// all remaining messages will be printed to UART
|
||||
ESP_LOGW(TAG, "Switched back to UART stdout");
|
||||
ESP_LOGI(TAG, "Wrote %d bytes", count);
|
||||
|
||||
printf("====================== HOST DATA START =========================\n");
|
||||
// open() can also be used to access files on the host
|
||||
int fd = open("/host/host_file.txt", O_RDONLY, 0);
|
||||
if (fd == -1) {
|
||||
ESP_LOGE(TAG, "Failed to open file (%d)!", errno);
|
||||
return;
|
||||
}
|
||||
ssize_t read_bytes;
|
||||
count = 0;
|
||||
do {
|
||||
read_bytes = read(fd, s_buf, sizeof(s_buf));
|
||||
if(read_bytes == -1) {
|
||||
ESP_LOGE(TAG, "Failed to read file (%d)!", errno);
|
||||
} else if(read_bytes > 0) {
|
||||
fwrite(s_buf, 1, read_bytes, stdout);
|
||||
count += read_bytes;
|
||||
}
|
||||
} while(read_bytes > 0);
|
||||
printf("====================== HOST DATA END =========================\n");
|
||||
ESP_LOGI(TAG, "Read %d bytes", count);
|
||||
if (close(fd) == -1) {
|
||||
ESP_LOGE(TAG, "Failed to close file (%d)!", errno);
|
||||
}
|
||||
|
||||
ret = esp_vfs_semihost_unregister("/host");
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to unregister semihost driver (%s)!", esp_err_to_name(ret));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
# need this to detect that OpenOCD is connected
|
||||
CONFIG_ESP32_DEBUG_OCDAWARE=y
|
||||
@@ -0,0 +1,58 @@
|
||||
from io import open
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import ttfw_idf
|
||||
|
||||
try:
|
||||
from itertools import izip_longest as zip_longest
|
||||
except ImportError:
|
||||
# Python 3
|
||||
from itertools import zip_longest
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag="test_jtag_arm")
|
||||
def test_examples_semihost_vfs(env, extra_data):
|
||||
|
||||
rel_project_path = os.path.join('examples', 'storage', 'semihost_vfs')
|
||||
dut = env.get_dut('semihost_vfs', rel_project_path)
|
||||
idf_path = dut.app.get_sdk_path()
|
||||
proj_path = os.path.join(idf_path, rel_project_path)
|
||||
host_file_name = 'host_file.txt'
|
||||
|
||||
try:
|
||||
temp_dir = tempfile.mkdtemp()
|
||||
host_file_path = os.path.join(proj_path, 'data', host_file_name)
|
||||
shutil.copyfile(host_file_path, os.path.join(temp_dir, host_file_name))
|
||||
openocd_extra_args = '-c \'set ESP_SEMIHOST_BASEDIR {}\''.format(temp_dir)
|
||||
|
||||
with ttfw_idf.OCDProcess(os.path.join(proj_path, 'openocd.log'), openocd_extra_args):
|
||||
dut.start_app()
|
||||
dut.expect_all('example: Switch to semihosted stdout',
|
||||
'example: Switched back to UART stdout',
|
||||
'example: Wrote 2798 bytes',
|
||||
'====================== HOST DATA START =========================',
|
||||
timeout=20)
|
||||
with open(host_file_path) as f:
|
||||
file_content = [line.strip() for line in f]
|
||||
dut.expect_all(*file_content, timeout=20)
|
||||
dut.expect_all('====================== HOST DATA END =========================',
|
||||
'example: Read 6121 bytes',
|
||||
timeout=5)
|
||||
|
||||
with open(os.path.join(temp_dir, 'esp32_stdout.txt')) as f:
|
||||
def expected_content():
|
||||
yield 'example: Switched to semihosted stdout'
|
||||
for i in range(100):
|
||||
yield 'Semihosted stdout write {}'.format(i)
|
||||
yield 'example: Switch to UART stdout'
|
||||
|
||||
for actual, expected in zip_longest(f, expected_content(), fillvalue='-'):
|
||||
if expected not in actual: # "in" used because of the printed ASCII color codes
|
||||
raise RuntimeError('"{}" != "{}"'.format(expected, actual.strip()))
|
||||
finally:
|
||||
shutil.rmtree(temp_dir, ignore_errors=True)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_examples_semihost_vfs()
|
||||
@@ -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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(spiffs)
|
||||
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := spiffs
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
# SPIFFS example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
This example demonstrates how to use SPIFFS with ESP32. Example does the following steps:
|
||||
|
||||
1. Use an "all-in-one" `esp_vfs_spiffs_register` function to:
|
||||
- initialize SPIFFS,
|
||||
- mount SPIFFS filesystem using SPIFFS library (and format, if the filesystem can not be mounted),
|
||||
- register SPIFFS filesystem in VFS, enabling C standard library and POSIX functions to be used.
|
||||
2. Create a file using `fopen` and write to it using `fprintf`.
|
||||
3. Rename the file. Before renaming, check if destination file already exists using `stat` function, and remove it using `unlink` function.
|
||||
4. Open renamed file for reading, read back the line, and print it to the terminal.
|
||||
|
||||
SPIFFS partition size is set in partitions_example.csv file. See [Partition Tables](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/partition-tables.html) documentation for more information.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware required
|
||||
|
||||
This example does not require any special hardware, and can be run on any common development board.
|
||||
|
||||
### Build and flash
|
||||
|
||||
Replace PORT with serial port name:
|
||||
|
||||
```
|
||||
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 an example console output. In this case `format_if_mount_failed` parameter was set to `true` in the source code. SPIFFS was unformatted, so the initial mount has failed. SPIFFS was then formatted, and mounted again.
|
||||
|
||||
```
|
||||
I (324) example: Initializing SPIFFS
|
||||
W (324) SPIFFS: mount failed, -10025. formatting...
|
||||
I (19414) example: Partition size: total: 896321, used: 0
|
||||
I (19414) example: Opening file
|
||||
I (19504) example: File written
|
||||
I (19544) example: Renaming file
|
||||
I (19584) example: Reading file
|
||||
I (19584) example: Read from file: 'Hello World!'
|
||||
I (19584) example: SPIFFS unmounted
|
||||
```
|
||||
|
||||
To erase the contents of SPIFFS partition, run `idf.py erase_flash` command. Then upload the example again as described above.
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "spiffs_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,4 @@
|
||||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
@@ -0,0 +1,99 @@
|
||||
/* SPIFFS filesystem example.
|
||||
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 <string.h>
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_spiffs.h"
|
||||
|
||||
static const char *TAG = "example";
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Initializing SPIFFS");
|
||||
|
||||
esp_vfs_spiffs_conf_t conf = {
|
||||
.base_path = "/spiffs",
|
||||
.partition_label = NULL,
|
||||
.max_files = 5,
|
||||
.format_if_mount_failed = true
|
||||
};
|
||||
|
||||
// Use settings defined above to initialize and mount SPIFFS filesystem.
|
||||
// Note: esp_vfs_spiffs_register is an all-in-one convenience function.
|
||||
esp_err_t ret = esp_vfs_spiffs_register(&conf);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
if (ret == ESP_FAIL) {
|
||||
ESP_LOGE(TAG, "Failed to mount or format filesystem");
|
||||
} else if (ret == ESP_ERR_NOT_FOUND) {
|
||||
ESP_LOGE(TAG, "Failed to find SPIFFS partition");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
size_t total = 0, used = 0;
|
||||
ret = esp_spiffs_info(conf.partition_label, &total, &used);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret));
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used);
|
||||
}
|
||||
|
||||
// Use POSIX and C standard library functions to work with files.
|
||||
// First create a file.
|
||||
ESP_LOGI(TAG, "Opening file");
|
||||
FILE* f = fopen("/spiffs/hello.txt", "w");
|
||||
if (f == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to open file for writing");
|
||||
return;
|
||||
}
|
||||
fprintf(f, "Hello World!\n");
|
||||
fclose(f);
|
||||
ESP_LOGI(TAG, "File written");
|
||||
|
||||
// Check if destination file exists before renaming
|
||||
struct stat st;
|
||||
if (stat("/spiffs/foo.txt", &st) == 0) {
|
||||
// Delete it if it exists
|
||||
unlink("/spiffs/foo.txt");
|
||||
}
|
||||
|
||||
// Rename original file
|
||||
ESP_LOGI(TAG, "Renaming file");
|
||||
if (rename("/spiffs/hello.txt", "/spiffs/foo.txt") != 0) {
|
||||
ESP_LOGE(TAG, "Rename failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// Open renamed file for reading
|
||||
ESP_LOGI(TAG, "Reading file");
|
||||
f = fopen("/spiffs/foo.txt", "r");
|
||||
if (f == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to open file for reading");
|
||||
return;
|
||||
}
|
||||
char line[64];
|
||||
fgets(line, sizeof(line), f);
|
||||
fclose(f);
|
||||
// strip newline
|
||||
char* pos = strchr(line, '\n');
|
||||
if (pos) {
|
||||
*pos = '\0';
|
||||
}
|
||||
ESP_LOGI(TAG, "Read from file: '%s'", line);
|
||||
|
||||
// All done, unmount partition and disable SPIFFS
|
||||
esp_vfs_spiffs_unregister(conf.partition_label);
|
||||
ESP_LOGI(TAG, "SPIFFS unmounted");
|
||||
}
|
||||
@@ -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
|
||||
nvs, data, nvs, 0x9000, 0x6000,
|
||||
phy_init, data, phy, 0xf000, 0x1000,
|
||||
factory, app, factory, 0x10000, 1M,
|
||||
storage, data, spiffs, , 0xF0000,
|
||||
|
@@ -0,0 +1,3 @@
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv"
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv"
|
||||
@@ -0,0 +1,22 @@
|
||||
import re
|
||||
import ttfw_idf
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_GENERIC')
|
||||
def test_examples_spiffs(env, extra_data):
|
||||
|
||||
dut = env.get_dut('spiffs', 'examples/storage/spiffs')
|
||||
dut.start_app()
|
||||
dut.expect_all('example: Initializing SPIFFS',
|
||||
re.compile(r'example: Partition size: total: \d+, used: \d+'),
|
||||
'example: Opening file',
|
||||
'example: File written',
|
||||
'example: Renaming file',
|
||||
'example: Reading file',
|
||||
'example: Read from file: \'Hello World!\'',
|
||||
'example: SPIFFS unmounted',
|
||||
timeout=20)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_examples_spiffs()
|
||||
@@ -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.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(spiffsgen)
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := spiffsgen
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
||||
# Create a SPIFFS image from the contents of the 'spiffs_image' directory
|
||||
# that fits the partition named 'storage'. FLASH_IN_PROJECT indicates that
|
||||
# the generated image should be flashed when the entire project is flashed to
|
||||
# the target with 'make flash'.
|
||||
SPIFFS_IMAGE_FLASH_IN_PROJECT := 1
|
||||
$(eval $(call spiffs_create_partition_image,storage,spiffs_image))
|
||||
@@ -0,0 +1,59 @@
|
||||
# SPIFFS 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 SPIFFS image generation tool [spiffsgen.py](../../../components/spiffs/spiffsgen.py) to automatically create a SPIFFS
|
||||
filesystem image from the contents of a host folder 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 `spiffsgen.py` on the ESP-IDF Programming Guide under API Reference > Storage > SPIFFS Filesystem.
|
||||
|
||||
The following gives an overview of the example:
|
||||
|
||||
1. There is a directory `spiffs_image` from which the SPIFFS filesystem image will be created.
|
||||
|
||||
2. The function `spiffs_create_partition_image` is used to specify that a SPIFFS image
|
||||
should be created during build for the `storage` partition. For CMake, it is called from [the main component's CMakeLists.txt](./main/CMakeLists.txt);
|
||||
for Make, from the [project Makefile](./Makefile). `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 `storage.bin`.
|
||||
|
||||
3. Upon invocation of `idf.py -p PORT flash monitor`, application loads and
|
||||
finds there is already a valid SPIFFS filesystem in the `storage` partition with files same as those in `spiffs_image` directory. The application is then
|
||||
able to read those files.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Build and flash
|
||||
|
||||
To run the example, type the following command:
|
||||
|
||||
```Makefile
|
||||
# Make
|
||||
make flash monitor
|
||||
```
|
||||
or
|
||||
```CMake
|
||||
# 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 (10) example: Initializing SPIFFS
|
||||
I (110) example: Partition size: total: 896321, used: 171935
|
||||
I (110) example: Reading hello.txt
|
||||
I (110) example: Read from hello.txt: Hello World!
|
||||
I (110) example: Computing alice.txt MD5 hash
|
||||
I (330) example: Computed MD5 hash of alice.txt: deeb71f585cbb3ae5f7976d5127faf2a
|
||||
I (330) example: SPIFFS unmounted
|
||||
```
|
||||
|
||||
The logic of the example is contained in a [single source file](./main/spiffsgen_example_main.c), and it should be relatively simple to match points in its execution with the log outputs above.
|
||||
@@ -0,0 +1,27 @@
|
||||
from __future__ import print_function
|
||||
import os
|
||||
import hashlib
|
||||
|
||||
import ttfw_idf
|
||||
|
||||
|
||||
@ttfw_idf.idf_example_test(env_tag='Example_GENERIC')
|
||||
def test_examples_spiffsgen(env, extra_data):
|
||||
# Test with default build configurations
|
||||
dut = env.get_dut('spiffsgen', 'examples/storage/spiffsgen', dut_class=ttfw_idf.ESP32DUT)
|
||||
dut.start_app()
|
||||
|
||||
base_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'spiffs_image')
|
||||
|
||||
# Expect hello.txt is read successfully
|
||||
with open(os.path.join(base_dir, 'hello.txt'), 'r') as hello_txt:
|
||||
dut.expect('Read from hello.txt: ' + hello_txt.read())
|
||||
|
||||
# Expect alice.txt MD5 hash is computed accurately
|
||||
with open(os.path.join(base_dir, 'sub', 'alice.txt'), 'rb') as alice_txt:
|
||||
alice_md5 = hashlib.md5(alice_txt.read()).hexdigest()
|
||||
dut.expect('Computed MD5 hash of alice.txt: ' + alice_md5)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_examples_spiffsgen()
|
||||
@@ -0,0 +1,8 @@
|
||||
idf_component_register(SRCS "spiffsgen_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
|
||||
# Create a SPIFFS image from the contents of the 'spiffs_image' directory
|
||||
# that fits the partition named 'storage'. 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'.
|
||||
spiffs_create_partition_image(storage ../spiffs_image FLASH_IN_PROJECT)
|
||||
@@ -0,0 +1,4 @@
|
||||
#
|
||||
# "main" pseudo-component makefile.
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user