mirror of
				https://github.com/espressif/esp-idf.git
				synced 2025-10-30 20:51:41 +00:00 
			
		
		
		
	feat(storage/fatfs): restructure fatfsgen example
This commit is contained in:
		 Tomáš Rohlínek
					Tomáš Rohlínek
				
			
				
					committed by
					
						 Tomas Rohlinek
						Tomas Rohlinek
					
				
			
			
				
	
			
			
			 Tomas Rohlinek
						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