Веб-сервер ESP32 с использованием SPIFFS (SPI Flash File System)
В этом уроке мы покажем вам, как создать веб-сервер, который обслуживает файлы HTML и CSS, хранящиеся в файловой системе ESP32. Вместо того чтобы писать текст HTML и CSS в скетче Arduino, мы создадим отдельные файлы HTML и CSS.
Для демонстрации веб-сервер, который мы создадим, будет управлять выводом ESP32, но его можно легко адаптировать для других целей, таких как отображение показаний датчиков.
Рекомендуемое чтение: Веб-сервер ESP8266 с использованием SPIFFS
Содержание
Плагин загрузчика файловой системы ESP32
Чтобы следовать этому уроку, у вас должен быть установлен плагин загрузчика файловой системы ESP32 в вашей Arduino IDE. Если его нет, следуйте следующему уроку, чтобы сначала установить его:
Установите ESP32 Filesystem Uploader на Arduino IDE
Обзор проекта
Прежде чем переходить непосредственно к проекту, важно определить, что будет делать наш веб-сервер, чтобы вам было легче его понять.

Веб-сервер, который вы создадите, управляет светодиодом, подключенным к GPIO 2 ESP32. Это встроенный светодиод ESP32. Вы можете управлять любым другим GPIO;
На веб-странице сервера отображаются две кнопки: ВКЛ и ВЫКЛ — для включения и выключения GPIO 2;
На веб-странице сервера также отображается текущее состояние GPIO.
Следующая схема показывает упрощенный диаграмму, чтобы продемонстрировать, как все работает.

ESP32 запускает код веб-сервера на основе библиотеки ESPAsyncWebServer;
Файлы HTML и CSS хранятся на SPIFFS (Serial Peripheral Interface Flash File System) ESP32;
Когда вы делаете запрос на определенный URL, используя ваш браузер, ESP32 отвечает запрошенными файлами;
Когда вы нажимаете кнопку ВКЛ, вы перенаправляетесь на корневой URL, за которым следует /on, и светодиод включается;
Когда вы нажимаете кнопку ВЫКЛ, вы перенаправляетесь на корневой URL, за которым следует /off, и светодиод выключается;
На веб-странице есть заполнитель для состояния GPIO. Заполнитель состояния GPIO записывается непосредственно в файл HTML между знаками %, например %STATE%.
Установка библиотек
В большинстве наших проектов мы создавали файлы HTML и CSS для веб-сервера в виде строки непосредственно в скетче Arduino. С помощью SPIFFS вы можете записать HTML и CSS в отдельные файлы и сохранить их в файловой системе ESP32.
Один из самых простых способов создания веб-сервера, использующего файлы из файловой системы, заключается в использовании библиотеки ESPAsyncWebServer. Библиотека ESPAsyncWebServer хорошо задокументирована на своей странице GitHub. Для получения дополнительной информации о библиотеке перейдите по следующей ссылке:
Установка библиотеки ESPAsyncWebServer
Следуйте следующим шагам для установки библиотеки ESPAsyncWebServer:
Скачайте библиотеку ESPAsyncWebServer. В вашей папке
Загрузки
должен появиться файл с расширением.zip
.Разархивируйте папку .zip, и вы получите папку ESPAsyncWebServer-master.
Переименуйте папку из ESPAsyncWebServer-master в ESPAsyncWebServer.
Переместите папку ESPAsyncWebServer в папку библиотек вашей установки Arduino IDE.
Установка библиотеки Async TCP для ESP32
Библиотека ESPAsyncWebServer требует библиотеку AsyncTCP для работы. Следуйте следующим шагам для установки этой библиотеки:
Скачайте библиотеку AsyncTCP. В вашей папке
Загрузки
должен появиться файл с расширением.zip
.Разархивируйте папку .zip, и вы получите папку AsyncTCP-master.
Переименуйте папку из AsyncTCP-master в AsyncTCP.
Переместите папку AsyncTCP в папку библиотек вашей установки Arduino IDE.
Наконец, перезапустите вашу Arduino IDE.
Организация файлов
Для создания веб-сервера вам понадобятся три разных файла: скетч Arduino, файл HTML и файл CSS. Файлы HTML и CSS должны быть сохранены внутри папки data в папке скетча Arduino, как показано ниже:

