ESP32:Примеры/Создание веб-сервера на базе ESP32 при помощи файлов из файловой системы (SPIFFS)

Материал из Онлайн справочника
Перейти к навигацииПерейти к поиску

Перевод: Максим Кузьмин
Проверка/Оформление/Редактирование: Мякишев Е.А.


Создание веб-сервера на базе ESP32 при помощи файлов из файловой системы (SPIFFS)

В руководстве «SPIFFS платы ESP32 (файловая система памяти SPI Flash)» мы рассказывали, как с помощью файловой системы SPIFFS загружать и манипулировать файлами, хранящимися на flash-памяти ESP32. В этом руководстве мы покажем, как создать веб-сервер, использующий HTML- и CSS-файлы, хранящиеся в файловой системе.

Веб-сервер, который мы сделаем в этом руководстве, будет похож на веб-сервер, который мы сделали в руководстве «Веб-сервер на базе ESP32: управление выходными контактами».

Чтобы продолжить дальше, в IDE Arduino должен быть установлен плагин загрузчика SPIFFS. О том, как это сделать, читайте в руководстве «SPIFFS платы ESP32 (файловая система памяти SPI Flash)».

Обзор проекта

Для начала давайте в общих чертах обрисуем, что будет делать наш веб-сервер – чтобы нам было проще над ним работать.

  • Веб-сервер будет управлять светодиодом, подключенным к контакту GPIO2 на ESP32 (это встроенный светодиод ESP32). Но вы можете задать для этого любой другой GPIO-контакт;
  • На странице веб-сервера будет показано две кнопки: «ON» и «OFF» – для включения и выключения контакта GPIO2;
  • Также страница веб-сервера будет показывать текущее состояние контакта GPIO2;
  • На изображении ниже схематически изображено, как все это будет работать:
  • Для создания веб-сервера будет использоваться библиотека «ESPAsyncWebServer»;
  • HTML- и CSS-файлы будут храниться в файловой системе ESP32 (SPIFFS);
  • Когда вы при помощи браузера будете делать запрос на определенный URL, ESP32 будет отвечать на это отправкой запрошенных файлов;
  • Когда вы будете кликать на кнопку «ON», вас перенаправит к корневому URL, в конце которого будет стоять «/on», а также заставит светодиод включиться;
  • Когда вы будете кликать на кнопку «OFF», вас перенаправит к корневому URL, в конце которого будет стоять «/off», а также заставит светодиод выключиться;
  • На веб-странице есть поле для текущего состояния GPIO-контакта. В HTML-файле оно задается между двумя символами «%» (например, «%STATE%»).

Устанавливаем библиотеки

До сих пор мы писали HTML- и CSS-код для веб-сервера напрямую в скетч IDE Arduino. Но при помощи SPIFFS мы можем записывать HTML- и CSS-данные в отдельные файлы и сохранять их в файловую систему ESP32.

Один из самых простых способов создания веб-сервера с использованием файлов из файловой системы – при помощи библиотеки «ESPAsyncWebServer» (исчерпывающую документацию о ней можно найти на ее GitHub-странице).

Устанавливаем библиотеку «ESPAsyncWebServer»

  1. Кликните тут, чтобы скачать ZIP-архив библиотеки. Он должен скачаться в папку «Загрузки» на вашем ПК;
  2. Распакуйте этот архив. У вас должна получиться папка «ESPAsyncWebServer-master»;
  3. Переименуйте ее на «ESPAsyncWebServer»;
  4. Переместите папку «ESPAsyncWebServer» в папку «libraries» (туда устанавливаются библиотеки для IDE Arduino), которая находится внутри папки, куда установлена IDE Arduino;

Устанавливаем библиотеку «AsyncTCP»

Библиотеке «ESPAsyncWebServer» необходима для работы библиотека «AsyncTCP».

  1. Кликните тут, чтобы скачать ZIP-архив библиотеки. Он должен скачаться в папку «Загрузки» на вашем ПК;
  2. Распакуйте этот архив. У вас должна получиться папка «AsyncTCP-master»;
  3. Переименуйте ее на «AsyncTCP»;
  4. Переместите папку «AsyncTCP» в папку «libraries» (туда устанавливаются библиотеки для IDE Arduino), которая находится внутри директории, куда установлена IDE Arduino;
  5. Наконец, перезапустите IDE Arduino.

Правильно раскладываем файлы

Для создания веб-сервера нам понадобится три разных файла – скетч IDE Arduino, HTML-файл и CSS-файл. Файлы для HTML и CSS должны храниться внутри папки «data», которая должна находиться внутри папки скетча IDE Arduino. Вот так:

Создание HTML-файла

