添加智能灯固件代码

This commit is contained in:
kerwincui
2021-07-13 17:14:51 +08:00
parent 332f74dd17
commit ecc0b91b8b
2568 changed files with 229441 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
# Storage Examples
Storage and management of user and system data in modules flash and on external memory / devices.
See the [README.md](../README.md) file in the upper level [examples](../) directory for more information about examples.

View File

@@ -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)

View File

@@ -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

View File

@@ -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'
```

View File

@@ -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()

View File

@@ -0,0 +1 @@
idf_component_register(SRCS "ext_flash_fatfs_example_main.c")

View File

@@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@@ -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;
}
}

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(nvs-rw-blob)

View File

@@ -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

View File

@@ -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.

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "nvs_blob_example_main.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,5 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@@ -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);
}
}

View File

@@ -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()

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(nvs-rw-value)

View File

@@ -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

View File

@@ -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.

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "nvs_value_example_main.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,5 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@@ -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();
}

View File

@@ -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()

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(nvs-rw-value-cxx)

View File

@@ -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

View File

@@ -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.

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "nvs_value_example_main.cpp"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,5 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@@ -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();
}

View File

@@ -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()

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(partition_find)

View File

@@ -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

View File

@@ -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.

View File

@@ -0,0 +1,3 @@
idf_component_register(SRCS "main.c"
INCLUDE_DIRS "."
EMBED_TXTFILES ../partitions_example.csv)

View File

@@ -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

View File

@@ -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");
}

View File

@@ -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()

View File

@@ -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,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
3 nvs, data, nvs, 0x9000, 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000,
5 factory, app, factory, 0x10000, 1M,
6 storage1, data, fat, , 0x40000,
7 storage2, data, fat, , 0x40000,

View File

@@ -0,0 +1,3 @@
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv"

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(partition_mmap)

View File

@@ -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

View File

@@ -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.

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "main.c"
INCLUDE_DIRS ".")

View File

@@ -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.
#

View File

@@ -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");
}

View File

@@ -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()

View File

@@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 1M,
storage, data, , , 0x40000,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
3 nvs, data, nvs, 0x9000, 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000,
5 factory, app, factory, 0x10000, 1M,
6 storage, data, , , 0x40000,

View File

@@ -0,0 +1,3 @@
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv"

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(partition_ops)

View File

@@ -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

View File

@@ -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.

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "main.c"
INCLUDE_DIRS ".")

View File

@@ -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.
#

View File

@@ -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");
}

View File

@@ -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()

View File

@@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 1M,
storage, data, , , 0x40000,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
3 nvs, data, nvs, 0x9000, 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000,
5 factory, app, factory, 0x10000, 1M,
6 storage, data, , , 0x40000,

View File

@@ -0,0 +1,3 @@
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv"

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(parttool)

View File

@@ -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

View File

@@ -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!
```

View File

@@ -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()

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "parttool_main.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@@ -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");
}

View File

@@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 1M,
storage, data, spiffs, , 0x10000,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
3 nvs, data, nvs, 0x9000, 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000,
5 factory, app, factory, 0x10000, 1M,
6 storage, data, spiffs, , 0x10000,

View File

@@ -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()

View File

@@ -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

View File

@@ -0,0 +1,3 @@
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv"

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(sd_card)

View File

@@ -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

View File

@@ -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.

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "sd_card_example_main.c"
INCLUDE_DIRS ".")

View File

@@ -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

View File

@@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@@ -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
}

View File

@@ -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()

View File

@@ -0,0 +1 @@
CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED=y

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(semihost_vfs)

View File

@@ -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

View File

@@ -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
```

View File

@@ -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

View File

@@ -0,0 +1,5 @@
set(COMPONENT_SRCS )
set(COMPONENT_ADD_INCLUDEDIRS ".")
idf_component_register(SRCS "semihost_vfs_example_main.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@@ -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));
}
}

View File

@@ -0,0 +1,2 @@
# need this to detect that OpenOCD is connected
CONFIG_ESP32_DEBUG_OCDAWARE=y

View File

@@ -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()

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(spiffs)

View File

@@ -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

View File

@@ -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.

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "spiffs_example_main.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@@ -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");
}

View File

@@ -0,0 +1,6 @@
# Name, Type, SubType, Offset, Size, Flags
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
nvs, data, nvs, 0x9000, 0x6000,
phy_init, data, phy, 0xf000, 0x1000,
factory, app, factory, 0x10000, 1M,
storage, data, spiffs, , 0xF0000,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
3 nvs, data, nvs, 0x9000, 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000,
5 factory, app, factory, 0x10000, 1M,
6 storage, data, spiffs, , 0xF0000,

View File

@@ -0,0 +1,3 @@
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv"

View File

@@ -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()

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(spiffsgen)

View File

@@ -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))

View File

@@ -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.

View File

@@ -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()

View File

@@ -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)

View File

@@ -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