В данной статье мы научимся использовать файловую систему SPIFFS (SPI flash file system) в ESP32 и создавать, записывать и читать файлы. Кроме того, мы рассмотрим, как загружать файлы в SPIFFS, такие как текстовые, HTML, CSS и JavaScript-файлы и т. д. Дополнительно мы увидим, как создать раздел SPIFFS во флэш-памяти ESP32. В конце мы воспользуемся доступными в ESP-IDF API SPIFFS, необходимыми для инициализации и монтирования раздела SPIFFS, а затем для создания, записи и чтения данных из файлов.
Введение в SPIFFS
Плата разработки ESP32 содержит файловую систему SPI (Serial Peripheral Interface Flash File System), обычно называемую SPIFFS. Это облегченная файловая система, которая формируется путем разделения SPI NOR-флэш-памяти ESP32 на область двоичных файлов и область файловой системы. Как и флэш-память ESP32, эта файловая система подключается по шине SPI. Это позволяет хранить файлы во флэш-памяти SPI без необходимости использования внешней памяти с ESP32.
Используя SPIFFS с ESP32, пользователи могут легко создавать, читать и записывать файлы во флэш-память платы. Из-за ограниченного количества циклов записи во флэш-память использование функции записи не рекомендуется, но она всё же присутствует. Вкратце, мы можем читать, записывать, удалять и закрывать файлы, сохранённые в SPIFFS, очень удобным способом. Давайте узнаем, как это сделать с помощью ESP-IDF.
Загрузка и чтение содержимого файлов SPIFFS на ESP32 с использованием ESP-IDF
В этом разделе давайте создадим проект с использованием ESP-IDF, в котором будем считывать данные из файла, сохраненного на файловой системе ESP32 SPIFFS.
Создание проекта
Откройте VS Code и перейдите в меню View > Command Palette («Вид» > «Палитра команд»). Введите в строке поиска ESP-IDF: New Project и нажмите Enter.
Укажите название проекта и каталог. Мы назвали наш проект «ESP32_SPIFFS_READ_FILE». Для платы ESP-IDF мы выбрали опцию custom board («Пользовательская плата»). В качестве целевой платформы для ESP-IDF мы выбрали модуль ESP32. Нажмите кнопку ‘Choose Template’ («Выбрать шаблон»), чтобы продолжить.
В разделе Extension («Расширение») выберите опцию ESP-IDF:
На вкладке get-started («Начало работы») мы щелкнем по пункту «sample_project». Затем нажмем ‘Create project using template sample_project’ («Создать проект, используя шаблон sample_project»).
Вы получите уведомление о создании проекта. Чтобы открыть проект в новом окне, нажмите «Да».
Это открывает наш проект ESP32_SPIFFS_READ_FILE, который мы создали на вкладке EXPLORER. Внутри папки проекта находится несколько папок. Это справедливо для всех проектов, которые вы будете создавать с помощью ESP-IDF Explorer.
Давайте сначала покажем вам, как создать несколько дополнительных файлов, необходимых для этого проекта.
Создайте новый файл и назовите его «partitions.csv». Скопируйте в этот файл приведенные ниже данные и сохраните его. Нам нужно добавить собственную пользовательскую таблицу разделов, поэтому мы будем использовать этот файл.
|
1 2 3 4 5 6 |
# Name, Type, SubType, Offset, Size, Flags # Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild nvs, data, nvs, , 0x6000, phy_init, data, phy, , 0x1000, factory, app, factory, , 1M, storage, data, spiffs, , 1M |
После создания файла partitions.csv давайте создадим папку с именем ‘spiffs_data‘ и внутри неё — текстовый файл с именем ‘data.txt‘. Это файл, который мы будем считывать из SPI-флэш-памяти. В этот файл можно добавить любой текст. Например, добавим следующий текст в наш файл data.txt и сохраним его.
|
1 |
This is a getting started guide to read, save and write to SPIFFS from ESP32 using ESP-IDF. |
Перейдите в папку с именем ‘main’ и откройте файл CMakeLists.txt. Добавьте в конец этого файла следующую строку и сохраните его.
|
1 |
spiffs_create_partition_image(storage ../spiffs_data FLASH_IN_PROJECT) |
Конфигурация проекта
Для начала перейдем в меню menuconfig. Щелкните значок, показанный ниже. Откроется редактор конфигурации ESP-IDF SDK.
Прокрутите вниз и откройте Serial Flasher config (конфигурацию Serial Flasher). Здесь вы можете установить режим SPI для флэш-памяти, режим выборки флэш-памяти, скорость SPI для флэш-памяти, размер флэш-памяти и т. д. Установите параметры, как показано ниже, и сохраните изменения. Обратите внимание, что мы установили размер флэш-памяти равным 4 МБ.
Далее прокрутите вниз и нажмите Partition Table («Таблица разделов»). Здесь мы выберем опцию ‘custom partition table CSV’ («Пользовательская таблица разделов CSV») в разделе «Таблица разделов», укажем имя созданного нами CSV-файла и смещение таблицы разделов.
Код чтения файла SPIFFS с помощью ESP-IDF
В папке main находится исходный код, то есть файл main.c будет расположен именно здесь. Теперь перейдите в папку main > main.c и откройте его. Скопируйте приведенный ниже код и сохраните его.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
#include <stdio.h> #include "esp_spiffs.h" #include "esp_log.h" #define TAG "spiffs" void app_main(void) { esp_vfs_spiffs_conf_t config = { .base_path = "/spiffs", .partition_label = NULL, .max_files = 5, .format_if_mount_failed = true, }; esp_vfs_spiffs_register(&config); FILE *file = fopen("/spiffs/data.txt", "r"); if(file ==NULL) { ESP_LOGE(TAG,"File does not exist!"); } else { char line[256]; while(fgets(line, sizeof(line), file) != NULL) { printf(line); } fclose(file); } esp_vfs_spiffs_unregister(NULL); } |
Как работает код?
Для начала мы подключим необходимые для этого проекта библиотеки, включая esp_spiffs.h для функциональности ESP32 SPIFFS и esp_log.h в качестве библиотеки для ведения журналов, отображающей информационные сообщения.
|
1 2 3 |
#include <stdio.h> #include "esp_spiffs.h" #include "esp_log.h" |
Этот код использует информационное логирование. Функция логирования принимает два аргумента. Первый аргумент — это тег, а второй — отформатированная строка. Поэтому эта глобальная переменная будет полезна при вызове функций ESP_LOGI(). Здесь «spiffs» — это тег, который будет использоваться при логировании.
|
1 |
#define TAG "spiffs" |
Во-первых, у нас есть структура конфигурации для esp_vfs_spiffs_register. Она содержит следующие элементы:
- base_path — это префикс пути к файлу, связанный с файловой системой. В нашем случае это «/spiffs».
- partition_label — это метка раздела SPIFFS, используемая в качестве параметра. Она необязательна. Мы установили её значение равным NULL, поэтому будет использоваться первый раздел с подтипом = spiffs.
- max_files — это максимальное количество файлов, которые можно открыть одновременно. В нашем случае мы установили его равным 5.
- Параметр format_if_mount_failed установлен в значение true. Следовательно, в случае сбоя при монтировании будет выполнено форматирование файловой системы.
Для регистрации и монтирования SPIFFS к VFS с заданным префиксом пути вызывается функция esp_vfs_spiffs_register(). Эта функция принимает один параметр — указатель на структуру конфигурации esp_vfs_spiffs_conf_t, определенную ранее.
|
1 2 3 4 5 6 7 |
esp_vfs_spiffs_conf_t config = { .base_path = "/spiffs", .partition_label = NULL, .max_files = 5, .format_if_mount_failed = true, }; esp_vfs_spiffs_register(&config); |
Файловая система ESP32 SPIFFS имеет плоскую структуру. Поэтому она не поддерживает каталоги. Например, если SPIFFS смонтирована в каталог /spiffs, то при создании файла с путем: /spiffs/tmp/myfile.txt в SPIFFS будет создан файл с именем /tmp/myfile.txt, а не myfile.txt в каталоге /spiffs/tmp.
Для открытия файла используется стандартная функция fopen(). В качестве первого параметра укажите путь к файлу, а в качестве второго — режим. В данном случае путь к файлу — «/spiffs/data.txt», и он открывается в режиме чтения (r). Здесь ‘file’ — это указатель на файл определенного типа.
|
1 |
FILE *file = fopen("/spiffs/data.txt", "r"); |
Если файл отсутствует в SPIFFS, терминал ESP-IDF выведет сообщение о том, что файл не существует. В противном случае, если файл присутствует, он считывает символы из файла и выводит их на терминал. Это достигается вызовом функции fgets(). Мы используем функцию fgets() для чтения символов из файла, которые сохраняются в массиве символов ‘line’. Первый параметр — это буфер, в нашем случае ‘line’. Второй параметр — это количество символов, в нашем случае ‘sizeof(line)’, а третий параметр — указатель на файл, в нашем случае ‘file’. Затем мы закрываем файл с помощью функции fclose(), передавая указатель на файл в качестве параметра.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
if(file ==NULL) { ESP_LOGE(TAG,"File does not exist!"); } else { char line[256]; while(fgets(line, sizeof(line), file) != NULL) { printf(line); } fclose(file); } |
В конце мы отменяем регистрацию и отмонтируем SPIFFS из VFS, вызывая функцию esp_vfs_spiffs_unregister(). Эта функция принимает один параметр — метку раздела, которую мы определили в структуре конфигурации esp_vfs_spiffs_conf_t. В нашем случае это было NULL.
|
1 |
esp_vfs_spiffs_unregister(NULL); |
Компиляция скетча
Для прошивки микросхемы введите следующую команду в последовательном терминале. Не забудьте заменить COM-порт на тот, через который подключена ваша плата.
|
1 |
idf.py -p COMX flash monitor |
После успешной прошивки кода ESP-IDF отображает текст, сохраненный в файле data.txt, который загружен на плату ESP32 в SPIFFS.
Создание и запись файлов в SPIFFS ESP32 с использованием ESP-IDF
В этом разделе мы создадим проект с использованием ESP-IDF, в котором будем создавать файл, загружать его в ESP32 SPIFFS и записывать в него данные.
Создание проекта
Откройте VS Code и перейдите в меню View > Command Palette («Вид» > «Палитра команд»). Введите в строке поиска ESP-IDF: New Project и нажмите Enter.
Укажите название проекта и каталог. Мы назвали наш проект «ESP32_SPIFFS_CREATE_WRITE». Для платы ESP-IDF мы выбрали опцию custom board («Пользовательская плата»). В качестве целевой платформы для ESP-IDF мы выбрали модуль ESP32. Нажмите кнопку ‘Choose Template’ («Выбрать шаблон»), чтобы продолжить.
В разделе Extension («Расширение») выберите опцию ESP-IDF:
На вкладке get-started («Начало работы») мы щелкнем по пункту «sample_project». Затем нажмем ‘Create project using template sample_project’ («Создать проект, используя шаблон sample_project»).
Вы получите уведомление о создании проекта. Чтобы открыть проект в новом окне, нажмите «Да».
Это откроет наш проект, который мы создали во вкладке EXPLORER («Проводник»). Создайте новый файл и назовите его «partitions.csv». Скопируйте в этот файл приведенные ниже данные и сохраните его. Нам нужно добавить собственную пользовательскую таблицу разделов, поэтому мы будем использовать этот файл.
|
1 2 3 4 5 6 |
# Name, Type, SubType, Offset, Size, Flags # Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild nvs, data, nvs, , 0x6000, phy_init, data, phy, , 0x1000, factory, app, factory, , 1M, storage, data, spiffs, , 1M |
Конфигурация проекта
Давайте сначала перейдем к menuconfig. Это откроет редактор конфигурации ESP-IDF SDK.
Прокрутите вниз и откройте Serial Flasher config (конфигурацию Serial Flasher). Здесь вы можете установить режим SPI для флэш-памяти, режим выборки флэш-памяти, скорость SPI для флэш-памяти, размер флэш-памяти и т. д. Установите параметры, как показано ниже, и сохраните изменения. Обратите внимание, что мы установили размер флэш-памяти равным 4 МБ.
Далее прокрутите вниз и нажмите Partition Table («Таблица разделов»). Здесь мы выберем опцию ‘custom partition table CSV’ («Пользовательская таблица разделов CSV») в разделе «Таблица разделов», укажем имя созданного нами CSV-файла и смещение таблицы разделов.
Создание нового файла и написание кода
В папке main находится исходный код, то есть файл main.c будет расположен именно здесь. Теперь перейдите в папку main > main.c и откройте его. Скопируйте приведенный ниже код и сохраните его.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
#include <stdio.h> #include "esp_spiffs.h" #include "esp_log.h" #define TAG "spiffs" void app_main(void) { esp_vfs_spiffs_conf_t config = { .base_path = "/spiffs", .partition_label = NULL, .max_files = 5, .format_if_mount_failed = true, }; esp_vfs_spiffs_register(&config); /*Create file with name hello.txt */ ESP_LOGE(TAG, "Creating New file: hello.txt"); FILE *f = fopen("/spiffs/hello.txt", "w"); if (f == NULL) { ESP_LOGE(TAG, "Failed to open file for writing"); return; } ESP_LOGE(TAG, "Writing data to file: hello.txt"); fprintf(f, "Welcome to ESP32Tutorials.com!\n"); // write data to hello.txt file fclose(f); ESP_LOGI(TAG, "File written"); /* read data from hello.txt file */ ESP_LOGE(TAG, "Reading data from file: hello.txt"); FILE *file = fopen("/spiffs/hello.txt", "r"); if (file == NULL) { ESP_LOGE(TAG, "File does not exist!"); } else { char line[256]; while (fgets(line, sizeof(line), file) != NULL) { printf(line); } fclose(file); } esp_vfs_spiffs_unregister(NULL); } |
Как работает код?
Большая часть кода та же, что мы использовали ранее при чтении из файла, сохраненного в SPIFFS. Однако здесь мы создадим новый файл, запишем в него данные, а затем прочтем его содержимое перед закрытием.
Для открытия или создания нового файла мы будем использовать функцию fopen(). В качестве первого параметра укажите путь к файлу, а в качестве второго — режим. В данном случае мы хотим создать новый файл с именем hello.txt, поэтому путь к файлу — «/spiffs/hello.txt», и он создается в режиме записи (w). Здесь ‘f’ — это указатель на файл, указывающий на тип файла.
|
1 2 |
ESP_LOGE(TAG, "Creating New file: hello.txt"); FILE *f = fopen("/spiffs/hello.txt", "w"); |
Если файл не существует в файловой системе SPIFFS, терминал ESP-IDF выведет сообщение о том, что файл не существует.
|
1 2 3 4 5 |
if (f == NULL) { ESP_LOGE(TAG, "Failed to open file for writing"); return; } |
После создания нового файла мы запишем в него строку. Для этого мы воспользуемся функцией fprintf(). Она принимает два параметра: указатель на файл и строку, которую мы хотим вывести в файл. В нашем случае мы записываем в файл строку «Welcome to ESP32Tutorials.com!».
После этого мы закроем файл, чтобы убедиться, что данные в него записаны. Для закрытия файла используется функция fclose(). Она принимает в качестве параметра указатель на файл, в нашем случае это ‘f’.
|
1 2 3 4 |
ESP_LOGE(TAG, "Writing data to file: hello.txt"); fprintf(f, "Welcome to ESP32Tutorials.com!\n"); // write data to hello.txt file fclose(f); ESP_LOGI(TAG, "File written"); |
Чтобы прочитать файл hello.txt, сначала откроем его в режиме чтения с помощью функции fopen(). В качестве первого параметра укажем путь к файлу, а в качестве второго — режим. В данном случае мы хотим открыть файл hello.txt в режиме чтения, поэтому путь к файлу — «/spiffs/hello.txt», и он открывается в режиме чтения (r). Здесь ‘file’ — это указатель на файл, указывающий на его тип.
Если файл отсутствует в SPIFFS, терминал ESP-IDF выведет сообщение о том, что файл не существует. В противном случае, если файл присутствует, он считывает символы из файла и выводит их на терминал. Это достигается вызовом функции fgets(). Мы используем функцию fgets() для чтения символов из файла, которые сохраняются в массиве символов ‘line’. Первый параметр — это буфер, в нашем случае ‘line’. Второй параметр — это количество символов, в нашем случае ‘sizeof(line)’, а третий параметр — указатель на файл, в нашем случае ‘file’. Затем мы закрываем файл с помощью функции fclose(), передавая указатель на файл в качестве параметра.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
ESP_LOGE(TAG, "Reading data from file: hello.txt"); FILE *file = fopen("/spiffs/hello.txt", "r"); if (file == NULL) { ESP_LOGE(TAG, "File does not exist!"); } else { char line[256]; while (fgets(line, sizeof(line), file) != NULL) { printf(line); } fclose(file); } |
В конце мы отменяем регистрацию и отключаем SPIFFS от VFS, вызывая функцию esp_vfs_spiffs_unregister().
|
1 |
esp_vfs_spiffs_unregister(NULL); |
Компиляция скетча
Для прошивки микросхемы введите следующую команду в последовательном терминале. Не забудьте заменить COM-порт на тот, через который подключена ваша плата.
|
1 |
idf.py -p COMX flash monitor |
После успешной прошивки кода терминал ESP-IDF отображает различные сообщения, в которых сначала создается новый файл с именем hello.txt, затем происходит запись в него, а затем чтение из него.