HTML-код для этого проекта очень прост. Нам нужно просто создать заголовок для веб-страницы, абзац для показа текущего состояния GPIO-контакта и две кнопки.

Создаем файл «index.html» со следующим содержимым:

<!DOCTYPE html>
<html>
<head>
  <title>ESP32 Web Server</title>
  <meta name="viewport" content="width=device-width, initialscale=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>
Примечание

Поскольку код для HTML и CSS находится в разных файлах, в HTML-файле необходима ссылка на CSS-файл. Для этого между тегами <head> и </head> нужно добавить вот такую строчку:

<link rel="stylesheet" type="text/css" href="style.css">

Тег <link> говорит HTML-файлу, что мы будем использовать внешний файл для оформления того, как будет выглядеть веб-страница. Атрибут «rel» указывает на то, что этим внешним файлом будет таблица стилей.

Атрибут «type» со значением «text/css» указывает, что для стилей будет использоваться CSS-файл. Атрибут «href» указывает место, где хранится файл, и поскольку оба файла (для CSS и HTML) находятся в одной папке, нам достаточно написать в нем «style.css».

В следующей строчке пишем главный заголовок веб-страницы. В нашем случае это «ESP32 Web Server» («Веб-сервер на базе ESP32»). Но вы, если хотите, можете вписать здесь все, что угодно.

<title>ESP32 Web Server</title>

Затем пишем абзац с текстом «GPIO state:» («Состояние GPIO-контакта»), после которого будет стоять само это состояние. Так как это динамическое значение, меняющееся в зависимости от текущего состояния GPIO-контакта, место для него нужно сделать так называемой «заглушкой».

Заглушка делается с помощью двух символов «%» (например, в нашем случае это «%STATE%»).

<p>GPIO state: <strong> %STATE% </strong></p>

Присвоение значения для заглушки «STATE» выполняется в скетче IDE Arduino.

Затем создаем кнопки «ON» и «OFF». В результате при клике на кнопку «ON» нас будет перенаправлять к веб-странице с корневым URL, после которого будет идти «/on», а при клике на «OFF» – к веб-странице с корневым 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 можно найти в этом руководстве, а более подробное – на этом сайте.

Скетч IDE Arduino

Скопируйте код ниже в IDE Arduino:

Укажите в нем SSID и пароль для своей WiFi-сети.

/*********
  Руи Сантос
  Более подробно о проекте на: https://randomnerdtutorials.com  
*********/

// Импортируем необходимые библиотеки:
#include "WiFi.h"
#include "ESPAsyncWebServer.h"
#include "SPIFFS.h"

// Вставьте ниже SSID и пароль для своей WiFi-сети:
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// Задаем GPIO-контакт, к которому подключен светодиод:
const int ledPin = 2;

// Создаем экземпляр класса «AsyncWebServer»
// под названием «server» и задаем ему номер порта «80»:
AsyncWebServer server(80);

// Меняем заглушку на текущее состояние светодиода:
String processor(const String& var){
  // Создаем переменную для хранения состояния светодиода:
  String ledState;

  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("An Error has occurred while mounting SPIFFS");
               //  "При монтировании SPIFFS произошла ошибка"
    return;
  }

  // Подключаемся к WiFi:
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
               //  "Подключаемся к WiFi..."
  }

  // Печатаем в мониторе порта локальный IP-адрес ESP32:
  Serial.println(WiFi.localIP());

  // URL для корневой страницы веб-сервера:
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/index.html", String(), false, processor);
  });
  
  // URL для файла «style.css»:
  server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/style.css", "text/css");
  });

  // URL для переключения GPIO-контакта на «HIGH»:
  server.on("/on", HTTP_GET, [](AsyncWebServerRequest *request){
    digitalWrite(ledPin, HIGH);    
    request->send(SPIFFS, "/index.html", String(), false, processor);
  });
  
  // URL для переключения 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"

В двух строчках ниже вписываем SSID и пароль для своей WiFi-сети.

const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Далее создаем переменную «ledPin», хранящую номер контакта (GPIO2), к которому подключен светодиод.

// Задаем GPIO-контакт для светодиода:
const int ledPin = 2;

Создаем экземпляр класса «AsyncWebServer» под названием «server» и задаем ему номер порта «80» (через него будет вестись прослушка запросов).

AsyncWebServer server(80);

Функция processor()

Эта функция меняет на веб-странице заглушку, которую мы создали в HTML-файле, на текущее значение GPIO-контакта светодиода. Единственным параметром для этой функции служит заглушка, а возвращаемым значением – значение типа «String», которое будет заменять эту заглушку. Функция processor() имеет следующую структуру:

// Меняем заглушку на состояние светодиода:
String processor(const String& var){
  // Создаем переменную для хранения состояния светодиода:
  String ledState;

  Serial.println(var);
  if(var == "STATE"){
    if(digitalRead(ledPin)){
      ledState = "ON";
    }
    else{
      ledState = "OFF";
    }
    Serial.print(ledState);
    return ledState;
  }
  return String();
}

Сперва функция проверяет, является ли заглушка значением «STATE», заданным нами в HTML-файле.

if(var == "STATE"){

Если да, то функция processor() заменит значение в переменной «ledState» на «ON» или «OFF» – в зависимости от текущего состояния GPIO-контакта светодиода.

if(digitalRead(ledPin)){
  ledState = "ON";
}
else{
  ledState = "OFF";
}

Наконец, возвращаем значение переменной «ledState» – оно заменяет значение в заглушке.

return ledState;

setup()

Блок setup() начинается с запуска последовательной коммуникации и переключения GPIO-контакта светодиода в режим «OUTPUT» (т.е. режим вывода данных).

// Включаем последовательную коммуникацию (для отладки):
Serial.begin(115200);
pinMode(ledPin, OUTPUT);

// Инициализируем SPIFFS:
if(!SPIFFS.begin(true)){
  Serial.println("An Error has occurred while mounting SPIFFS");
               //  "При монтировании SPIFFS произошла ошибка"
  return;
}

Подключение к WiFi

Далее подключаемся к WiFi и печатаем IP-адрес ESP32:

// Подключаемся к WiFi:
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
  delay(1000);
  Serial.println("Connecting to WiFi..");
               //  "Подключаемся к WiFi..."
}

// Печатаем в мониторе порта локальный IP-адрес ESP32:
Serial.println(WiFi.localIP());

Асинхронный веб-сервер

С помощью библиотеки «ESPAsyncWebServer» мы можем настроить веб-сервер так, чтобы он прослушивал входящие HTTP-запросы на разные URL и – в зависимости от полученного запроса – выполнял те или иные функции. Для этого мы будем использовать метод on() на объекте «server».

server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ request->send(SPIFFS, "/index.html", String(), false, processor); });

Получив запрос на корневой URL «/», сервер отправит клиенту файл «index.html». Последний параметр функции send() – это функция processor(), меняющая заглушку на необходимое нам значение (в данном случае – на значение из переменной «ledState»).

Поскольку мы в HTML-файле ссылаемся на CSS-файл, клиент также сделает запрос на этот CSS-файл, и веб-сервер в ответ вышлет его клиенту.

server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(SPIFFS, "/style.css", "text/css");
});

Наконец, задаем, что произойдет, если пользователь сделает запрос на URL «/on» и «/off» – светодиод либо включится, либо выключится, а ESP32 соответствующим образом поменяет веб-страницу в браузере.

// URL для переключения GPIO-контакта на «HIGH»:
server.on("/on", HTTP_GET, [](AsyncWebServerRequest *request){
  digitalWrite(ledPin, HIGH);    
  request->send(SPIFFS, "/index.html", String(), false, processor);
});
  
// URL для переключения GPIO-контакта на «LOW»:
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». Затем кликните в IDE Arduino на «Скетч» > «Показать папку скетча» (Sketch > Show sketch folder) и создайте в открывшейся директории папку «data». Внутрь нее нужно поместить HTML- и CSS-файлы для этого скетча.

Затем загрузите этот скетч на ESP32, но перед этим убедитесь, что в IDE Arduino выбраны правильные плата и COM-порт, а в скетче заданы правильные SSID и пароль для вашей WiFi-сети.

После загрузки кода нам нужно загрузить все необходимые файлы в файловую систему ESP32. Для этого кликаем в IDE Arduino на «Инструменты» > «ESP32 Sketch Data Upload» (Tools > ESP32 Sketch Data Upload) и ждем, пока файлы загрузятся.

После того, как все загрузится, открываем монитор порта на скорости 115200 бод и жмем на кнопку EN на ESP32. В мониторе порта должен напечататься IP-адрес ESP32.

Демонстрация

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

Итого

Файловая система SPIFFS особенно полезна для хранения HTML- и CSS-файлов, используемых для обслуживания веб-клиентов. Благодаря ей вам не приходится писать код для веб-сервера прямо в скетче IDE Arduino.

Библиотека «ESPAsyncWebServer» позволяет создать веб-сервер, у которого для разных запросов приготовлены разные варианты действий. Кроме того, мы можем добавить в HTML-страницу заглушку, а затем вставить вместо нее необходимое значение – например, данные от датчиков, состояние GPIO-контакта и т.д.

См.также

Внешние ссылки