forked from espressif/esp-idf
feat(storage/fatfs): restructure advanced example
This commit is contained in:
committed by
Tomas Rohlinek
parent
85b5869053
commit
587290567d
@@ -34,14 +34,6 @@ examples/storage/ext_flash_fatfs:
|
|||||||
temporary: true
|
temporary: true
|
||||||
reason: lack of runners
|
reason: lack of runners
|
||||||
|
|
||||||
examples/storage/fatfs_advanced:
|
|
||||||
depends_components:
|
|
||||||
- fatfs
|
|
||||||
- vfs
|
|
||||||
disable_test:
|
|
||||||
- if: IDF_TARGET != "esp32"
|
|
||||||
reason: only one target needed
|
|
||||||
|
|
||||||
examples/storage/nvs_rw_blob:
|
examples/storage/nvs_rw_blob:
|
||||||
depends_components:
|
depends_components:
|
||||||
- nvs_flash
|
- nvs_flash
|
||||||
|
94
examples/storage/fatfs/fs_operations/README.md
Normal file
94
examples/storage/fatfs/fs_operations/README.md
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
|
||||||
|
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | -------- |
|
||||||
|
|
||||||
|
# FATFS Filesystem Operations Example
|
||||||
|
|
||||||
|
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||||
|
|
||||||
|
This example demonstrates some of the POSIX functions available for working with the FATFS filesystem.
|
||||||
|
Including basic read and write operations, as well as creating moving, and deleting files and directories.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
1. Partition labeled `storage` is mounted (and formatted if necessary) as FATFS filesystem to `/spiflash` mountpoint.
|
||||||
|
|
||||||
|
2. All existing files and directories in the root directory are deleted.
|
||||||
|
|
||||||
|
3. File `hello.txt` is created and written to.
|
||||||
|
|
||||||
|
4. File `hello.txt` is inspected using `fstat` function showing file size and last modification time.
|
||||||
|
|
||||||
|
5. File `hello.txt` is written to again, appending to the end of the file.
|
||||||
|
|
||||||
|
6. File `hello.txt` is read from and the contents are printed to the console.
|
||||||
|
|
||||||
|
7. New directory `new_dir` is created.
|
||||||
|
|
||||||
|
8. All files and directories in the root directory are listed.
|
||||||
|
|
||||||
|
9. File `hello.txt` is moved and renamed to `new_dir/hello_renamed.txt`.
|
||||||
|
|
||||||
|
## 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 (323) example: Mounting FAT filesystem
|
||||||
|
I (333) example: Deleting everything in /spiflash:
|
||||||
|
I (333) example: Deleting everything in /spiflash/new_dir:
|
||||||
|
I (673) example: Creating a file
|
||||||
|
I (683) example: Writing to the file
|
||||||
|
I (733) example: File stats:
|
||||||
|
File size: 13 bytes
|
||||||
|
File modification time: Thu Jan 1 00:00:00 1970
|
||||||
|
|
||||||
|
I (743) example: Wait for 1 seconds
|
||||||
|
I (1743) example: Write more to the file
|
||||||
|
I (1743) example: File stats:
|
||||||
|
File size: 26 bytes
|
||||||
|
File modification time: Thu Jan 1 00:00:00 1970
|
||||||
|
|
||||||
|
I (1743) example: Go to the beginning of the file
|
||||||
|
I (1753) example: Reading from file:
|
||||||
|
Hello World!
|
||||||
|
Hello World!
|
||||||
|
|
||||||
|
I (1753) example: Closing file
|
||||||
|
I (1993) example: Listing files in /spiflash:
|
||||||
|
/spiflash:
|
||||||
|
file : hello.txt
|
||||||
|
I (1993) example: Creating a new directory
|
||||||
|
I (2383) example: Listing files in /spiflash:
|
||||||
|
/spiflash:
|
||||||
|
file : hello.txt
|
||||||
|
directory: new_dir
|
||||||
|
I (2383) example: Rename a file
|
||||||
|
I (2503) example: Listing files in /spiflash:
|
||||||
|
/spiflash:
|
||||||
|
directory: new_dir
|
||||||
|
I (2503) example: Listing files in /spiflash/new_dir:
|
||||||
|
/spiflash/new_dir:
|
||||||
|
file : hello_renamed.txt
|
||||||
|
I (2513) example: Unmounting FAT filesystem
|
||||||
|
I (2643) example: Done
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
The logic of the example is contained in a [single source file](./main/fatfs_fs_operations_example_main.c),
|
||||||
|
and it should be relatively simple to match points in its execution with the log outputs above.
|
2
examples/storage/fatfs/fs_operations/main/CMakeLists.txt
Normal file
2
examples/storage/fatfs/fs_operations/main/CMakeLists.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
idf_component_register(SRCS "fatfs_fs_operations_example_main.c"
|
||||||
|
INCLUDE_DIRS ".")
|
@@ -0,0 +1,207 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/errno.h>
|
||||||
|
#include "esp_vfs.h"
|
||||||
|
#include "esp_vfs_fat.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
static const char *TAG = "example";
|
||||||
|
|
||||||
|
// Mount path for the partition
|
||||||
|
static const char *base_path = "/spiflash";
|
||||||
|
|
||||||
|
// File name
|
||||||
|
static const char *filename = "/spiflash/hello.txt";
|
||||||
|
|
||||||
|
// Function to dump contents of a directory
|
||||||
|
static void list_dir(const char *path);
|
||||||
|
|
||||||
|
// Best effort recursive function to clean a directory
|
||||||
|
static void clean_dir(const char *path);
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Mounting FAT filesystem");
|
||||||
|
|
||||||
|
// 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 = true,
|
||||||
|
.allocation_unit_size = CONFIG_WL_SECTOR_SIZE,
|
||||||
|
.use_one_fat = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
wl_handle_t wl_handle = WL_INVALID_HANDLE;
|
||||||
|
|
||||||
|
esp_err_t err = ESP_OK;
|
||||||
|
|
||||||
|
err = esp_vfs_fat_spiflash_mount_rw_wl(base_path, "storage", &mount_config, &wl_handle);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "Failed to mount FATFS (%s)", esp_err_to_name(err));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the working directory is empty
|
||||||
|
clean_dir(base_path);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Creating a file");
|
||||||
|
|
||||||
|
// Unlike C standard library which uses FILE*, POSIX API uses file descriptors for file operations
|
||||||
|
int fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0);
|
||||||
|
if (fd < 0) {
|
||||||
|
ESP_LOGE(TAG, "Failed to open file for writing");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Writing to the file");
|
||||||
|
const char *text = "Hello World!\n";
|
||||||
|
write(fd, text, strlen(text));
|
||||||
|
|
||||||
|
struct stat info;
|
||||||
|
// We have to use `stat` instead of `fstat`, because `fstat` currently isn't fully supported
|
||||||
|
if (stat(filename, &info) < 0) {
|
||||||
|
ESP_LOGE(TAG, "Failed to stat file: %s", strerror(errno));
|
||||||
|
close(fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(
|
||||||
|
TAG,
|
||||||
|
"File stats:\n"
|
||||||
|
"\tFile size: %ld bytes\n"
|
||||||
|
"\tFile modification time: %s",
|
||||||
|
info.st_size,
|
||||||
|
ctime(&info.st_mtime)
|
||||||
|
);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Wait for 3 seconds");
|
||||||
|
sleep(3);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Write more to the file");
|
||||||
|
write(fd, text, strlen(text));
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Force cached data and metadata to the filesystem");
|
||||||
|
fsync(fd);
|
||||||
|
|
||||||
|
if (stat(filename, &info) < 0) {
|
||||||
|
ESP_LOGE(TAG, "Failed to stat file: %s", strerror(errno));
|
||||||
|
close(fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(
|
||||||
|
TAG,
|
||||||
|
"File stats:\n"
|
||||||
|
"\tFile size: %ld bytes\n"
|
||||||
|
"\tFile modification time: %s",
|
||||||
|
info.st_size,
|
||||||
|
ctime(&info.st_mtime)
|
||||||
|
);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Go to the beginning of the file");
|
||||||
|
lseek(fd, 0, SEEK_SET);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Reading from file:");
|
||||||
|
|
||||||
|
char buf[128] = {0};
|
||||||
|
|
||||||
|
ssize_t len = read(fd, buf, sizeof(buf) - 1);
|
||||||
|
if (len < 0) {
|
||||||
|
ESP_LOGE(TAG, "Failed to read file: %s", strerror(errno));
|
||||||
|
close(fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%.*s\n", len, buf);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Closing file");
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
// List files in the directory
|
||||||
|
list_dir(base_path);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Creating a new directory");
|
||||||
|
if (mkdir("/spiflash/new_dir", 0777) < 0) {
|
||||||
|
ESP_LOGE(TAG, "Failed to create a new directory: %s", strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// List files in the directory
|
||||||
|
list_dir(base_path);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Rename a file");
|
||||||
|
|
||||||
|
if (rename(filename, "/spiflash/new_dir/hello_renamed.txt") < 0) {
|
||||||
|
ESP_LOGE(TAG, "Failed to rename file: %s", strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// List files in the directory
|
||||||
|
list_dir(base_path);
|
||||||
|
list_dir("/spiflash/new_dir");
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Unmounting FAT filesystem");
|
||||||
|
ESP_ERROR_CHECK(esp_vfs_fat_spiflash_unmount_rw_wl(base_path, wl_handle));
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Done");
|
||||||
|
}
|
||||||
|
|
||||||
|
void list_dir(const char *path)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Listing files in %s:", path);
|
||||||
|
|
||||||
|
DIR *dir = opendir(path);
|
||||||
|
if (!dir) {
|
||||||
|
ESP_LOGE(TAG, "Failed to open directory: %s", strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%s:\n", path);
|
||||||
|
struct dirent *entry;
|
||||||
|
while ((entry = readdir(dir)) != NULL) {
|
||||||
|
printf(
|
||||||
|
" %s: %s\n",
|
||||||
|
(entry->d_type == DT_DIR)
|
||||||
|
? "directory"
|
||||||
|
: "file ",
|
||||||
|
entry->d_name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clean_dir(const char *path)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Deleting everything in %s:", path);
|
||||||
|
|
||||||
|
DIR *dir = opendir(path);
|
||||||
|
if (!dir) {
|
||||||
|
ESP_LOGE(TAG, "Failed to open directory: %s", strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dirent *entry;
|
||||||
|
while ((entry = readdir(dir)) != NULL) {
|
||||||
|
char full_path[64] = {0};
|
||||||
|
snprintf(full_path, sizeof(full_path), "%.20s/%.40s", path, entry->d_name);
|
||||||
|
if (entry->d_type == DT_DIR)
|
||||||
|
clean_dir(full_path);
|
||||||
|
if (remove(full_path) != 0) {
|
||||||
|
ESP_LOGE(TAG, "Failed to remove %s: %s", full_path, strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(dir);
|
||||||
|
}
|
@@ -0,0 +1,80 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from pytest_embedded import Dut
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.esp32
|
||||||
|
@pytest.mark.generic
|
||||||
|
def test_examples_fatfs_fs_operations(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)
|
||||||
|
|
||||||
|
def parse_date() -> datetime:
|
||||||
|
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
|
||||||
|
|
||||||
|
pattern = r'([A-Z][a-z]{2}) ([A-Z][a-z]{2}) ([ \d]\d) (\d{2}):(\d{2}):(\d{2}) (\d{4})'
|
||||||
|
|
||||||
|
match = dut.expect(pattern)
|
||||||
|
return datetime(
|
||||||
|
month=months.index(match[2].decode('utf-8')) + 1,
|
||||||
|
day=int(match[3]),
|
||||||
|
hour=int(match[4]),
|
||||||
|
minute=int(match[5]),
|
||||||
|
second=int(match[6]),
|
||||||
|
year=int(match[7]),
|
||||||
|
)
|
||||||
|
|
||||||
|
expect_all(
|
||||||
|
[
|
||||||
|
'example: Mounting FAT filesystem',
|
||||||
|
'example: Creating a file',
|
||||||
|
'example: Writing to the file',
|
||||||
|
'example: File stats:',
|
||||||
|
'File size:',
|
||||||
|
],
|
||||||
|
5
|
||||||
|
)
|
||||||
|
|
||||||
|
original = parse_date()
|
||||||
|
|
||||||
|
expect_all(
|
||||||
|
[
|
||||||
|
'example: Wait for 3 seconds',
|
||||||
|
'example: Write more to the file',
|
||||||
|
'example: Force cached data and metadata to the filesystem',
|
||||||
|
'File size:',
|
||||||
|
],
|
||||||
|
5
|
||||||
|
)
|
||||||
|
|
||||||
|
updated = parse_date()
|
||||||
|
|
||||||
|
assert updated > original
|
||||||
|
|
||||||
|
expect_all(
|
||||||
|
[
|
||||||
|
'example: Go to the beginning of the file',
|
||||||
|
'example: Reading from file',
|
||||||
|
'Hello World!',
|
||||||
|
'Hello World!',
|
||||||
|
'example: Closing file',
|
||||||
|
'example: Listing files in /spiflash:',
|
||||||
|
'hello.txt',
|
||||||
|
'example: Creating a new directory',
|
||||||
|
'example: Listing files in /spiflash:',
|
||||||
|
'hello.txt',
|
||||||
|
'new_dir',
|
||||||
|
'example: Rename a file',
|
||||||
|
'example: Listing files in /spiflash:',
|
||||||
|
'new_dir',
|
||||||
|
'example: Listing files in /spiflash/new_dir:',
|
||||||
|
'hello_renamed.txt',
|
||||||
|
],
|
||||||
|
5
|
||||||
|
)
|
@@ -1,69 +0,0 @@
|
|||||||
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | 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` as well as usage of long file names for FATFS.
|
|
||||||
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_image` from which the FATFS filesystem image will be created.
|
|
||||||
|
|
||||||
2. The function `fatfs_create_rawflash_image` 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.
|
|
@@ -1 +0,0 @@
|
|||||||
This is generated on the host; it has long name
|
|
@@ -1 +0,0 @@
|
|||||||
this is test; it has long name
|
|
@@ -1,26 +0,0 @@
|
|||||||
idf_component_register(SRCS "fatfs_advanced_example_main.c"
|
|
||||||
INCLUDE_DIRS ".")
|
|
||||||
|
|
||||||
# Create a FATFS image from the contents of the 'fatfs_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_long_name_image)
|
|
||||||
|
|
||||||
if(CONFIG_EXAMPLE_FATFS_MODE_READ_ONLY)
|
|
||||||
if(CONFIG_EXAMPLE_FATFS_DEFAULT_DATETIME)
|
|
||||||
fatfs_create_rawflash_image(storage ${image} FLASH_IN_PROJECT)
|
|
||||||
else()
|
|
||||||
fatfs_create_rawflash_image(storage ${image} FLASH_IN_PROJECT PRESERVE_TIME)
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
if(CONFIG_EXAMPLE_FATFS_DEFAULT_DATETIME)
|
|
||||||
fatfs_create_spiflash_image(storage ${image} FLASH_IN_PROJECT)
|
|
||||||
else()
|
|
||||||
fatfs_create_spiflash_image(storage ${image} FLASH_IN_PROJECT PRESERVE_TIME)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
@@ -1,24 +0,0 @@
|
|||||||
menu "Example Configuration"
|
|
||||||
|
|
||||||
config EXAMPLE_FATFS_MODE_READ_ONLY
|
|
||||||
bool "Read only mode for generated FATFS image"
|
|
||||||
default n
|
|
||||||
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.
|
|
||||||
|
|
||||||
config EXAMPLE_FATFS_WRITE_COUNT
|
|
||||||
int "Number of volumes"
|
|
||||||
default 1
|
|
||||||
range 1 600
|
|
||||||
help
|
|
||||||
Number of writes to the file (for testing purposes).
|
|
||||||
|
|
||||||
config EXAMPLE_FATFS_DEFAULT_DATETIME
|
|
||||||
bool "Default modification date and time for generated FATFS image"
|
|
||||||
default n
|
|
||||||
help
|
|
||||||
If default datetime is set, all files created in the generated FATFS partition have default time
|
|
||||||
equal to FATFS origin time (1 January 1980)
|
|
||||||
|
|
||||||
endmenu
|
|
@@ -1,138 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 <sys/errno.h>
|
|
||||||
#include "esp_vfs.h"
|
|
||||||
#include "esp_vfs_fat.h"
|
|
||||||
#include "sdkconfig.h"
|
|
||||||
|
|
||||||
static const char *TAG = "example";
|
|
||||||
|
|
||||||
// Mount path for the partition
|
|
||||||
const char *base_path = "/spiflash";
|
|
||||||
|
|
||||||
void write_file(const char *filename, const char *data)
|
|
||||||
{
|
|
||||||
ESP_LOGI(TAG, "Opening file '%s' for writing", filename);
|
|
||||||
|
|
||||||
FILE *f = fopen(filename, "w");
|
|
||||||
if (f == NULL) {
|
|
||||||
ESP_LOGE(TAG, "Failed to open file for writing: %s", strerror(errno));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Writing to file");
|
|
||||||
|
|
||||||
fputs(data, f);
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "File written");
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Closing file");
|
|
||||||
fclose(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void read_file(const char *filename)
|
|
||||||
{
|
|
||||||
ESP_LOGI(TAG, "Opening file '%s' for reading", filename);
|
|
||||||
|
|
||||||
FILE *f = fopen(filename, "r");
|
|
||||||
if (f == NULL) {
|
|
||||||
ESP_LOGE(TAG, "Failed to open file for reading: %s", strerror(errno));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Reading from file");
|
|
||||||
|
|
||||||
char line[128];
|
|
||||||
fgets(line, sizeof(line), f);
|
|
||||||
|
|
||||||
// strip newline
|
|
||||||
char *pos = strchr(line, '\n');
|
|
||||||
if (pos) {
|
|
||||||
*pos = '\0'; // strip newline
|
|
||||||
}
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Read from file: '%s'", line);
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Closing file '%s'", filename);
|
|
||||||
fclose(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void stat_file(const char *filename)
|
|
||||||
{
|
|
||||||
|
|
||||||
struct stat info;
|
|
||||||
struct tm timeinfo;
|
|
||||||
char buffer[32];
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Stating file '%s' for modification time", filename);
|
|
||||||
|
|
||||||
if(stat(filename, &info) < 0){
|
|
||||||
ESP_LOGE(TAG, "Failed to read file stats: %s", strerror(errno));
|
|
||||||
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", filename, buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void app_main(void)
|
|
||||||
{
|
|
||||||
ESP_LOGI(TAG, "Mounting FAT filesystem");
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
};
|
|
||||||
|
|
||||||
esp_err_t err;
|
|
||||||
|
|
||||||
#ifdef CONFIG_EXAMPLE_FATFS_MODE_READ_ONLY
|
|
||||||
err = esp_vfs_fat_spiflash_mount_ro(base_path, "storage", &mount_config);
|
|
||||||
#else // CONFIG_EXAMPLE_FATFS_MODE_READ_ONLY
|
|
||||||
|
|
||||||
// Handle of the wear levelling library instance
|
|
||||||
wl_handle_t wl_handle = WL_INVALID_HANDLE;
|
|
||||||
err = esp_vfs_fat_spiflash_mount_rw_wl(base_path, "storage", &mount_config, &wl_handle);
|
|
||||||
#endif // CONFIG_EXAMPLE_FATFS_MODE_READ_ONLY
|
|
||||||
|
|
||||||
if (err != ESP_OK) {
|
|
||||||
ESP_LOGE(TAG, "Failed to mount FATFS (%s)python $IDF_PATH/tools/ci/ci_build_apps.py . --target esp32 -vv --pytest-apps", esp_err_to_name(err));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read contents of a file
|
|
||||||
read_file("/spiflash/filegeneratedonhost.txt");
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef CONFIG_EXAMPLE_FATFS_MODE_READ_ONLY
|
|
||||||
// Create and write to a file
|
|
||||||
write_file("/spiflash/messagefromthedevice.txt", "This is written by the device");
|
|
||||||
read_file("/spiflash/messagefromthedevice.txt");
|
|
||||||
#endif // CONFIG_EXAMPLE_FATFS_MODE_READ_ONLY
|
|
||||||
|
|
||||||
// Check when the file was last modified
|
|
||||||
stat_file("/spiflash/subdirectoryfromhost/innerfile.txt");
|
|
||||||
|
|
||||||
// Unmount FATFS
|
|
||||||
ESP_LOGI(TAG, "Unmounting FAT filesystem");
|
|
||||||
|
|
||||||
#ifdef CONFIG_EXAMPLE_FATFS_MODE_READ_ONLY
|
|
||||||
ESP_ERROR_CHECK(esp_vfs_fat_spiflash_unmount_ro(base_path, "storage"));
|
|
||||||
#else // CONFIG_EXAMPLE_FATFS_MODE_READ_ONLY
|
|
||||||
ESP_ERROR_CHECK(esp_vfs_fat_spiflash_unmount_rw_wl(base_path, wl_handle));
|
|
||||||
#endif // CONFIG_EXAMPLE_FATFS_MODE_READ_ONLY
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Done");
|
|
||||||
}
|
|
@@ -1,112 +0,0 @@
|
|||||||
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
|
||||||
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
|
||||||
import re
|
|
||||||
from datetime import datetime
|
|
||||||
from typing import List
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
from pytest_embedded import Dut
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.esp32
|
|
||||||
@pytest.mark.generic
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
'config',
|
|
||||||
[
|
|
||||||
'test_read_only_partition_gen',
|
|
||||||
'test_read_only_partition_gen_default_dt',
|
|
||||||
'test_read_write_partition_gen',
|
|
||||||
'test_read_write_partition_gen_default_dt',
|
|
||||||
],
|
|
||||||
indirect=True,
|
|
||||||
)
|
|
||||||
def test_examples_fatfs_advanced(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
|
|
||||||
|
|
||||||
expected_date = (
|
|
||||||
datetime(1980, 1, 1) if config.endswith('default_dt') else datetime.today()
|
|
||||||
)
|
|
||||||
|
|
||||||
base_path = '/spiflash/'
|
|
||||||
folder_name = 'subdirectoryfromhost/'
|
|
||||||
read_filename = base_path + 'filegeneratedonhost.txt'
|
|
||||||
write_filename = base_path + 'messagefromthedevice.txt'
|
|
||||||
stat_filename = base_path + folder_name + 'innerfile.txt'
|
|
||||||
|
|
||||||
dut.expect('example: Mounting FAT filesystem', timeout=timeout)
|
|
||||||
|
|
||||||
# Check read
|
|
||||||
expect_all(
|
|
||||||
[
|
|
||||||
f'example: Opening file \'{read_filename}\' for reading',
|
|
||||||
f'example: Reading from file',
|
|
||||||
'example: Read from file: \'This is generated on the host; it has long name\'',
|
|
||||||
f'example: Closing file \'{read_filename}\'',
|
|
||||||
],
|
|
||||||
timeout,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Check write
|
|
||||||
if config.startswith('test_read_write'):
|
|
||||||
expect_all(
|
|
||||||
[
|
|
||||||
f'example: Opening file \'{write_filename}\' for writing',
|
|
||||||
'example: Writing to file',
|
|
||||||
'example: File written',
|
|
||||||
'example: Closing file',
|
|
||||||
f'example: Opening file \'{write_filename}\' for reading',
|
|
||||||
f'example: Reading from file',
|
|
||||||
'example: Read from file: \'This is written by the device\'',
|
|
||||||
f'example: Closing file \'{write_filename}\'',
|
|
||||||
],
|
|
||||||
timeout,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Check date using stat
|
|
||||||
dut.expect(
|
|
||||||
f'example: Stating file \'{stat_filename}\' for modification time', timeout=timeout
|
|
||||||
)
|
|
||||||
|
|
||||||
actual_date = expect_date(
|
|
||||||
f'The file \'{stat_filename}\' was modified at date: ', timeout
|
|
||||||
)
|
|
||||||
|
|
||||||
evaluate_dates(expected_date, actual_date, tolerance)
|
|
||||||
|
|
||||||
# Finish
|
|
||||||
expect_all(
|
|
||||||
[
|
|
||||||
'example: Unmounting FAT filesystem',
|
|
||||||
'example: Done',
|
|
||||||
],
|
|
||||||
timeout,
|
|
||||||
)
|
|
@@ -1,5 +0,0 @@
|
|||||||
CONFIG_EXAMPLE_FATFS_MODE_READ_ONLY=y
|
|
||||||
CONFIG_FATFS_LFN_HEAP=y
|
|
||||||
CONFIG_FATFS_LFN_STACK=n
|
|
||||||
CONFIG_FATFS_LFN_NONE=n
|
|
||||||
CONFIG_EXAMPLE_FATFS_WRITE_COUNT=300
|
|
@@ -1,6 +0,0 @@
|
|||||||
CONFIG_EXAMPLE_FATFS_MODE_READ_ONLY=y
|
|
||||||
CONFIG_FATFS_LFN_HEAP=y
|
|
||||||
CONFIG_FATFS_LFN_STACK=n
|
|
||||||
CONFIG_FATFS_LFN_NONE=n
|
|
||||||
CONFIG_EXAMPLE_FATFS_DEFAULT_DATETIME=y
|
|
||||||
CONFIG_EXAMPLE_FATFS_WRITE_COUNT=300
|
|
@@ -1,5 +0,0 @@
|
|||||||
CONFIG_EXAMPLE_FATFS_MODE_READ_ONLY=n
|
|
||||||
CONFIG_FATFS_LFN_HEAP=y
|
|
||||||
CONFIG_FATFS_LFN_STACK=n
|
|
||||||
CONFIG_FATFS_LFN_NONE=n
|
|
||||||
CONFIG_EXAMPLE_FATFS_WRITE_COUNT=300
|
|
@@ -1,6 +0,0 @@
|
|||||||
CONFIG_EXAMPLE_FATFS_MODE_READ_ONLY=n
|
|
||||||
CONFIG_FATFS_LFN_HEAP=y
|
|
||||||
CONFIG_FATFS_LFN_STACK=n
|
|
||||||
CONFIG_FATFS_LFN_NONE=n
|
|
||||||
CONFIG_EXAMPLE_FATFS_DEFAULT_DATETIME=y
|
|
||||||
CONFIG_EXAMPLE_FATFS_WRITE_COUNT=300
|
|
Reference in New Issue
Block a user