Создание файла HTML
HTML для этого проекта очень простой. Нам нужно создать заголовок для веб-страницы, абзац для отображения состояния GPIO и две кнопки.
Создайте файл index.html со следующим содержимым:
<!DOCTYPE html>
<html>
<head>
<title>ESP32 Web Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<h1>ESP32 Web Server</h1>
<p>GPIO state: <strong>%STATE%</strong></p>
<p><a href="/on"><button class="button">ON</button></a></p>
<p><a href="/off"><button class="button button2">OFF</button></a></p>
</body>
</html>
Поскольку мы используем CSS и HTML в разных файлах, нам нужно ссылаться на файл CSS в тексте HTML. Следующая строка должна быть добавлена между тегами
<link rel="stylesheet" type="text/css" href="style.css">
Тег <link> сообщает файлу HTML, что вы используете внешний стиль для форматирования внешнего вида страницы. Атрибут rel указывает на природу внешнего файла, в данном случае это файл стилей — файл CSS, который будет использоваться для изменения внешнего вида страницы.
Атрибут type установлен на «text/css», чтобы указать, что вы используете файл CSS для стилей. Атрибут href указывает местоположение файла; поскольку файлы CSS и HTML будут находиться в одной папке, вам нужно просто указать имя файла: style.css.
В следующей строке мы пишем первый заголовок нашей веб-страницы. В данном случае у нас есть «ESP32 Web Server». Вы можете изменить заголовок на любой текст по вашему желанию:
<h1>ESP32 Web Server</h1>
Затем мы добавляем абзац с текстом «GPIO state: « за которым следует состояние GPIO. Поскольку состояние GPIO меняется в зависимости от состояния GPIO, мы можем добавить заполнитель, который затем будет заменен на любое значение, которое мы установим в скетче Arduino.
Чтобы добавить заполнитель, мы используем знаки %. Чтобы создать заполнитель для состояния, мы можем использовать %STATE%, например:
<p>GPIO state: <strong>%STATE%</strong></p>
Присвоение значения заполнителю STATE выполняется в скетче Arduino.
Затем мы создаем кнопки «ВКЛ» и «ВЫКЛ». Когда вы нажимаете кнопку «ВКЛ», мы перенаправляем веб-страницу на корневой URL, за которым следует /on. Когда вы нажимаете кнопку «ВЫКЛ», вы перенаправляетесь на URL /off.
<p><a href="/on"><button class="button">ON</button></a></p>
<p><a href="/off"><button class="button button2">OFF</button></a></p>
Создание файла CSS
Создайте файл style.css со следующим содержимым:
html {
font-family: Helvetica;
display: inline-block;
margin: 0px auto;
text-align: center;
}
h1 {
color: #0F3376;
padding: 2vh;
}
p {
font-size: 1.5rem;
}
.button {
display: inline-block;
background-color: #008CBA;
border: none;
border-radius: 4px;
color: white;
padding: 16px 40px;
text-decoration: none;
font-size: 30px;
margin: 2px;
cursor: pointer;
}
.button2 {
background-color: #f44336;
}
Это просто базовый файл CSS для установки размера шрифта, стиля и цвета кнопок и выравнивания страницы. Мы не будем объяснять, как работает CSS. Хорошее место для изучения CSS — это веб-сайт W3Schools.
Скетч Arduino
Скопируйте следующий код в Arduino IDE:
// Импорт необходимых библиотек
#include "WiFi.h"
#include "ESPAsyncWebServer.h"
#include "SPIFFS.h"
// Замените на ваши сетевые данные
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
// Установите GPIO для светодиода
const int ledPin = 2;
// Сохраняет состояние светодиода
String ledState;
// Создайте объект AsyncWebServer на порту 80
AsyncWebServer server(80);
// Заменяет заполнитель значением состояния светодиода
String processor(const String& var){
Serial.println(var);
if(var == "STATE"){
if(digitalRead(ledPin)){
ledState = "ON";
}else{
ledState = "OFF";
}
Serial.print(ledState);
return ledState;
}
return String();
}
void setup(){
// Последовательный порт для целей отладки
Serial.begin(115200);
pinMode(ledPin, OUTPUT);
// Инициализация SPIFFS
if(!SPIFFS.begin(true)){
Serial.println("Произошла ошибка при монтировании SPIFFS");
return;
}
// Подключение к Wi-Fi
WiFi.begin(ssid, password);
while(WiFi.status() != WL_CONNECTED){
delay(1000);
Serial.println("Подключение к WiFi..");
}
// Печать локального IP-адреса ESP32
Serial.println(WiFi.localIP());
// Маршрут для корня / веб-страницы
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(SPIFFS, "/index.html", String(), false, processor);
});
// Маршрут для загрузки файла style.css
server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(SPIFFS, "/style.css", "text/css");
});
// Маршрут для установки GPIO в HIGH
server.on("/on", HTTP_GET, [](AsyncWebServerRequest *request){
digitalWrite(ledPin, HIGH);
request->send(SPIFFS, "/index.html", String(), false, processor);
});
// Маршрут для установки GPIO в LOW
server.on("/off", HTTP_GET, [](AsyncWebServerRequest *request){
digitalWrite(ledPin, LOW);
request->send(SPIFFS, "/index.html", String(), false, processor);
});
// Запуск сервера
server.begin();
}
void loop(){
}
Как работает код
Во-первых, импортируйте необходимые библиотеки:
#include "WiFi.h"
#include "ESPAsyncWebServer.h"
#include "SPIFFS.h"
Введите ваши сетевые данные в следующие переменные:
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
Далее создайте переменную, относящуюся к GPIO 2, называемую ledPin
, и строковую переменную для хранения состояния светодиода ledState
:
const int ledPin = 2;
String ledState;
Создайте объект AsyncWebServer
, называемый server
, который будет слушать на порту 80:
AsyncWebServer server(80);
Функция processor()
Функция processor() предназначена для того, чтобы присвоить значение заполнителю, созданному в файле HTML. Она принимает в качестве аргумента заполнитель и должна возвращать строку, которая заменит этот заполнитель. Функция processor() должна иметь следующую структуру:
Определим функцию processor, которая будет обрабатывать заполнитель в HTML-шаблоне:
String processor(const String& var) {
Serial.println(var);
if (var == "STATE") {
if (digitalRead(ledPin)) {
ledState = "ON";
} else {
ledState = "OFF";
}
Serial.print(ledState);
return ledState;
}
return String();
}
Эта функция сначала проверяет, является ли заполнитель тем, который мы создали в HTML-файле:
if (var == "STATE") {
Если да, то в зависимости от состояния светодиода мы устанавливаем переменную ledState в «ON» или «OFF»:
if (digitalRead(ledPin)) {
ledState = "ON";
} else {
ledState = "OFF";
}
Наконец, возвращается переменная ledState, которая заменит заполнитель в HTML:
return ledState;
Функция setup()
В функции setup()
начните с инициализации Серийного монитора и настройки GPIO как выхода:
Serial.begin(115200);
pinMode(ledPin, OUTPUT);
Затем инициализируйте файловую систему SPIFFS:
if(!SPIFFS.begin(true)){
Serial.println("Произошла ошибка при монтировании SPIFFS");
return;
}
Подключение к Wi-Fi
Подключитесь к Wi-Fi и выведите IP-адрес ESP32:
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Подключение к WiFi..");
}
Serial.println(WiFi.localIP());
Асинхронный веб-сервер
Библиотека ESPAsyncWebServer
позволяет настраивать маршруты, по которым сервер будет принимать входящие HTTP-запросы, и выполнять функции при получении запроса на этом маршруте.
Для этого используйте метод on()
на объекте сервера следующим образом:
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
request->send(SPIFFS, "/index.html", String(), false, processor);
});
Когда сервер получает запрос на корневом URL-адресе "/"
, он отправляет файл index.html
клиенту.
Последним аргументом функции send()
является processor
, чтобы можно было заменить заполнитель на значение (в данном случае — состояние светодиода).
—
Поскольку в HTML-файле есть ссылка на CSS, браузер клиента отправит запрос на файл стилей. В этом случае сервер вернёт его:
server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request) {
request->send(SPIFFS, "/style.css", "text/css");
});
—
Теперь определим поведение для маршрутов /on
и /off
.
Когда приходит запрос на /on
, светодиод включается, а клиенту возвращается index.html
с обновлённым состоянием:
server.on("/on", HTTP_GET, [](AsyncWebServerRequest *request) {
digitalWrite(ledPin, HIGH);
request->send(SPIFFS, "/index.html", String(), false, processor);
});
Когда приходит запрос на /off
— светодиод выключается:
server.on("/off", HTTP_GET, [](AsyncWebServerRequest *request) {
digitalWrite(ledPin, LOW);
request->send(SPIFFS, "/index.html", String(), false, processor);
});
—
В завершение используйте метод begin()
у объекта server
, чтобы запустить веб-сервер:
server.begin();
Поскольку это асинхронный сервер, все маршруты можно определить в функции setup()
.
В loop()
вы можете выполнять другие действия, сервер продолжит обслуживать запросы независимо.
Загрузка кода и файлов
Сохраните код как Async_ESP32_Web_Server
или скачайте все файлы проекта.
Перейдите в меню Скетч > Показать папку скетча и создайте папку с названием data
.
Внутри этой папки сохраните файлы HTML
и CSS
.
Затем загрузите код на вашу плату ESP32. Убедитесь, что выбрана правильная плата и COM-порт. Также убедитесь, что вы добавили данные вашей сети в код.

После загрузки кода вам нужно загрузить файлы. Перейдите в Инструменты> ESP32 Data Sketch Upload и дождитесь загрузки файлов.
Когда все успешно загружено, откройте Серийный монитор на скорости 115200. Нажмите кнопку «ENABLE» на плате ESP32, и должен быть выведен IP-адрес ESP32.

Демонстрация
Откройте ваш браузер и введите IP-адрес ESP32. Нажмите кнопки «ON» и «OFF», чтобы управлять встроенным светодиодом ESP32. Также проверьте, что состояние GPIO обновляется корректно.

Заключение
Использование SPI Flash File System (SPIFFS) особенно полезно для хранения файлов HTML и CSS, чтобы обслуживать их клиенту, вместо того чтобы писать весь код внутри скетча Arduino.