forked from espressif/esp-idf
feat(storage/fatfs): restructure fatfsgen example
This commit is contained in:
committed by
Tomas Rohlinek
parent
33788de979
commit
d43669b2d7
6
examples/storage/fatfs/fatfsgen/CMakeLists.txt
Normal file
6
examples/storage/fatfs/fatfsgen/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||||
|
# in this exact order for cmake to work correctly
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(fatfsgen)
|
69
examples/storage/fatfs/fatfsgen/README.md
Normal file
69
examples/storage/fatfs/fatfsgen/README.md
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
|
||||||
|
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | -------- |
|
||||||
|
|
||||||
|
# FATFS partition generation example
|
||||||
|
|
||||||
|
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||||
|
|
||||||
|
This example demonstrates how to use the FATFS partition
|
||||||
|
generation tool [fatfsgen.py](../../../components/fatfs/fatfsgen.py) to automatically create a FATFS
|
||||||
|
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`.
|
||||||
|
Beware that the minimal required size of the flash is 4 MB.
|
||||||
|
You can specify using menuconfig weather example will use read-only or read-write mode. The default option is read-write mode.
|
||||||
|
To change it just use menuconfig:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
idf.py menuconfig
|
||||||
|
```
|
||||||
|
|
||||||
|
Then select `Example Configuration` a chose `Mode for generated FATFS image` either `Read-Write Mode` or `Read-Only Mode`.
|
||||||
|
`Read-Only` option indicates generating raw fatfs image without wear levelling support.
|
||||||
|
On the other hand, for `Read-Write` the generated fatfs image will support wear levelling thus can be mounted in read-write mode.
|
||||||
|
|
||||||
|
|
||||||
|
The following gives an overview of the example:
|
||||||
|
|
||||||
|
1. There is a directory `fatfs_long_name_image` from which the FATFS filesystem image will be created.
|
||||||
|
|
||||||
|
2. Based on the RO/RW configuration either `fatfs_create_rawflash_image` or `fatfs_create_spiflash_image` respectively,
|
||||||
|
is used to specify that a FATFS 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).
|
||||||
|
`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.
|
||||||
|
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 FATFS filesystem in the `storage` partition with files same as those in `fatfs_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:
|
||||||
|
|
||||||
|
```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 (322) example: Mounting FAT filesystem
|
||||||
|
I (332) example: Reading file
|
||||||
|
I (332) example: Read from file: 'this is test'
|
||||||
|
I (332) example: Unmounting FAT filesystem
|
||||||
|
I (342) example: Done
|
||||||
|
```
|
||||||
|
|
||||||
|
The logic of the example is contained in a [single source file](./main/fatfsgen_example_main.c),
|
||||||
|
and it should be relatively simple to match points in its execution with the log outputs above.
|
@@ -0,0 +1 @@
|
|||||||
|
This is generated on the host; long name it has
|
@@ -0,0 +1 @@
|
|||||||
|
this is test; long name it has
|
18
examples/storage/fatfs/fatfsgen/main/CMakeLists.txt
Normal file
18
examples/storage/fatfs/fatfsgen/main/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
idf_component_register(SRCS "fatfsgen_example_main.c"
|
||||||
|
INCLUDE_DIRS ".")
|
||||||
|
|
||||||
|
# Create a FATFS image from the contents of the 'fatfs_long_name_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'.
|
||||||
|
# If read-only mode is set (CONFIG_EXAMPLE_FATFS_MODE_READ_ONLY)
|
||||||
|
# the generated image will be raw without wear levelling support.
|
||||||
|
# Otherwise it will support wear levelling and thus enable read-write mounting of the image in the device.
|
||||||
|
|
||||||
|
set(image ../fatfs_image)
|
||||||
|
|
||||||
|
if(CONFIG_EXAMPLE_FATFS_MODE_READ_ONLY)
|
||||||
|
fatfs_create_rawflash_image(storage ${image} FLASH_IN_PROJECT PRESERVE_TIME)
|
||||||
|
else()
|
||||||
|
fatfs_create_spiflash_image(storage ${image} FLASH_IN_PROJECT PRESERVE_TIME)
|
||||||
|
endif()
|
10
examples/storage/fatfs/fatfsgen/main/Kconfig.projbuild
Normal file
10
examples/storage/fatfs/fatfsgen/main/Kconfig.projbuild
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
menu "Example Configuration"
|
||||||
|
|
||||||
|
config EXAMPLE_FATFS_MODE_READ_ONLY
|
||||||
|
bool "Read only mode for generated FATFS image"
|
||||||
|
default y
|
||||||
|
help
|
||||||
|
If read-only mode is set, the generated fatfs image will be raw (without wear levelling support).
|
||||||
|
Otherwise it will support wear levelling that enables read-write mounting.
|
||||||
|
|
||||||
|
endmenu
|
92
examples/storage/fatfs/fatfsgen/main/fatfsgen_example_main.c
Normal file
92
examples/storage/fatfs/fatfsgen/main/fatfsgen_example_main.c
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "esp_vfs.h"
|
||||||
|
#include "esp_vfs_fat.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
static const char *TAG = "example";
|
||||||
|
|
||||||
|
// Mount path for the partition
|
||||||
|
const char *base_path = "/spiflash";
|
||||||
|
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
// To mount device we need name of device partition, define base_path
|
||||||
|
// and allow format partition in case if it is new one and was not formatted before
|
||||||
|
const esp_vfs_fat_mount_config_t mount_config = {
|
||||||
|
.max_files = 4,
|
||||||
|
.format_if_mount_failed = false,
|
||||||
|
.allocation_unit_size = CONFIG_WL_SECTOR_SIZE,
|
||||||
|
.use_one_fat = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
#if CONFIG_EXAMPLE_FATFS_MODE_READ_ONLY
|
||||||
|
ESP_LOGI(TAG, "Mounting FAT filesystem in read-only mode");
|
||||||
|
esp_err_t err = esp_vfs_fat_spiflash_mount_ro(base_path, "storage", &mount_config);
|
||||||
|
#else
|
||||||
|
ESP_LOGI(TAG, "Mounting FAT filesystem in read/write mode");
|
||||||
|
static wl_handle_t wl_handle = WL_INVALID_HANDLE;
|
||||||
|
esp_err_t err = esp_vfs_fat_spiflash_mount_rw_wl(base_path, "storage", &mount_config, &wl_handle);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to mount FATFS (%s)", esp_err_to_name(err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char line[128];
|
||||||
|
|
||||||
|
FILE *f;
|
||||||
|
char *pos;
|
||||||
|
ESP_LOGI(TAG, "Reading file");
|
||||||
|
|
||||||
|
const char *host_filename1 = "/spiflash/subdir/testlongfilenames.txt";
|
||||||
|
|
||||||
|
struct stat info;
|
||||||
|
struct tm timeinfo;
|
||||||
|
char buffer[32];
|
||||||
|
|
||||||
|
if(stat(host_filename1, &info) < 0){
|
||||||
|
ESP_LOGE(TAG, "Failed to read file stats");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
localtime_r(&info.st_mtime, &timeinfo);
|
||||||
|
strftime(buffer, sizeof(buffer), "%Y-%m-%d", &timeinfo);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "The file '%s' was modified at date: %s", host_filename1, buffer);
|
||||||
|
|
||||||
|
|
||||||
|
f = fopen(host_filename1, "rb");
|
||||||
|
if (f == NULL) {
|
||||||
|
ESP_LOGE(TAG, "Failed to open file for reading");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fgets(line, sizeof(line), f);
|
||||||
|
fclose(f);
|
||||||
|
// strip newline
|
||||||
|
pos = strchr(line, '\n');
|
||||||
|
if (pos) {
|
||||||
|
*pos = '\0';
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "Read from file: '%s'", line);
|
||||||
|
|
||||||
|
// Unmount FATFS
|
||||||
|
ESP_LOGI(TAG, "Unmounting FAT filesystem");
|
||||||
|
|
||||||
|
#if CONFIG_EXAMPLE_FATFS_MODE_READ_ONLY
|
||||||
|
ESP_ERROR_CHECK(esp_vfs_fat_spiflash_unmount_ro(base_path, "storage"));
|
||||||
|
#else
|
||||||
|
ESP_ERROR_CHECK(esp_vfs_fat_spiflash_unmount_rw_wl(base_path, wl_handle));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Done");
|
||||||
|
}
|
6
examples/storage/fatfs/fatfsgen/partitions_example.csv
Normal file
6
examples/storage/fatfs/fatfsgen/partitions_example.csv
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# Name, Type, SubType, Offset, Size, Flags
|
||||||
|
# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
|
||||||
|
nvs, data, nvs, 0x9000, 0x6000,
|
||||||
|
phy_init, data, phy, 0xf000, 0x1000,
|
||||||
|
factory, app, factory, 0x10000, 1M,
|
||||||
|
storage, data, fat, , 1M,
|
|
@@ -0,0 +1,72 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from pytest_embedded import Dut
|
||||||
|
|
||||||
|
idf_path = os.environ['IDF_PATH'] # get value of IDF_PATH from environment
|
||||||
|
parttool_dir = os.path.join(idf_path, 'components', 'partition_table')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.esp32
|
||||||
|
@pytest.mark.generic
|
||||||
|
@pytest.mark.parametrize('config', ['test_read_only_partition_gen_ln',
|
||||||
|
'test_read_write_partition_gen_ln',
|
||||||
|
], indirect=True)
|
||||||
|
def test_examples_fatfs_fatfsgen(config: str, dut: Dut) -> None:
|
||||||
|
# Expects list of strings sequentially
|
||||||
|
def expect_all(msg_list: List[str], to: int) -> None:
|
||||||
|
for msg in msg_list:
|
||||||
|
dut.expect(msg, timeout=to)
|
||||||
|
|
||||||
|
# Expects prefix string followed by date in the format 'yyyy-mm-dd'
|
||||||
|
def expect_date(prefix: str, to: int) -> datetime:
|
||||||
|
expect_str = prefix + '(\\d+)-(\\d+)-(\\d+)'
|
||||||
|
match_ = dut.expect(re.compile(str.encode(expect_str)), timeout=to)
|
||||||
|
year_ = int(match_[1].decode())
|
||||||
|
month_ = int(match_[2].decode())
|
||||||
|
day_ = int(match_[3].decode())
|
||||||
|
return datetime(year_, month_, day_)
|
||||||
|
|
||||||
|
# Calculates absolute difference in days between date_reference and date_actual.
|
||||||
|
# Raises exception if difference exceeds tolerance
|
||||||
|
def evaluate_dates(date_reference: datetime, date_actual: datetime, days_tolerance: int) -> None:
|
||||||
|
td = date_actual - date_reference
|
||||||
|
if abs(td.days) > days_tolerance:
|
||||||
|
raise Exception(f'Too big date difference. Actual: {date_actual}, reference: {date_reference}, tolerance: {days_tolerance} day(s)')
|
||||||
|
|
||||||
|
# Expect timeout
|
||||||
|
timeout = 20
|
||||||
|
|
||||||
|
# We tolerate 30 days difference between actual file creation and date when test was executed.
|
||||||
|
tolerance = 30
|
||||||
|
filename = 'sublongnames/testlongfilenames.txt'
|
||||||
|
date_ref = datetime.today()
|
||||||
|
|
||||||
|
if config in ['test_read_write_partition_gen']:
|
||||||
|
filename_expected = f'/spiflash/{filename}'
|
||||||
|
expect_all(['example: Mounting FAT filesystem',
|
||||||
|
'example: Opening file',
|
||||||
|
'example: File written',
|
||||||
|
'example: Reading file',
|
||||||
|
'example: Read from file: \'This is written by the device\'',
|
||||||
|
'example: Reading file'], timeout)
|
||||||
|
date_act = expect_date(f'The file \'{filename_expected}\' was modified at date: ', timeout)
|
||||||
|
evaluate_dates(date_ref, date_act, tolerance)
|
||||||
|
expect_all(['example: Read from file: \'This is generated on the host\'',
|
||||||
|
'example: Unmounting FAT filesystem',
|
||||||
|
'example: Done'], timeout)
|
||||||
|
|
||||||
|
elif config in ['test_read_only_partition_gen']:
|
||||||
|
filename_expected = f'/spiflash/{filename}'
|
||||||
|
expect_all(['example: Mounting FAT filesystem',
|
||||||
|
'example: Reading file'], timeout)
|
||||||
|
date_act = expect_date(f'The file \'{filename_expected}\' was modified at date: ', timeout)
|
||||||
|
evaluate_dates(date_ref, date_act, tolerance)
|
||||||
|
expect_all(['example: Read from file: \'this is test\'',
|
||||||
|
'example: Unmounting FAT filesystem',
|
||||||
|
'example: Done'], timeout)
|
@@ -0,0 +1,4 @@
|
|||||||
|
CONFIG_EXAMPLE_FATFS_MODE_READ_ONLY=y
|
||||||
|
CONFIG_FATFS_LFN_HEAP=y
|
||||||
|
CONFIG_FATFS_LFN_NONE=n
|
||||||
|
CONFIG_FATFS_LFN_STACK=n
|
@@ -0,0 +1,4 @@
|
|||||||
|
CONFIG_EXAMPLE_FATFS_MODE_READ_ONLY=n
|
||||||
|
CONFIG_FATFS_LFN_HEAP=y
|
||||||
|
CONFIG_FATFS_LFN_NONE=n
|
||||||
|
CONFIG_FATFS_LFN_STACK=n
|
4
examples/storage/fatfs/fatfsgen/sdkconfig.defaults
Normal file
4
examples/storage/fatfs/fatfsgen/sdkconfig.defaults
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||||
|
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||||
|
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv"
|
||||||
|
CONFIG_FATFS_LFN_HEAP=y
|
Reference in New Issue
Block a user