From b9a54fb1f3f9fc992f5db142b30f130212d6c156 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Rohl=C3=ADnek?= Date: Wed, 29 May 2024 10:10:38 +0200 Subject: [PATCH] feat(storage/fatfs): add minimal FatFS example --- examples/storage/.build-test-rules.yml | 8 ++ examples/storage/README.md | 1 + examples/storage/fatfs_basic/CMakeLists.txt | 6 ++ examples/storage/fatfs_basic/README.md | 43 +++++++++ .../storage/fatfs_basic/main/CMakeLists.txt | 2 + .../fatfs_basic/main/fat_example_main.c | 87 +++++++++++++++++++ .../fatfs_basic/partitions_example.csv | 6 ++ .../storage/fatfs_basic/pytest_fat_example.py | 17 ++++ .../storage/fatfs_basic/sdkconfig.defaults | 4 + 9 files changed, 174 insertions(+) create mode 100644 examples/storage/fatfs_basic/CMakeLists.txt create mode 100644 examples/storage/fatfs_basic/README.md create mode 100644 examples/storage/fatfs_basic/main/CMakeLists.txt create mode 100644 examples/storage/fatfs_basic/main/fat_example_main.c create mode 100644 examples/storage/fatfs_basic/partitions_example.csv create mode 100644 examples/storage/fatfs_basic/pytest_fat_example.py create mode 100644 examples/storage/fatfs_basic/sdkconfig.defaults diff --git a/examples/storage/.build-test-rules.yml b/examples/storage/.build-test-rules.yml index 341e0f595a..a15e95e333 100644 --- a/examples/storage/.build-test-rules.yml +++ b/examples/storage/.build-test-rules.yml @@ -34,6 +34,14 @@ examples/storage/ext_flash_fatfs: temporary: true reason: lack of runners +examples/storage/fatfs_basic: + depends_components: + - fatfs + - vfs + disable_test: + - if: IDF_TARGET != "esp32" + reason: only one target needed + examples/storage/nvs_rw_blob: depends_components: - nvs_flash diff --git a/examples/storage/README.md b/examples/storage/README.md index 4705e72615..ade0b883a1 100644 --- a/examples/storage/README.md +++ b/examples/storage/README.md @@ -7,6 +7,7 @@ This directory contains a range of examples ESP-IDF projects. These are intended The examples are grouped into sub-directories by category. Each category directory contains one or more example projects: +* `fatfs_basic` minimal example of FatFS usage on SPI FLASH * `custom_flash_driver` example demonstrates how to implement your own flash chip driver by overriding the default driver. * `emmc` example demonstrates how to use an eMMC chip with an ESP device. * `ext_flash_fatfs` example demonstrates how to use FATFS partition with external SPI FLASH chip. diff --git a/examples/storage/fatfs_basic/CMakeLists.txt b/examples/storage/fatfs_basic/CMakeLists.txt new file mode 100644 index 0000000000..1c0c2b8a1a --- /dev/null +++ b/examples/storage/fatfs_basic/CMakeLists.txt @@ -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) diff --git a/examples/storage/fatfs_basic/README.md b/examples/storage/fatfs_basic/README.md new file mode 100644 index 0000000000..0ee84f105d --- /dev/null +++ b/examples/storage/fatfs_basic/README.md @@ -0,0 +1,43 @@ +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | -------- | + +# FATFS minimal example + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +This example demonstrates the minimal setup required to store persistent data on SPI Flash using the FAT filesystem. +Beware that the minimal required size of the flash is 4 MB. + +## 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 (339) example: Mounting FAT filesystem +I (339) example: Filesystem mounted +I (339) example: Opening file +I (729) example: File written +I (729) example: Reading file +I (739) example: Read from file: 'This is written by the device' +I (739) example: Unmounting FAT filesystem +I (849) example: Done +``` + +The logic of the example is contained in a [single source file](./main/fat_example_main.c), +and it should be relatively simple to match points in its execution with the log outputs above. diff --git a/examples/storage/fatfs_basic/main/CMakeLists.txt b/examples/storage/fatfs_basic/main/CMakeLists.txt new file mode 100644 index 0000000000..827c3c2e8e --- /dev/null +++ b/examples/storage/fatfs_basic/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "fat_example_main.c" + INCLUDE_DIRS ".") diff --git a/examples/storage/fatfs_basic/main/fat_example_main.c b/examples/storage/fatfs_basic/main/fat_example_main.c new file mode 100644 index 0000000000..2a57821e70 --- /dev/null +++ b/examples/storage/fatfs_basic/main/fat_example_main.c @@ -0,0 +1,87 @@ +/* + * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include +#include +#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"; + +// Handle of the wear levelling library instance +static wl_handle_t s_wl_handle = WL_INVALID_HANDLE; + +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, // Number of files that can be open at a time + .format_if_mount_failed = true, // If true, try to format the partition if mount fails + .allocation_unit_size = CONFIG_WL_SECTOR_SIZE, // Size of allocation unit, cluster size. + .use_one_fat = false, // Use only one FAT table (reduce memory usage), but decrease reliability of file system in case of power failure. + }; + + esp_err_t err = esp_vfs_fat_spiflash_mount_rw_wl(base_path, "storage", &mount_config, &s_wl_handle); + + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to mount FATFS (%s)", esp_err_to_name(err)); + return; + } + + ESP_LOGI(TAG, "Filesystem mounted"); + + ESP_LOGI(TAG, "Opening file"); + + const char *filename = "/spiflash/example.txt"; + + FILE *f = fopen(filename, "wb"); + if (f == NULL) { + perror("fopen"); + ESP_LOGE(TAG, "Failed to open file for writing"); + return; + } + + fprintf(f, "This is written by the device"); + fclose(f); + + ESP_LOGI(TAG, "File written"); + + // Open file for reading + ESP_LOGI(TAG, "Reading file"); + + f = fopen(filename, "r"); + 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); + + // Unmount FATFS + ESP_LOGI(TAG, "Unmounting FAT filesystem"); + + ESP_ERROR_CHECK(esp_vfs_fat_spiflash_unmount_rw_wl(base_path, s_wl_handle)); + + ESP_LOGI(TAG, "Done"); +} diff --git a/examples/storage/fatfs_basic/partitions_example.csv b/examples/storage/fatfs_basic/partitions_example.csv new file mode 100644 index 0000000000..1c79321a10 --- /dev/null +++ b/examples/storage/fatfs_basic/partitions_example.csv @@ -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, diff --git a/examples/storage/fatfs_basic/pytest_fat_example.py b/examples/storage/fatfs_basic/pytest_fat_example.py new file mode 100644 index 0000000000..7abafe062d --- /dev/null +++ b/examples/storage/fatfs_basic/pytest_fat_example.py @@ -0,0 +1,17 @@ +# SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Unlicense OR CC0-1.0 +import pytest +from pytest_embedded import Dut + + +@pytest.mark.esp32 +@pytest.mark.generic +def test_examples_fatfs_basic(dut: Dut) -> None: + dut.expect('example: Mounting FAT filesystem', timeout=90) + dut.expect('example: Filesystem mounted', timeout=90) + dut.expect('example: Opening file', timeout=90) + dut.expect('example: File written', timeout=90) + dut.expect('example: Reading file', timeout=90) + dut.expect('example: Read from file: \'This is written by the device\'', timeout=90) + dut.expect('example: Unmounting FAT filesystem', timeout=90) + dut.expect('example: Done', timeout=90) diff --git a/examples/storage/fatfs_basic/sdkconfig.defaults b/examples/storage/fatfs_basic/sdkconfig.defaults new file mode 100644 index 0000000000..47363c32d5 --- /dev/null +++ b/examples/storage/fatfs_basic/sdkconfig.defaults @@ -0,0 +1,4 @@ +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv" +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y