From 924860a78f389a8ff4c912d3eb8f6e5181d1059c Mon Sep 17 00:00:00 2001 From: sheinz Date: Fri, 15 Jul 2016 15:22:03 +0300 Subject: [PATCH] SPIFFS: Update example, README.md Separate method to initialize SPIFFS memory buffers. REDME.md for spiffs component. Simplify spiffs example. --- examples/posix_fs/posix_fs_example.c | 3 +- examples/spiffs/Makefile | 2 +- examples/spiffs/spiffs_example.c | 194 ++++++++------------------- extras/spiffs/README.md | 153 +++++++++++++++++++++ extras/spiffs/esp_spiffs.c | 71 ++++++---- extras/spiffs/esp_spiffs.h | 25 +++- 6 files changed, 277 insertions(+), 171 deletions(-) create mode 100644 extras/spiffs/README.md diff --git a/examples/posix_fs/posix_fs_example.c b/examples/posix_fs/posix_fs_example.c index 4525be3..2f5ad14 100644 --- a/examples/posix_fs/posix_fs_example.c +++ b/examples/posix_fs/posix_fs_example.c @@ -18,8 +18,9 @@ static fs_time_t get_current_time() void test_task(void *pvParameters) { + esp_spiffs_init(); esp_spiffs_mount(); - esp_spiffs_unmount(); // FS must be unmounted before formating + SPIFFS_unmount(&fs); // FS must be unmounted before formating if (SPIFFS_format(&fs) == SPIFFS_OK) { printf("Format complete\n"); } else { diff --git a/examples/spiffs/Makefile b/examples/spiffs/Makefile index b0be23f..7327707 100644 --- a/examples/spiffs/Makefile +++ b/examples/spiffs/Makefile @@ -4,7 +4,7 @@ FLASH_SIZE = 32 # spiffs configuration SPIFFS_BASE_ADDR = 0x200000 -SPIFFS_SIZE = 0x100000 +SPIFFS_SIZE = 0x010000 include ../../common.mk diff --git a/examples/spiffs/spiffs_example.c b/examples/spiffs/spiffs_example.c index 226ee51..98decaf 100644 --- a/examples/spiffs/spiffs_example.c +++ b/examples/spiffs/spiffs_example.c @@ -4,171 +4,95 @@ #include "task.h" #include "esp8266.h" +#include "fcntl.h" +#include "unistd.h" + #include "spiffs.h" #include "esp_spiffs.h" -#define TEST_FILE_NAME_LEN 16 -#define TEST_FILES 32 -#define TEST_FILE_MAX_SIZE 8192 - -typedef struct { - char name[TEST_FILE_NAME_LEN]; - uint16_t size; - uint8_t first_data_byte; -} TestFile; - -static TestFile test_files[TEST_FILES]; - -inline static void fill_test_data(uint8_t *src, uint16_t size, uint8_t first_byte) +static void example_read_file_posix() { - while (size--) { - *src++ = first_byte++; - } -} + const int buf_size = 0xFF; + uint8_t buf[buf_size]; -static bool write_test_files() -{ - uint8_t *buf = (uint8_t*)malloc(TEST_FILE_MAX_SIZE); - bool result = true; - - for (uint8_t i = 0; i < TEST_FILES; i++) { - sprintf(test_files[i].name, "file_%d.dat", i); - spiffs_file f = SPIFFS_open(&fs, test_files[i].name, - SPIFFS_CREAT|SPIFFS_RDWR|SPIFFS_TRUNC, 0); - if (f < 0) { - printf("Open file operation failed\n"); - result = false; - break; - } - test_files[i].size = rand() % TEST_FILE_MAX_SIZE; - test_files[i].first_data_byte = rand() % 256; - fill_test_data(buf, test_files[i].size, test_files[i].first_data_byte); - - printf("Writing file %s size=%d\n", test_files[i].name, - test_files[i].size); - int32_t written = SPIFFS_write(&fs, f, buf, test_files[i].size); - if (written != test_files[i].size) { - printf("Write file operation failed, written=%d\n", written); - result = false; - break; - } - SPIFFS_close(&fs, f); - } - free(buf); - return result; -} - -inline static bool verify_test_data(uint8_t *data, uint16_t size, - uint8_t first_byte) -{ - while (size--) { - if (*data++ != first_byte++) { - return false; - } - } - return true; -} - -static bool verify_test_files() -{ - uint8_t *buf = (uint8_t*)malloc(TEST_FILE_MAX_SIZE); - bool result = true; - - for (uint8_t i = 0; i < TEST_FILES; i++) { - printf("Verifying file %s\n", test_files[i].name); - spiffs_file f = SPIFFS_open(&fs, test_files[i].name, SPIFFS_RDONLY, 0); - if (f < 0) { - printf("Open file operation failed\n"); - result = false; - break; - } - - int32_t n = SPIFFS_read(&fs, f, buf, test_files[i].size); - if (n != test_files[i].size) { - printf("Read file operation failed\n"); - result = false; - break; - } - - if (!verify_test_data(buf, test_files[i].size, - test_files[i].first_data_byte)) { - printf("Data verification failed\n"); - result = false; - break; - } - - SPIFFS_close(&fs, f); + int fd = open("test.txt", O_RDONLY); + if (fd < 0) { + printf("Error opening file\n"); + return; } - free(buf); - return result; + int read_bytes = read(fd, buf, buf_size); + printf("Read %d bytes\n", read_bytes); + + buf[read_bytes] = '\0'; // zero terminate string + printf("Data: %s\n", buf); + + close(fd); } -static bool cleanup_test_files() +static void example_read_file_spiffs() { - bool result = true; + const int buf_size = 0xFF; + uint8_t buf[buf_size]; - for (uint8_t i = 0; i < TEST_FILES; i++) { - printf("Removing file %s\n", test_files[i].name); - if (SPIFFS_remove(&fs, test_files[i].name) != SPIFFS_OK) { - printf("Remove file operation failed\n"); - result = false; - break; - } + spiffs_file fd = SPIFFS_open(&fs, "other.txt", SPIFFS_RDONLY, 0); + if (fd < 0) { + printf("Error opening file\n"); + return; } - return result; + + int read_bytes = SPIFFS_read(&fs, fd, buf, buf_size); + printf("Read %d bytes\n", read_bytes); + + buf[read_bytes] = '\0'; // zero terminate string + printf("Data: %s\n", buf); + + SPIFFS_close(&fs, fd); } -inline static void print_info() +static void example_write_file() +{ + uint8_t buf[] = "Example data, written by ESP8266"; + + int fd = open("other.txt", O_WRONLY|O_CREAT, 0); + if (fd < 0) { + printf("Error opening file\n"); + return; + } + + int written = write(fd, buf, sizeof(buf)); + printf("Written %d bytes\n", written); + + close(fd); +} + +static void example_fs_info() { uint32_t total, used; - SPIFFS_info(&fs, &total, &used); - - printf("FS total=%d bytes, used=%d bytes\n", total, used); - printf("FS %d %% used\n", 100 * used/total); - - // File system structure visualisation - // SPIFFS_vis(&fs); + printf("Total: %d bytes, used: %d bytes", total, used); } void test_task(void *pvParameters) { - bool result = true; - - esp_spiffs_mount(); - esp_spiffs_unmount(); // FS must be unmounted before formating - if (SPIFFS_format(&fs) == SPIFFS_OK) { - printf("Format complete\n"); - } else { - printf("Format failed\n"); + esp_spiffs_init(); + if (esp_spiffs_mount() != SPIFFS_OK) { + printf("Error mount SPIFFS\n"); } - esp_spiffs_mount(); while (1) { - vTaskDelay(5000 / portTICK_RATE_MS); + vTaskDelay(2000 / portTICK_RATE_MS); - result = write_test_files(); + example_write_file(); - if (result) { - result = verify_test_files(); - } + example_read_file_posix(); - print_info(); + example_read_file_spiffs(); - if (result) { - result = cleanup_test_files(); - } + example_fs_info(); - if (result) { - printf("Test passed!\n"); - } else { - printf("Test failed!\n"); - while (1) { - vTaskDelay(1); - } - } + printf("\n\n"); } } diff --git a/extras/spiffs/README.md b/extras/spiffs/README.md new file mode 100644 index 0000000..76d2081 --- /dev/null +++ b/extras/spiffs/README.md @@ -0,0 +1,153 @@ +# SPIFFS ESP8266 File system + +This component adds file system support for ESP8266. File system of choice +for ESP8266 is [SPIFFS](https://github.com/pellepl/spiffs). +It was specifically designed to use with SPI NOR flash on embedded systems. +The main advantage of SPIFFS is wear leveling, which prolongs life time +of a flash memory. + +## Features + + * SPIFFS - embedded file system for NOR flash memory. + * POSIX file operations. + * Static files upload to ESP8266 file system within build process. + * SPIFFS singleton configuration. Only one instance of FS on a device. + +## Usage + +In order to use file system in a project the following steps should be made: + * Add SPIFFS component in a project Makefile `EXTRA_COMPONENTS = extras/spiffs` + * Specify your flash size in the Makefile `FLASH_SIZE = 32` + * Specify the start address of file system region on the flash memory +`SPIFFS_BASE_ADDR = 0x200000` + * If you want to upload files to a file system during flash process specify +the directory with files `$(eval $(call make_spiffs_image,files))` + +In the end the Makefile should look like: + +``` +PROGRAM=spiffs_example +EXTRA_COMPONENTS = extras/spiffs +FLASH_SIZE = 32 + +SPIFFS_BASE_ADDR = 0x200000 +SPIFFS_SIZE = 0x100000 + +include ../../common.mk + +$(eval $(call make_spiffs_image,files)) +``` + +Note: Macro call to prepare SPIFFS image for flashing should go after +`include common.mk` + +### Files upload + +To upload files to a file system during flash process the following macro is +used: + +``` +$(eval $(call make_spiffs_image,files)) +``` + +It enables the build of a helper utility **mkspiffs**. This utility creates +an SPIFFS image with files in the specified directory. + +The SPIFFS image is created during build stage, after `make` is run. +The image is flashed into the device along with firmware during flash stage, +after `make flash` is run. + +**mkspiffs** utility uses the same SPIFFS source code and the same +configuration as ESP8266. So the created image should always be compatible +with SPIFFS on a device. + +The build process will catch any changes in files directory and rebuild the +image each time `make` is run. +The build process will handle SPIFFS_SIZE change and rebuild **mkspiffs** +utility and the image. + +## Example + +### Mount + +``` +esp_spiffs_init(); // allocate memory buffers +if (esp_spiffs_mount() != SPIFFS_OK) { + printf("Error mounting SPIFFS\n"); +} +``` + +### Format + +Formatting SPIFFS is a little bit awkward. Before formatting SPIFFS must be +mounted and unmounted. +``` +esp_spiffs_init(); +if (esp_spiffs_mount() != SPIFFS_OK) { + printf("Error mount SPIFFS\n"); +} +SPIFFS_unmount(&fs); // FS must be unmounted before formating +if (SPIFFS_format(&fs) == SPIFFS_OK) { + printf("Format complete\n"); +} else { + printf("Format failed\n"); +} +esp_spiffs_mount(); +``` + +### POSIX read + +Nothing special here. + +``` +const int buf_size = 0xFF; +uint8_t buf[buf_size]; + +int fd = open("test.txt", O_RDONLY); +if (fd < 0) { + printf("Error opening file\n"); +} + +read(fd, buf, buf_size); +printf("Data: %s\n", buf); + +close(fd); +``` + +### SPIFFS read + +SPIFFS interface is intended to be as close to POSIX as possible. + +``` +const int buf_size = 0xFF; +uint8_t buf[buf_size]; + +spiffs_file fd = SPIFFS_open(&fs, "other.txt", SPIFFS_RDONLY, 0); +if (fd < 0) { + printf("Error opening file\n"); +} + +SPIFFS_read(&fs, fd, buf, buf_size); +printf("Data: %s\n", buf); + +SPIFFS_close(&fs, fd); +``` + +### POSIX write + +``` +uint8_t buf[] = "Example data, written by ESP8266"; + +int fd = open("other.txt", O_WRONLY|O_CREAT, 0); +if (fd < 0) { + printf("Error opening file\n"); +} + +write(fd, buf, sizeof(buf)); + +close(fd); +``` + +## Resources + +[SPIFFS](https://github.com/pellepl/spiffs) diff --git a/extras/spiffs/esp_spiffs.c b/extras/spiffs/esp_spiffs.c index 72419a1..11a84d5 100644 --- a/extras/spiffs/esp_spiffs.c +++ b/extras/spiffs/esp_spiffs.c @@ -14,9 +14,21 @@ spiffs fs; -static void *work_buf = 0; -static void *fds_buf = 0; -static void *cache_buf = 0; +typedef struct { + void *buf; + uint32_t size; +} fs_buf_t; + +static fs_buf_t work_buf = {0}; +static fs_buf_t fds_buf = {0}; +static fs_buf_t cache_buf = {0}; + +/** + * Number of file descriptors opened at the same time + */ +#define ESP_SPIFFS_FD_NUMBER 5 + +#define ESP_SPIFFS_CACHE_PAGES 5 /* * Flash addresses and size alignment is a rip-off of Arduino implementation. @@ -147,6 +159,29 @@ static s32_t esp_spiffs_erase(u32_t addr, u32_t size) return SPIFFS_OK; } +void esp_spiffs_init() +{ + work_buf.size = 2 * SPIFFS_CFG_LOG_PAGE_SZ(); + fds_buf.size = SPIFFS_buffer_bytes_for_filedescs(&fs, ESP_SPIFFS_FD_NUMBER); + cache_buf.size= SPIFFS_buffer_bytes_for_cache(&fs, ESP_SPIFFS_CACHE_PAGES); + + work_buf.buf = malloc(work_buf.size); + fds_buf.buf = malloc(fds_buf.size); + cache_buf.buf = malloc(cache_buf.size); +} + +void esp_spiffs_deinit() +{ + free(work_buf.buf); + work_buf.buf = 0; + + free(fds_buf.buf); + fds_buf.buf = 0; + + free(cache_buf.buf); + cache_buf.buf = 0; +} + int32_t esp_spiffs_mount() { spiffs_config config = {0}; @@ -155,18 +190,13 @@ int32_t esp_spiffs_mount() config.hal_write_f = esp_spiffs_write; config.hal_erase_f = esp_spiffs_erase; - size_t workBufSize = 2 * SPIFFS_CFG_LOG_PAGE_SZ(); - size_t fdsBufSize = SPIFFS_buffer_bytes_for_filedescs(&fs, 5); - size_t cacheBufSize = SPIFFS_buffer_bytes_for_cache(&fs, 5); + printf("SPIFFS size: %d\n", SPIFFS_SIZE); + printf("SPIFFS memory, work_buf_size=%d, fds_buf_size=%d, cache_buf_size=%d\n", + work_buf.size, fds_buf.size, cache_buf.size); - work_buf = malloc(workBufSize); - fds_buf = malloc(fdsBufSize); - cache_buf = malloc(cacheBufSize); - printf("spiffs memory, work_buf_size=%d, fds_buf_size=%d, cache_buf_size=%d\n", - workBufSize, fdsBufSize, cacheBufSize); - - int32_t err = SPIFFS_mount(&fs, &config, work_buf, fds_buf, fdsBufSize, - cache_buf, cacheBufSize, 0); + int32_t err = SPIFFS_mount(&fs, &config, (uint8_t*)work_buf.buf, + (uint8_t*)fds_buf.buf, fds_buf.size, + cache_buf.buf, cache_buf.size, 0); if (err != SPIFFS_OK) { printf("Error spiffs mount: %d\n", err); @@ -175,19 +205,6 @@ int32_t esp_spiffs_mount() return err; } -void esp_spiffs_unmount() -{ - SPIFFS_unmount(&fs); - - free(work_buf); - free(fds_buf); - free(cache_buf); - - work_buf = 0; - fds_buf = 0; - cache_buf = 0; -} - #define FD_OFFSET 3 // This implementation replaces implementation in core/newlib_syscals.c diff --git a/extras/spiffs/esp_spiffs.h b/extras/spiffs/esp_spiffs.h index 3022d51..f074017 100644 --- a/extras/spiffs/esp_spiffs.h +++ b/extras/spiffs/esp_spiffs.h @@ -13,16 +13,27 @@ extern spiffs fs; /** - * Provide SPIFFS with all necessary configuration, allocate memory buffers - * and mount SPIFFS. + * Prepare for SPIFFS mount. + * + * The function allocates all the necessary buffers. + */ +void esp_spiffs_init(); + +/** + * Free all memory buffers that were used by SPIFFS. + * + * The function should be called after SPIFFS unmount if the file system is not + * going to need any more. + */ +void esp_spiffs_deinit(); + +/** + * Mount SPIFFS. + * + * esp_spiffs_init must be called first. * * Return SPIFFS return code. */ int32_t esp_spiffs_mount(); -/** - * Unmount SPIFFS and free all allocated buffers. - */ -void esp_spiffs_unmount(); - #endif // __ESP_SPIFFS_H__