Продолжаем работу по программированию микроконтроллера ESP8266 с использованием операционной системы реального времени FREEFTOS.
На прошлом уроке мы убедились, что мы вполне можем передавать по протоколу HTTP документы с нашего сервера клиенту. Только мы столкнулись с проблемой их неудобного хранения на нашем сервере, а именно при помощи массива. Хотелось бы как-то хранить их в явном виде — в виде файлов. Помочь нам в этом сможет то, что у контроллера ESP8266 (как впрочем и у ESP32) есть своя файловая система SPIFFS (Serial Peripheral Interface Flash File System). Вернее даже будет сказать, что она есть не у контроллера, а содержится в API SDK. Данная файловая система размещается в свободном пространстве FLASH-памяти, а так как у нас её 4 мегабайта, а для прошивки нам много места не нужно, мы до сих пор вполне умещаемся в 500 килобайтах в начале памяти, то мы вполне можем использовать некоторое пространство для файловой системы. На всякий случай мы для прошивки оставим полностью первый мегабайт, а дальше уже будем размещать файловую систему. 1-2 мегабайта нам вполне хватит для размещения служебных файлов сервера. Также в технической документации мы можем прочитать то, что на файловую систему SPIFFS наложен ряд ограничений, как то невозможность создавать директории, ограниченный размер имени файла и т.д. Но, так как хранить нам придётся немного, мы вполне в рамках данных ограничений уместимся.
Итак, SDK нам предоставляет некоторый функционал по работе с файловой системой SPIFS. Последний комплект даже предоставляет намного больше, но пока мне его, к сожалению, не удалось прикрутить к работе под Windows.
Думаю, что минимум информации о SPIFFS я вам рассказал, остальное будем постигать по мере написания кода.
Также существует утилита для добавления в образ файловой системы файлов mkspiffs, но к сожалению у меня она корректно работает только для контроллера ESP32. Поэтому будем пока обходиться другими средствами, также мы этой невозможностью будем мотивированы для создания проекта по добавлению файлов через браузер в дальнейшем.
Плату мы будем использовать ту же самую, что и на прошлом уроке
Проект мы также с именем WIFI_STA_HTTP_SERVER_RTOS возьмём из прошлого урока и на его основе создадим проект с именем SPIFFS_RTOS.
Откроем наш проект в Eclipse, и, так как мы не будем работать ни с дисплеем, ни с сетью, то мы вполне можем себе позволить удалить из проекта файлы i2c_user.c, i2c_user.h, wifi.c и wifi.h.
Удалим из кода во всех файлах подключение заголовочных файлов i2c_user.h и wifi.h к проекту.
В файле main.c из бесконечного цикла функции task1 удалим мониторинг сетевого адреса
wifi_get_ip_info(STATION_IF, &ipinfo);
os_printf(«Addr STA: %s\n», inet_ntoa(ipinfo.ip));
Также удалим до бесконечного цикла вот этот код
struct ip_info ipinfo;
uint32_t ip;
char ip_char[17];
snprintf(ip_char, sizeof(ip_char), «%s», «192.168.0.1»);
ip = ipaddr_addr(ip_char);
memcpy(&ipinfo.ip, &ip, 4);
Также в теле функции user_init() удалим инициализацию Wi-Fi-станции
init_esp_wifi();
//start_wifi_ap(WIFI_APSSID, WIFI_APPASSWORD);
start_wifi_station(WIFI_CLIENTSSID, WIFI_CLIENTPASSWORD);
Теперь у нас проект соберётся.
Для удобной работы с файловой системой создадим под это два файла — user_spiffs.h и user_spiffs.c следующего содержания
1 2 3 4 5 6 7 8 9 10 |
#ifndef INC_USER_SPIFFS_H_ #define INC_USER_SPIFFS_H_ //------------------------------------------------ #include "esp_common.h" #include "esp_spiffs.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/queue.h" //------------------------------------------------ #endif /* INC_USER_SPIFFS_H_ */ |
1 2 3 |
#include "user_spiffs.h" //------------------------------------------------ //------------------------------------------------ |
Также подключим вновь созданную библиотеку в файле main.h
1 2 |
#include "gpio.h" #include "user_spiffs.h" |
В файле user_spiffs.c добавим функцию инициализации, которая ничего не будет принимать во входных параметрах и будет возвращать результат, успешно ли прошла инициализация файловой системы SPIFFS
1 2 3 4 5 6 7 |
//------------------------------------------------ uint8_t ICACHE_FLASH_ATTR spiffs_init() { uint8_t ret = 0; return ret; } //------------------------------------------------ |
Объявим в заголовочном файле на данную функцию прототип и, перейдём в функцию user_init() файла main.c и также сначала объявим целочисленную переменную
1 2 3 |
void ICACHE_FLASH_ATTR user_init() { uint8_t ret = 0; |
Вызовем нашу функцию инициализации SPIFFS и по результату проверим, успешно ли у нас прошла инициализация, если нет, то в терминальной программе об этом скажем и выйдем из программы
1 2 3 4 5 6 7 |
PIN_FUNC_SELECT (PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2); ret = spiffs_init(); if(ret != 0) { os_printf("SPIFFS init failed\n"); return; } |
Вернёмся в файл user_spiffs.c в функцию spiffs_init и объявим ещё одну целочисленную переменную, которая там также в дальнейшем понадобится
1 2 |
uint8_t ret = 0; int res; |
Для первичной инициализации нам потребуется объявление переменной типа конфигурационной структуры
1 2 |
int res; struct esp_spiffs_config config; |
Далее нам потребуется заполнить поля данной переменной. Для этого мы в файле user_spiffs.h объявим вот такие макросы
1 2 3 4 5 6 7 8 9 |
#include "freertos/queue.h" //------------------------------------------------ #define FS1_FLASH_SIZE (0xF0000) #define FS1_FLASH_ADDR (0x110000) #define SECTOR_SIZE (4*1024) #define LOG_BLOCK (SECTOR_SIZE) #define LOG_PAGE (256) #define FD_BUF_SIZE 32*4 #define CACHE_BUF_SIZE (LOG_PAGE + 32)*8 |
Мы пока будем работать с файловой системой размером чуть меньше одного мегабайта, расположим её с адреса 0x110000, зададим размер страницы 256 байт, размер сектора (как и блока) в 4 килобайта.
Вернёмся в файл user_spiffs.c и, используя наши макросы, проинициализируем поля
1 2 3 4 5 6 7 8 |
struct esp_spiffs_config config; config.phys_size = FS1_FLASH_SIZE; config.phys_addr = FS1_FLASH_ADDR; config.phys_erase_block = SECTOR_SIZE; config.log_block_size = LOG_BLOCK; config.log_page_size = LOG_PAGE; config.fd_buf_size = FD_BUF_SIZE * 2; config.cache_buf_size = CACHE_BUF_SIZE; |
И теперь, воспользовавшись данной переменной типа структуры, вызовем функцию инициализации файловой системы SPIFFS библиотеки SDK
1 2 |
config.cache_buf_size = CACHE_BUF_SIZE; esp_spiffs_init(&config); |
Если мы после этого попытаемся собрать проект, то у нас этап компиляции пройдёт успешно, а на этапе линковки мы, скорее всего получим вот такую ошибку
Эта ошибка говорит о неподключенной библиотеке. Так как весь функционал библиотеки SDK для SPIFFS с закрытым кодом, то мы видим пока только заголовки. Поэтому в makefile вот здесь подключим нужную библиотеку
@$(LD) -L$(SDK_LIBDIR) $(LD_SCRIPT) $(LDFLAGS) -Wl,--start-group $(LIBS) build/app_app.a $(SDK_LIBDIR)/libdriver.a $(SDK_LIBDIR)/libspiffs.a -Wl,--end-group -o build/app.out
Вот теперь всё нормально соберётся.
Далее нам нужно примонтировать нашу файловую систему, чтобы мы затем спокойно могли к ней обращаться.
Для этого сначала в файле user_spiffs.c добавим функцию выше функции spiffs_init
1 2 3 4 5 6 7 |
//------------------------------------------------ int ICACHE_FLASH_ATTR mount_fs() { int res; return res; } //------------------------------------------------ |
Добавим на данную функцию прототип в заголовочном файле.
В теле данной функции объявим переменную другой структуры
1 2 |
int res; spiffs_config mount_config; |
Используя те же макросы, а также имена функций обратного вызова файловых операций, которые мы напишем позже, в функции инициализации spiffs_init файла user_spiffs.c инициализируем поля нашей переменной
1 2 3 4 5 6 7 8 9 |
esp_spiffs_init(&config); mount_config.hal_erase_f = _erase; mount_config.hal_read_f = _read; mount_config.hal_write_f = _write; mount_config.log_block_size = LOG_BLOCK; mount_config.log_page_size = LOG_PAGE; mount_config.phys_addr = FS1_FLASH_ADDR; mount_config.phys_erase_block = SECTOR_SIZE; mount_config.phys_size = FS1_FLASH_SIZE; |
И, прежде чем писать здесь код дальше, мы должны добавить недостающие функции, а также несколько функций, нужных для работы первых.
Для начала объявим несколько глобальных переменных
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#include "user_spiffs.h" //------------------------------------------------ spiffs_config mount_config; spiffs __fs; static u32_t fs_check_fixes = 0; static int erases[FS1_FLASH_SIZE/SECTOR_SIZE]; u8_t _cache[CACHE_BUF_SIZE]; u8_t _work[LOG_PAGE * 2]; u8_t _fds[FD_BUF_SIZE * 2]; static char log_flash_ops = 1; static u32_t bytes_rd = 0; static u32_t bytes_wr = 0; static u32_t reads = 0; static u32_t writes = 0; static u32_t error_after_bytes_read = 0; static u32_t error_after_bytes_written = 0; static char error_after_bytes_written_once_only = 0; static char error_after_bytes_read_once_only = 0; |
Следующая функция будет при операции стирания выводить в терминал при ошибке соответствующие сообщения, стирать нужный сектор и возвращать результат данной операции
1 2 3 4 5 6 7 8 9 10 11 12 |
static char error_after_bytes_read_once_only = 0; //------------------------------------------------ static s32_t ICACHE_FLASH_ATTR esp_spiffs_erase(u32_t addr, u32_t size) { if (size != __fs.cfg.phys_erase_block || addr % __fs.cfg.phys_erase_block != 0) { printf("Invalid size provided to esp_spiffs_erase (%d, %d)\n\r", (int) addr, (int) size); return SPIFFS_ERR_NOT_CONFIGURED; } return spi_flash_erase_sector(addr / __fs.cfg.phys_erase_block); } //------------------------------------------------ |
Далее объявим макрос, который будет нам нужен ниже
1 2 3 4 |
return spi_flash_erase_sector(addr / __fs.cfg.phys_erase_block); } //------------------------------------------------ #define FLASH_UNIT_SIZE 4 |
Следующая функция будет читать и писать информацию некоторого диапазона памяти. Дело в том, что диапазон при работе с файлами не всегда чётко укладывается в секторы и блоки, а, чтобы записать в FLASH-память что-то, нужно перед этим стереть ту область, в которую пишем, а стирать мы минимум можем только сектор, а информацию мы должны сохранить, которая не вошла в диапазон
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 |
//------------------------------------------------ static s32_t ICACHE_FLASH_ATTR esp_spiffs_readwrite(u32_t addr, u32_t size, u8_t *p, int write) { if (size > __fs.cfg.log_page_size) { printf("Invalid size provided to read/write (%d)\n\r", (int) size); return SPIFFS_ERR_NOT_CONFIGURED; } char tmp_buf[__fs.cfg.log_page_size + FLASH_UNIT_SIZE * 2]; u32_t aligned_addr = addr & (-FLASH_UNIT_SIZE); u32_t aligned_size = ((size + (FLASH_UNIT_SIZE - 1)) & -FLASH_UNIT_SIZE) + FLASH_UNIT_SIZE; int res = spi_flash_read(aligned_addr, (u32_t *) tmp_buf, aligned_size); if (res != 0) { printf("spi_flash_read failed: %d (%d, %d)\n\r", res, (int) aligned_addr, (int) aligned_size); return res; } if (!write) { memcpy(p, tmp_buf + (addr - aligned_addr), size); return SPIFFS_OK; } memcpy(tmp_buf + (addr - aligned_addr), p, size); res = spi_flash_write(aligned_addr, (u32_t *) tmp_buf, aligned_size); if (res != 0) { return res; } return SPIFFS_OK; } //------------------------------------------------ |
Следующие две функции простые — одна для чтения другая для записи, и они при этом вызывают предыдущую функцию с разницей в флаге записи
1 2 3 4 5 6 7 8 9 10 11 |
//------------------------------------------------ static s32_t ICACHE_FLASH_ATTR esp_spiffs_read(u32_t addr, u32_t size, u8_t *dst) { return esp_spiffs_readwrite(addr, size, dst, 0); } //------------------------------------------------ static s32_t ICACHE_FLASH_ATTR esp_spiffs_write(u32_t addr, u32_t size, u8_t *src) { return esp_spiffs_readwrite(addr, size, src, 1); } //------------------------------------------------ |
Следующая функция будет вызываться при стирании информации
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
//------------------------------------------------ static s32_t ICACHE_FLASH_ATTR _erase(u32_t addr, u32_t size) { if (addr & (__fs.cfg.phys_erase_block-1)) { printf("trying to erase at addr %08x, out of boundary\n", addr); return -1; } if (size & (__fs.cfg.phys_erase_block-1)) { printf("trying to erase at with size %08x, out of boundary\n", size); return -1; } erases[(addr-__fs.cfg.phys_addr)/__fs.cfg.phys_erase_block]++; return esp_spiffs_erase(addr, size); } //------------------------------------------------ |
Следующая — при чтении
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
//------------------------------------------------ static s32_t ICACHE_FLASH_ATTR _read(u32_t addr, u32_t size, u8_t *dst) { if (log_flash_ops) { bytes_rd += size; reads++; if (error_after_bytes_read > 0 && bytes_rd >= error_after_bytes_read) { if (error_after_bytes_read_once_only) { error_after_bytes_read = 0; } return SPIFFS_ERR_TEST; } } if (addr < __fs.cfg.phys_addr) { printf("FATAL read addr too low %08x < %08x\n", addr, FS1_FLASH_ADDR); exit(0); } if (addr + size > __fs.cfg.phys_addr + __fs.cfg.phys_size) { printf("FATAL read addr too high %08x + %08x > %08x\n", addr, size, FS1_FLASH_ADDR + FS1_FLASH_SIZE); exit(0); } return esp_spiffs_read(addr, size, dst); } //------------------------------------------------ |
Следующая — при записи
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
//------------------------------------------------ static s32_t ICACHE_FLASH_ATTR _write(u32_t addr, u32_t size, u8_t *src) { int i; if (log_flash_ops) { bytes_wr += size; writes++; if (error_after_bytes_written > 0 && bytes_wr >= error_after_bytes_written) { if (error_after_bytes_written_once_only) { error_after_bytes_written = 0; } return SPIFFS_ERR_TEST; } } if (addr < __fs.cfg.phys_addr) { printf("FATAL write addr too low %08x < %08x\n", addr, FS1_FLASH_ADDR); exit(0); } if (addr + size > __fs.cfg.phys_addr + __fs.cfg.phys_size) { printf("FATAL write addr too high %08x + %08x > %08x\n", addr, size, FS1_FLASH_ADDR + FS1_FLASH_SIZE); exit(0); } return esp_spiffs_write(addr, size, src); } //------------------------------------------------ |
Вернёмся в функцию mount_fs и обнулим следующие массивы
1 2 3 4 5 6 |
mount_config.phys_size = FS1_FLASH_SIZE; memset(&__fs, 0, sizeof(__fs)); memset(erases,0,sizeof(erases)); memset(_cache,0,sizeof(_work)); memset(_cache,0,sizeof(_fds)); |
Вызовем функцию монтирования файловой системы и запишем результат в переменную
1 2 3 |
memset(_cache,0,sizeof(_fds)); res = SPIFFS_mount(&__fs, &mount_config, _work, _fds, sizeof(_fds), _cache, sizeof(_cache), spiffs_check_cb_f); |
В последнем параметре вызове функции монтирования находится указатель на функцию обратного вызова проверки согласованности.
Добавим данную функцию в наш код
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 |
static char error_after_bytes_read_once_only = 0; //------------------------------------------------ static void ICACHE_FLASH_ATTR spiffs_check_cb_f(spiffs_check_type type, spiffs_check_report report, u32_t arg1, u32_t arg2) { if (report != SPIFFS_CHECK_PROGRESS) { if (report != SPIFFS_CHECK_ERROR) fs_check_fixes++; printf(" check: "); switch (type) { case SPIFFS_CHECK_INDEX: printf("INDEX "); break; case SPIFFS_CHECK_LOOKUP: printf("LOOKUP "); break; case SPIFFS_CHECK_PAGE: printf("PAGE "); break; default: printf("???? "); break; } if (report == SPIFFS_CHECK_ERROR) { printf("ERROR %i", arg1); } else if (report == SPIFFS_CHECK_DELETE_BAD_FILE) { printf("DELETE BAD FILE %04x", arg1); } else if (report == SPIFFS_CHECK_DELETE_ORPHANED_INDEX) { printf("DELETE ORPHANED INDEX %04x", arg1); } else if (report == SPIFFS_CHECK_DELETE_PAGE) { printf("DELETE PAGE %04x", arg1); } else if (report == SPIFFS_CHECK_FIX_INDEX) { printf("FIX INDEX %04x:%04x", arg1, arg2); } else if (report == SPIFFS_CHECK_FIX_LOOKUP) { printf("FIX INDEX %04x:%04x", arg1, arg2); } else { printf("??"); } printf("\n"); } } //------------------------------------------------ |
Функция эта ничего не возвращает, лишь только отправляет информацию в терминальную программу при различных операциях и ошибках.
В функции spiffs_init вызовем нашу функцию, проверим результат и в случае ошибки выведем соответствующее сообщение в терминал
1 2 3 4 5 6 |
esp_spiffs_init(&config); res = mount_fs(); if (res != SPIFFS_OK) { printf("mount failed, %i\n", SPIFFS_errno(&__fs)); } |
Теперь перейдём в файл main.c и попробуем как-то воспользоваться нашей файловой системой. Подключим глобальную переменную, предоставляющую доступ к нашей файловой системе
1 2 3 |
#include "main.h" //------------------------------------------------------ extern spiffs __fs; |
Вначале нам желательно будет отформатировать наш виртуальный раздел. Хотя практика показала, что это необязательно, но мы хотя бы проверим, что у нас работает функционал. В документации написано, что, прежде чем форматировать систему, нужно её примонтировать и затем отмонтировать. Так как она у нас уже примонтирована, то мы должны её только отмонтировать. Для этого в функции user_init() вызовем следующую функцию
1 2 3 4 |
os_printf("SPIFFS init failed\n"); return; } SPIFFS_unmount(&__fs); |
Объявим целочисленную локальную переменную
1 2 |
uint8_t ret = 0; int res; |
Теперь попробуем отформатировать нашу систему и проверить результат
1 2 3 4 5 |
SPIFFS_unmount(&__fs); res = SPIFFS_format(&__fs); if (res != SPIFFS_OK) { printf("format failed, %i\n", SPIFFS_errno(&__fs)); } |
Дальше мы должны её примонтировать обратно
1 2 3 4 5 6 7 |
printf("format failed, %i\n", SPIFFS_errno(&__fs)); } res = mount_fs(); if (res != SPIFFS_OK) { printf("mount failed, %i\n", SPIFFS_errno(&__fs)); } |
Попробуем создать файл. Для этого мы воспользуемся массивом файла из проекта прошлого занятия. Объявим его и проинициализируем
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
extern spiffs __fs; //------------------------------------------------------ const uint8_t index_htm[] = { 0x3c,0x68,0x74,0x6d,0x6c,0x3e,0x3c,0x62,0x6f,0x64,0x79,0x3e,0x3c,0x68,0x31,0x20, 0x73,0x74,0x79,0x6c,0x65,0x3d,0x22,0x74,0x65,0x78,0x74,0x2d,0x61,0x6c,0x69,0x67, 0x6e,0x3a,0x20,0x63,0x65,0x6e,0x74,0x65,0x72,0x3b,0x22,0x3e,0x45,0x73,0x70,0x20, 0x38,0x32,0x36,0x36,0x3c,0x62,0x72,0x3e,0x3c,0x62,0x72,0x3e,0x48,0x54,0x54,0x50, 0x20,0x53,0x65,0x72,0x76,0x65,0x72,0x3c,0x2f,0x68,0x31,0x3e,0x0d,0x0a,0x3c,0x70, 0x3e,0x3c,0x73,0x70,0x61,0x6e,0x20,0x73,0x74,0x79,0x6c,0x65,0x3d,0x22,0x66,0x6f, 0x6e,0x74,0x2d,0x66,0x61,0x6d,0x69,0x6c,0x79,0x3a,0x20,0x54,0x69,0x6d,0x65,0x73, 0x20,0x4e,0x65,0x77,0x20,0x52,0x6f,0x6d,0x61,0x6e,0x2c,0x54,0x69,0x6d,0x65,0x73, 0x2c,0x73,0x65,0x72,0x69,0x66,0x3b,0x22,0x3e,0x57,0x69,0x2d,0x46,0x69,0x20,0x53, 0x6f,0x43,0x3c,0x2f,0x73,0x70,0x61,0x6e,0x3e,0x20,0x3c,0x2f,0x70,0x3e,0x0d,0x0a, 0x3c,0x2f,0x62,0x6f,0x64,0x79,0x3e,0x3c,0x2f,0x68,0x74,0x6d,0x6c,0x3e}; //------------------------------------------------------ |
Вернёмся в user_init и попытаемся создать файл
1 2 3 4 5 |
printf("mount failed, %i\n", SPIFFS_errno(&__fs)); } res = SPIFFS_creat(&__fs, "index.html", 0); os_printf("SPIFFS_creat: res %d\n", res); |
Объявим локальную переменную для дискриптора файла
1 2 |
int res; spiffs_file fd; |
Откроем файл на чтение и запись
1 2 3 4 |
os_printf("SPIFFS_creat: res %d\n", res); fd = SPIFFS_open(&__fs, "index.html", SPIFFS_CREAT | SPIFFS_RDWR, 0); os_printf("SPIFFS_open: fd %d\n", fd); |
Попытаемся записать в него данные массива
1 2 3 4 |
os_printf("SPIFFS_open: fd %d\n", fd); res = SPIFFS_write(&__fs, fd, (void*)index_htm, sizeof(index_htm)); os_printf("SPIFFS_write: res %d\n", res); |
Закроем файл
1 2 3 |
os_printf("SPIFFS_write: res %d\n", res); SPIFFS_close(&__fs, fd); |
Теперь нам как-то надо узнать, что файл у нас появился. Для начала узнаем информацию вообще о файловой системе — каков её размер и сколько его уже занято.
В функции task1 объявим две переменных
1 2 3 4 |
void ICACHE_FLASH_ATTR task1(void *pvParameters) { u32_t total; u32_t used; |
В бесконечном цикле узнаем наши размеры
1 2 3 |
os_printf("Heap free size: %d\n", xPortGetFreeHeapSize()); SPIFFS_info(&__fs, &total, &used); os_printf("SPIFFS: total %d, used %d\n", total, used); |
Соберём проект, прошьём контроллер и посмотрим, что у нас вышло.
На этапе инициализации мы видим только служебные сообщения, никаких сообщений об ошибках мы не видим
Значит весь этап инициализации и создания файла прошел успешно. А вот информация в бесконечном цикле
Отлично!
Теперь давайте узнаем побольше информации, прочитав из файловой системы весь список файлов, для чего в бесконечном цикле добавим вот такой код
1 2 3 4 5 6 7 8 9 |
os_printf("SPIFFS: total %d, used %d\n", total, used); spiffs_DIR d; struct spiffs_dirent e; struct spiffs_dirent *pe = &e; SPIFFS_opendir(&__fs, "/", &d); while ((pe = SPIFFS_readdir(&d, pe))) { printf("%s [%04x] size:%i\n", pe->name, pe->obj_id, pe->size); } SPIFFS_closedir(&d); |
Думаю, здесь всё понятно. Мы объявляем переменную структуры DIR для доступа к директории, затем объявляем ещё одну подобную переменную, только для одной файловой записи в каталоге, затем переменную-указатель на неё, читаем каталог, пробегаемся по списку, выводя информацию о каждой записи.
Прежде чем это пробовать, в функции user_init() закомментируем форматирование, повторное монтирование, а также создание файла и запись в него, чтобы у нас каждый раз всё заново не пересоздавалось, так как содержимое файловой системы не затирается при повторной прошивке кода и никуда у нас ничего не денется
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
/* SPIFFS_unmount(&__fs); res = SPIFFS_format(&__fs); if (res != SPIFFS_OK) { printf("format failed, %i\n", SPIFFS_errno(&__fs)); } res = mount_fs(); if (res != SPIFFS_OK) { printf("mount failed, %i\n", SPIFFS_errno(&__fs)); } res = SPIFFS_creat(&__fs, "index.html", 0); os_printf("SPIFFS_creat: res %d\n", res); fd = SPIFFS_open(&__fs, "index.html", SPIFFS_CREAT | SPIFFS_RDWR, 0); os_printf("SPIFFS_open: fd %d\n", fd); res = SPIFFS_write(&__fs, fd, (void*)index_htm, sizeof(index_htm)); os_printf("SPIFFS_write: res %d\n", res); SPIFFS_close(&__fs, fd); */ |
Соберём код, прошьём контроллер и посмотрим, что у нас выведется
У нас есть один файл, у него есть имя, идентификатор, а также он имеет определённый размер. Отлично!
Теперь давайте в user_init() просто прочитаем содержимое нашего файла, для чего мы уже откроем файл только на чтение. Для начала объявим небольшой буфер
1 2 3 |
void ICACHE_FLASH_ATTR user_init() { uint8_t buf[500] = {0}; |
Теперь после закомментированного кода проделаем наши операции
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
SPIFFS_close(&__fs, fd); */ fd = SPIFFS_open(&__fs, "index.html", SPIFFS_RDONLY, 0); os_printf("SPIFFS_open: fd %d\n", fd); res = SPIFFS_read(&__fs, fd, (void*)buf, 500); os_printf("SPIFFS_read: res %d\n", res); buf[res] = 0; os_printf("%s\n", (char*)buf); SPIFFS_close(&__fs, fd); |
Испытаем наш код, собрав проект и прошив контроллер
Содержимое нашего файла полностью вывелось в терминальную программу.
Итак, на данном уроке мы научились пользоваться файловой системой SPIFFS. Пусть мы её освоили только немного, это лишь один урок. Но, тем не менее, мы теперь умеем её создать, отформатировать, создавать и читать файлы, а также выводить информацию о размере, свободном месте, а также список файлов с именами, идентификаторами и их размерами. Согласитесь, для одного урока это немало. Причём это не в IDE Arduino, а с использованием чистого SDK.
Всем спасибо за внимание!
Предыдущий урок Программирование МК ESP8266 Следующий урок
Модуль ESP NodeMCU можно купить здесь: Модуль ESP NodeMCU
Различные модули ЕSP8266 можно приобрести здесь Модули ЕSP8266
Переходник I2C to LCD можно приобрести здесьI2C to LCD1602 2004
Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
Спасибо! Очень интересно! А где видео?
Простите, случайно опубликовал черновик. Теперь есть и видео.