
Создание простого веб-сервера на ESP32 в Arduino IDE
ESP32 стал одним из самых популярных микроконтроллеров в мире IoT — и не зря. Он мощный, доступный по цене и имеет встроенный Wi-Fi и Bluetooth, что делает его идеальным для создания умных подключённых устройств. Одна из самых интересных возможностей ESP32 — это способность работать как автономный веб-сервер!
В этом руководстве вы узнаете, как настроить веб-серверы на ESP32 с помощью Arduino IDE. Чтобы обучение было увлекательным и практичным, мы создадим три проекта:
Счётчик посещений — размещение простой веб-страницы, которая отслеживает и отображает количество визитов.
Монитор температуры — подключение датчика DS18B20 и отображение данных о температуре в реальном времени на веб-странице.
Wi-Fi контроллер светодиодов — создание интерактивного веб-интерфейса для удалённого включения и выключения светодиодов.
Звучит интересно? Давайте начнём!
Что такое веб-сервер и как он работает?
Представьте, что вы в ресторане. Вы садитесь, изучаете меню и говорите официанту, что хотите заказать. Официант относит ваш заказ на кухню, и через несколько минут возвращается именно с тем, что вы попросили. Веб-сервер работает удивительно похожим образом — только вместо еды он доставляет веб-сайты.
Веб-сервер — это, по сути, специализированный компьютер с одной главной задачей: хранить веб-сайты и доставлять их пользователям по запросу. Когда вы вводите адрес веб-сайта в браузере или нажимаете на ссылку, вы, по сути, делаете заказ на веб-сервере.
Когда веб-сервер получает ваш запрос, он быстро ищет среди сохранённых файлов запрашиваемую веб-страницу (HTML-документы, изображения, видео и другой контент, из которого состоит веб-сайт).
Если он находит их, веб-сервер собирает все эти компоненты вместе, упаковывает их в HTTP-ответ и отправляет обратно в ваш браузер.
Затем ваш браузер берёт эту информацию и собирает из неё веб-страницу, которую вы можете видеть и с которой можете взаимодействовать.
Режимы работы Wi-Fi на ESP32
Теперь, когда вы понимаете, что такое веб-сервер, давайте посмотрим, как ESP32 может выступать в этой роли.
Чтобы ESP32 мог функционировать как веб-сервер и обслуживать веб-страницу, он сначала должен установить сетевое подключение, используя один из трёх режимов Wi-Fi: режим станции (STA), режим точки доступа (AP) и двойной режим (AP+STA).
Режим станции (STA)
Когда ваш ESP32 находится в режиме STA, он действует как любое другое устройство, например ноутбук или смартфон, которое хочет подключиться к существующей Wi-Fi сети. Он ищет Wi-Fi сеть, подключается к ней, используя имя сети (SSID) и пароль, и затем становится частью этой сети.
После подключения роутер выдаёт ESP32 собственный IP-адрес. Этот IP-адрес очень важен, потому что с его помощью ESP32 может настроить веб-сервер. Это означает, что ESP32 может обслуживать веб-страницы для всех других подключённых устройств в той же Wi-Fi сети. Так что вы можете управлять своим проектом на ESP32, открыв веб-браузер на телефоне или компьютере. Кроме того, если эта Wi-Fi сеть имеет доступ к интернету, ESP32 также может получать информацию из интернета, например, данные о погоде, или отправлять данные в облачный сервис.
Режим точки доступа (AP)
Когда ESP32 находится в режиме AP, он создаёт собственную Wi-Fi сеть, назначая ей SSID (имя сети), пароль и IP-адрес. Другие устройства, такие как смартфон или компьютер, могут найти эту сеть и подключиться к ней, используя установленный вами пароль. С IP-адресом, который ESP32 назначает сам себе, он может обслуживать веб-страницы напрямую для всех устройств, подключившихся к его новой сети.
Режим AP очень полезен, когда нет доступной Wi-Fi сети, или когда вы хотите создать частную сеть только для своего проекта. Например, если вы собираете робота в гараже, где нет Wi-Fi, вы можете перевести ESP32 в режим AP и подключить телефон напрямую к нему для управления роботом.
Двойной режим
Что делает ESP32 особенно универсальным, так это то, что он может работать в обоих режимах одновременно — это называется режим AP+STA или двойной режим. В такой конфигурации ESP32 может подключаться к существующей Wi-Fi сети как станция, одновременно создавая собственную точку доступа для подключения других устройств. Это открывает интересные возможности, например, создание устройства, которое может расширить зону покрытия Wi-Fi или создать мост между различными сетями.
Пример 1 — Настройка веб-сервера ESP32 в режиме станции (STA)
Теперь, когда мы понимаем основы веб-серверов и знаем, как ESP32 может работать в различных режимах Wi-Fi, давайте применим эти знания в увлекательном практическом проекте.
Обзор проекта
В этом первом проекте мы создадим простой счётчик посещений с использованием ESP32. Идея в том, что ESP32 будет размещать веб-страницу, и каждый раз, когда кто-то посещает эту страницу, счётчик увеличивается на единицу. Это интересный способ увидеть, сколько «визитов» получает веб-страница вашего ESP32!
Чтобы это работало, ESP32 будет действовать как веб-сервер в режиме станции (STA), то есть подключится к вашей существующей Wi-Fi сети. ESP32 отслеживает счётчик посещений внутри себя. Когда вы открываете веб-браузер на телефоне или компьютере и вводите определённый адрес (например, http://192.168.1.1/ — не волнуйтесь, ESP32 сообщит вам свой точный адрес!), ваш браузер отправляет запрос к ESP32. Когда ESP32 получает этот запрос, он увеличивает счётчик посещений на единицу и отправляет обновлённую веб-страницу обратно в ваш браузер, показывая новое значение счётчика.
Довольно просто, не так ли? Каждый раз, когда вы обновляете страницу или кто-то другой посещает её, счётчик увеличивается!
Пример кода
Ниже вы найдёте полный код для этого проекта. Однако перед загрузкой его на ESP32 обязательно обновите две строки в коде — имя вашей Wi-Fi сети (SSID) и пароль Wi-Fi. Эта информация указывает ESP32, к какой сети подключаться.
После внесения изменений загрузите скетч на ESP32.
#include <WiFi.h>
#include <WebServer.h>
/*Put your SSID & Password*/
const char* ssid = "YourNetworkName"; // Enter SSID here
const char* password = "YourPassword"; // Enter Password here
WebServer server(80);
int counter = 0;
void setup() {
Serial.begin(115200);
Serial.println("Connecting to ");
Serial.println(ssid);
//connect to your local wi-fi network
WiFi.begin(ssid, password);
//check wi-fi is connected to wi-fi network
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected..!");
Serial.print("Got IP: ");
Serial.println(WiFi.localIP());
server.on("/", handle_OnConnect);
server.onNotFound(handle_NotFound);
server.begin();
Serial.println("HTTP server started");
}
void loop() {
server.handleClient();
}
void handle_OnConnect() {
counter++;
server.send(200, "text/html", createHTML());
}
void handle_NotFound() {
server.send(404, "text/plain", "Not found");
}
String createHTML() {
String str = "<!DOCTYPE html> <html>";
str += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">";
str += "<style>";
str += "body {font-family: Arial, sans-serif; color: #444; text-align: center;}";
str += ".title {font-size: 30px; font-weight: bold; letter-spacing: 2px; margin: 80px 0 55px;}";
str += ".counter {font-size: 80px; font-weight: 300; line-height: 1; margin: 0px; color: #4285f4;}";
str += "</style>";
str += "</head>";
str += "<body>";
str += "<h1 class=\"title\">VISITOR COUNTER</h1>";
str += "<div class=\"counter\">";
str += counter;
str += "</div>";
str += "</body>";
str += "</html>";
return str;
}
Доступ к веб-серверу в режиме STA
После загрузки скетча откройте монитор порта и убедитесь, что скорость передачи данных установлена на 115200. Затем нажмите кнопку EN (сброс) на ESP32. Если всё работает правильно, ESP32 подключится к вашей Wi-Fi сети и выведет сообщение «WiFi connected..!» вместе с IP-адресом, который ваш роутер назначил ESP32. Вы также увидите «HTTP server started» в мониторе порта.
Теперь возьмите устройство, например телефон или ноутбук, подключённое к той же Wi-Fi сети, что и ESP32. Откройте веб-браузер (Chrome или Firefox) и введите IP-адрес, который появился в мониторе порта. Нажмите Enter, и вы должны увидеть веб-страницу!
На этой странице отображается текущий счётчик посещений. Попробуйте обновить страницу — вы заметите, что счётчик увеличивается каждый раз.
Теперь давайте разберём код шаг за шагом, чтобы вы поняли, как всё работает.
Разбор кода
Мы начинаем с подключения двух важных библиотек: WiFi.h и WebServer.h. Библиотека WiFi.h помогает ESP32 подключаться к Wi-Fi сети, а библиотека WebServer.h позволяет ESP32 работать как веб-сервер, обрабатывающий запросы веб-страниц из браузера.
#include <WiFi.h>
#include <WebServer.h>
Поскольку мы подключаем ESP32 к вашей существующей Wi-Fi сети, мы определяем две переменные для хранения имени сети (SSID) и пароля. Обязательно обновите их данными вашей сети, чтобы ESP32 мог подключиться.
/* Put your SSID & Password */
const char* ssid = "YourNetworkName"; // Enter SSID here
const char* password = "YourPassword"; //Enter Password here
Затем мы создаём объект из библиотеки WebServer. Мы указываем этому объекту прослушивать порт 80. Это стандартный порт для веб-трафика, поэтому вам не нужно вводить специальный номер порта при посещении веб-страницы в браузере.
// declare an object of WebServer library
WebServer server(80);
Мы также создаём простую целочисленную переменную counter и устанавливаем её в 0. Здесь мы будем отслеживать, сколько раз кто-то посетил веб-страницу.
int counter = 0;
Функция Setup()
В функции setup() мы сначала открываем последовательное соединение для отправки сообщений на компьютер.
Serial.begin(115200);
Для подключения к Wi-Fi сети мы используем функцию WiFi.begin() и передаём ей имя и пароль вашей Wi-Fi сети.
WiFi.begin(ssid, password);
Пока ESP32 пытается подключиться, мы проверяем статус подключения с помощью WiFi.status(). Если он ещё не подключён, программа ждёт и пробует снова, пока подключение не будет установлено.
//check wi-fi is connected to wi-fi network
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Для справки, функция WiFi.status() может возвращать следующие статусы:
WL_CONNECTED: Подключён к Wi-Fi сети.WL_NO_SHIELD: Wi-Fi модуль не обнаружен (не применимо к ESP32, но актуально для некоторых плат Arduino).WL_IDLE_STATUS: Временный статус во время попытки подключения после вызоваWiFi.begin().WL_NO_SSID_AVAIL: Не найдено сетей, соответствующих указанному SSID.WL_SCAN_COMPLETED: Сканирование сети завершено.WL_CONNECT_FAILED: Не удалось подключиться после всех попыток.WL_CONNECTION_LOST: Соединение с сетью потеряно.WL_DISCONNECTED: Не подключён ни к одной сети.
Когда ESP32 подключён к Wi-Fi, он получает IP-адрес. Мы выводим этот IP-адрес в монитор порта с помощью WiFi.localIP(). Это адрес, который вы будете вводить в браузере для доступа к веб-странице.
Serial.println("WiFi connected..!");
Serial.print("Got IP: ");
Serial.println(WiFi.localIP());
После подключения ESP32 мы указываем веб-серверу, что делать, когда кто-то посещает определённый веб-адрес. Мы делаем это с помощью функции server.on(). Этот метод позволяет нам сказать: «Когда кто-то посетит этот веб-адрес, выполни этот конкретный фрагмент кода». Например, когда кто-то посещает главную страницу (обозначенную /), ESP32 запустит функцию handle_OnConnect().
server.on("/", handle_OnConnect);
Но что если кто-то случайно введёт неправильный или случайный веб-адрес, которого не существует? Для обработки этого мы используем server.onNotFound(). Это сообщает нашему ESP32, что если он не может найти запрашиваемую страницу, он должен показать сообщение об ошибке «404 Not Found», что является стандартным способом сказать «Страница не найдена».
server.onNotFound(handle_NotFound);
Наконец, мы используем server.begin() для запуска веб-сервера и начала прослушивания входящих запросов.
server.begin();
Serial.println("HTTP server started");
Функция Loop()
В функции loop() у нас только одна строка — server.handleClient() — которая постоянно проверяет, пытается ли какое-либо устройство (например, браузер) отправить запрос к ESP32. Если да, ESP32 отвечает с помощью функции, которую мы определили ранее.
void loop() {
server.handleClient();
}
Обработка веб-запросов
Теперь давайте рассмотрим пользовательские функции, которые мы привязали к определённым веб-адресам с помощью server.on().
Функция handle_OnConnect() вызывается каждый раз, когда кто-то посещает главную страницу нашего веб-сервера. Внутри этой функции мы делаем две основные вещи: увеличиваем счётчик посещений на единицу, а затем отправляем веб-страницу с обновлённым значением счётчика обратно в браузер.
Для отправки веб-страницы в браузер мы используем функцию server.send(). Хотя этот метод может принимать несколько аргументов, наиболее распространённая форма включает три: код HTTP-ответа, тип контента и сам контент веб-страницы.
Первый аргумент — код ответа
200, стандартный HTTP-код для успешного запроса (OK).Второй аргумент — тип контента
"text/html", указывающий, что сервер отправляет HTML-страницу.Третий аргумент — результат функции
createHTML(), которая возвращает динамически сгенерированную HTML-строку с актуальным количеством посещений.
void handle_OnConnect() {
counter++;
server.send(200, "text/html", createHTML());
}
Если пользователь пытается получить доступ к несуществующей странице, ESP32 запускает функцию handle_NotFound(), которая просто отправляет сообщение об ошибке 404 — так же, как любой обычный веб-сайт при вводе неправильного URL.
void handle_NotFound() {
server.send(404, "text/plain", "Not found");
}
Отображение HTML веб-страницы
Теперь поговорим о функции createHTML(). Эта пользовательская функция берёт текущее значение счётчика посещений и использует его для построения полной HTML-страницы с нуля, поэтому веб-страница, которую вы видите в браузере, всегда показывает самый актуальный счёт.
В начале HTML мы включаем <!DOCTYPE html>, чтобы сообщить браузеру, что это HTML5-документ. Мы также включаем тег <meta>, который обеспечивает корректное отображение страницы на всех размерах экранов, таких как телефоны и планшеты.
String createHTML(){
String str = "<!DOCTYPE html> <html>\n";
str +="<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
Стилизация веб-страницы
Затем мы используем CSS-стили, чтобы страница выглядела аккуратно. Это включает установку шрифта Arial, центрирование текста, выбор цветов и задание размера текста счётчика — крупного и легко читаемого.
str += "<style>";
str += "body {font-family: Arial, sans-serif; color: #444; text-align: center;}";
str += ".title {font-size: 30px; font-weight: bold; letter-spacing: 2px; margin: 80px 0 55px;}";
str += ".counter {font-size: 80px; font-weight: 300; line-height: 1; margin: 0px; color: #4285f4;}";
str += "</style>";
Заголовок веб-страницы
Далее мы добавляем основной заголовок веб-страницы.
str += "<h1 class=\"title\">VISITOR COUNTER</h1>";
Отображение счётчика посещений
После этого мы вставляем фактическое значение счётчика прямо в HTML. Именно так число, которое вы видите на веб-странице, меняется каждый раз при посещении.
str += "<div class=\"counter\">";
str += counter;
str += "</div>";
Наконец, функция завершает HTML, закрывая все необходимые теги. Когда полный HTML-код собран, он отправляется обратно в функцию server.send(), которая доставляет его в ваш браузер.
str += "</body>\n";
str += "</html>\n";
return str;
Пример 2 — Настройка веб-сервера ESP32 в режиме точки доступа (AP)
Для второго проекта мы создадим тот же веб-сервер со счётчиком посещений на ESP32, но на этот раз в режиме точки доступа (AP).
Представьте, что вы работаете над проектом ESP32 в месте без Wi-Fi — например, в гараже или на улице. Вот тут-то и пригодится режим AP. Вместо подключения к существующей Wi-Fi сети ESP32 создаст собственную Wi-Fi сеть. Затем ваш телефон, ноутбук или планшет могут подключиться к ней напрямую. По сути, ESP32 превращается в Wi-Fi точку доступа!
Перед загрузкой кода на ESP32 вы можете изменить две вещи: выбрать имя сети (SSID) и пароль, которые ESP32 будет использовать для создания Wi-Fi сети. Эти данные понадобятся для подключения других устройств к сети ESP32.
/* Put your SSID & Password */
const char* ssid = "ESP32"; // Enter SSID here
const char* password = "12345678"; // Enter Password here
После настройки загрузите скетч на ESP32.
#include <WiFi.h>
#include <WebServer.h>
/* Put your SSID & Password */
const char* ssid = "ESP32"; // Enter SSID here
const char* password = "12345678"; // Enter Password here
/* Put IP Address details */
IPAddress local_ip(192,168,1,1);
IPAddress gateway(192,168,1,1);
IPAddress subnet(255,255,255,0);
WebServer server(80);
int counter = 0;
void setup() {
Serial.begin(115200);
WiFi.softAP(ssid, password);
WiFi.softAPConfig(local_ip, gateway, subnet);
delay(100);
server.on("/", handle_OnConnect);
server.onNotFound(handle_NotFound);
server.begin();
Serial.println("HTTP server started");
}
void loop() {
server.handleClient();
}
void handle_OnConnect() {
counter++;
server.send(200, "text/html", createHTML());
}
void handle_NotFound() {
server.send(404, "text/plain", "Not found");
}
String createHTML() {
String str = "<!DOCTYPE html> <html>";
str += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">";
str += "<style>";
str += "body {font-family: Arial, sans-serif; color: #444; text-align: center;}";
str += ".title {font-size: 30px; font-weight: bold; letter-spacing: 2px; margin: 80px 0 55px;}";
str += ".counter {font-size: 80px; font-weight: 300; line-height: 1; margin: 0px; color: #4285f4;}";
str += "</style>";
str += "</head>";
str += "<body>";
str += "<h1 class=\"title\">VISITOR COUNTER</h1>";
str += "<div class=\"counter\">";
str += counter;
str += "</div>";
str += "</body>";
str += "</html>";
return str;
}
Доступ к веб-серверу в режиме AP
После загрузки кода откройте монитор порта и установите скорость передачи данных 115200. Затем нажмите кнопку EN на ESP32. Если всё работает правильно, вы увидите сообщение «HTTP server started» в мониторе порта. Это означает, что ESP32 успешно создал собственную Wi-Fi сеть и готов обслуживать веб-страницу.
Теперь возьмите устройство, например телефон или ноутбук, которое может подключаться к Wi-Fi сети. Найдите новую Wi-Fi сеть с именем «ESP32» (или какой SSID вы выбрали в коде). Подключитесь к этой сети, используя пароль, установленный в коде (по умолчанию в этом примере — «12345678»).
После подключения вашего устройства к Wi-Fi сети ESP32 откройте веб-браузер и введите 192.168.1.1 в адресную строку. Это IP-адрес, который мы вручную установили в коде для ESP32 при настройке режима AP. Если вы измените IP-адрес в коде, обязательно введите новый адрес. Нажмите Enter, и вы должны увидеть веб-страницу со счётчиком посещений, как и в предыдущем примере.
Разбор кода
Большая часть этого кода аналогична предыдущему примеру. Главное отличие в том, что вместо подключения к существующей Wi-Fi сети ESP32 создаёт собственную сеть.
Сначала мы настраиваем параметры Wi-Fi сети, которую создаст ESP32. Сюда входят SSID (имя сети, например «ESP32»), пароль и важные настройки IP-адреса. Это включает локальный IP (который вы вводите в браузере для посещения веб-страницы), шлюз (обычно совпадает с локальным IP) и маску подсети (определяющую диапазон адресов в сети).
/* Put your SSID & Password */
const char* ssid = "ESP32"; // Enter SSID here
const char* password = "12345678"; //Enter Password here
/* Put IP Address details */
IPAddress local_ip(192,168,1,1);
IPAddress gateway(192,168,1,1);
IPAddress subnet(255,255,255,0);
В функции setup() мы используем WiFi.softAP() для запуска ESP32 в режиме точки доступа. Здесь мы передаём SSID и пароль, определённые ранее. Сразу после этого мы используем WiFi.softAPConfig() для настройки IP-адреса, шлюза и маски подсети для новой сети. Небольшая задержка добавлена, чтобы дать ESP32 время на настройку сети.
WiFi.softAP(ssid, password);
WiFi.softAPConfig(local_ip, gateway, subnet);
delay(100);
Это практически всё, что нового для настройки режима AP! Остальной код точно такой же, как мы обсуждали в примере режима STA. Он включает те же настройки для обработки входящих запросов веб-страниц, способ построения веб-страницы с помощью HTML и её отправку обратно в ваш браузер.
Пример 3 — Отображение показаний датчика на веб-сервере ESP32
Давайте пойдём дальше. На этот раз мы будем отображать реальные данные — а именно показания температуры с датчика — прямо на веб-странице, размещённой на ESP32.
Обзор проекта
В этом проекте мы настроим ESP32 как веб-сервер в режиме станции (STA), так же как наш первый счётчик посещений. Каждый раз, когда кто-то посещает главную страницу веб-сервера, ESP32 немедленно считывает текущую температуру с датчика DS18B20 и отправляет полную веб-страницу, показывающую температуру в градусах Цельсия и Фаренгейта.
Эти значения обновляются каждый раз при запросе страницы. Поэтому, если вы хотите увидеть последнюю температуру, вам нужно просто обновить веб-страницу в браузере.
И не беспокойтесь, если у вас нет именно этого датчика DS18B20 — ту же идею можно использовать для отображения данных практически с любого другого датчика!
Подключение схемы
Подключение датчика DS18B20 к ESP32 — простая задача.
Сначала подключите вывод VDD датчика к контакту 3.3V на ESP32. Затем подключите вывод GND (земля) датчика к любому выводу GND на ESP32.
Для передачи данных подключите вывод DQ (данные) датчика к GPIO 15 на ESP32. Для обеспечения стабильности сигнала данных необходимо добавить подтягивающий резистор 4,7 кОм между выводом DQ и выводом питания 3.3V.
Вот краткая таблица подключений:
DS18B20 |
ESP32 |
Примечания |
|---|---|---|
VDD |
3.3V |
– |
GND |
GND |
– |
DQ |
D15 |
подтянут резистором 4,7 кОм |
На изображении ниже показано, как всё подключить.
Установка библиотеки
Протокол связи 1-Wire, используемый датчиком DS18B20, довольно сложен, и написание всего кода для него с нуля заняло бы много времени. К счастью, есть специальная библиотека DallasTemperature, которая значительно упрощает работу.
Для установки библиотеки:
Откройте Arduino IDE. Затем нажмите на значок Library Manager на левой боковой панели.
Введите «ds18b20» в поле поиска для фильтрации результатов.
Найдите DallasTemperature Library от Miles Burton.
Нажмите кнопку Install, чтобы добавить её в Arduino IDE.
Для корректной работы библиотеке DallasTemperature необходима другая библиотека — OneWire.
Для установки библиотеки OneWire:
Снова откройте Library Manager.
В поле поиска введите «onewire».
Найдите OneWire Library и нажмите Install.
Пример кода
Скопируйте код ниже в Arduino IDE. Как и раньше, вам нужно обновить две вещи — имя Wi-Fi сети (SSID) и пароль.
/*Put your SSID & Password*/
const char* ssid = "YourNetworkName"; // Enter SSID here
const char* password = "YourPassword"; // Enter Password here
После внесения изменений загрузите скетч на ESP32.
#include <WiFi.h>
#include <WebServer.h>
#include <OneWire.h>
#include <DallasTemperature.h>
// Data wire is plugged into digital pin 15 on the ESP32
#define ONE_WIRE_BUS 15
// Setup a oneWire instance to communicate with any OneWire device
OneWire oneWire(ONE_WIRE_BUS);
// Pass oneWire reference to DallasTemperature library
DallasTemperature sensors(&oneWire);
/*Put your SSID & Password*/
const char* ssid = "YourNetworkName"; // Enter SSID here
const char* password = "YourPassword"; // Enter Password here
WebServer server(80);
float Temperature;
void setup() {
Serial.begin(115200);
sensors.begin();
Serial.println("Connecting to ");
Serial.println(ssid);
//connect to your local wi-fi network
WiFi.begin(ssid, password);
//check wi-fi is connected to wi-fi network
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected..!");
Serial.print("Got IP: ");
Serial.println(WiFi.localIP());
server.on("/", handle_OnConnect);
server.onNotFound(handle_NotFound);
server.begin();
Serial.println("HTTP server started");
}
void loop() {
server.handleClient();
}
void handle_OnConnect() {
// Send the command to get temperatures
sensors.requestTemperatures();
Temperature = sensors.getTempCByIndex(0); // Gets the values of the temperature
server.send(200, "text/html", createHTML());
}
void handle_NotFound() {
server.send(404, "text/plain", "Not found");
}
String createHTML() {
String str = "<!DOCTYPE html> <html>";
str += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">";
str += "<style>";
str += "body {font-family: Arial, sans-serif; color: #444; text-align: center;}";
str += ".title {font-size: 30px; font-weight: bold; letter-spacing: 2px; margin: 80px 0 55px;}";
str += ".temperature {font-size: 80px; font-weight: 300; line-height: 1; margin-bottom: 40px; color: #4285f4;}";
str += ".unit {font-size: 28px; vertical-align: top;}";
str += "</style>";
str += "</head>";
str += "<body>";
str += "<div id=\"temperature-container\">";
str += "<h1 class=\"title\">TEMPERATURE</h1>";
str += "<div class=\"temperature celsius\">";
str += String(Temperature, 1);
str += "<span class=\"unit\">°C</span></div>";
str += "<div class=\"temperature fahrenheit\">";
str += String((Temperature * 9.0) / 5.0 + 32.0, 1);
str += "<span class=\"unit\">°F</span></div>";
str += "</div>";
str += "</body>";
str += "</html>";
return str;
}
Доступ к веб-серверу
После загрузки скетча на ESP32 откройте монитор порта и установите скорость 115200. Затем нажмите кнопку EN (сброс) на ESP32. Если всё работает правильно, ESP32 подключится к вашей Wi-Fi сети и выведет IP-адрес, полученный от роутера.
Теперь возьмите устройство — телефон, планшет или компьютер — подключённое к той же Wi-Fi сети, и откройте веб-браузер. В адресной строке введите IP-адрес, отображённый в мониторе порта. Вы должны увидеть стильную веб-страницу с текущими показаниями температуры от датчика DS18B20.
Разбор кода
Этот скетч очень похож на пример веб-сервера в режиме станции (STA), который мы использовали ранее, но с несколькими важными изменениями.
В начале скетча мы подключаем две дополнительные библиотеки. Первая, OneWire.h, позволяет ESP32 общаться с устройствами по протоколу 1-Wire — именно этот протокол использует датчик DS18B20. Вторая, DallasTemperature.h, разработана специально для работы с датчиками DS18B20 и упрощает получение показаний температуры.
#include <OneWire.h>
#include <DallasTemperature.h>
Далее мы определяем, какой GPIO-вывод на ESP32 подключён к линии данных датчика. В этом примере мы используем GPIO 15.
#define ONE_WIRE_BUS 15
Чтобы ESP32 мог общаться с датчиком DS18B20, мы делаем две вещи. Сначала создаём объект OneWire и указываем, к какому выводу подключён датчик. Затем создаём объект DallasTemperature и передаём ему ссылку на только что созданный объект OneWire.
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
Далее мы создаём глобальную переменную Temperature. В этой переменной будет храниться значение, полученное от датчика, чтобы мы могли использовать его при построении веб-страницы.
float Temperature;
В функции setup() мы вызываем sensors.begin(). Это заставляет ESP32 искать датчики DS18B20, подключённые к шине 1-Wire, и устанавливает их разрешение на 12 бит.
sensors.begin();
На этот раз, когда вызывается функция handle_OnConnect() (то есть кто-то посетил нашу главную веб-страницу), первое, что мы делаем — вызываем функцию sensors.requestTemperatures(). Это даёт команду DS18B20 выполнить новое измерение температуры.
После завершения преобразования температуры мы вызываем sensors.getTempCByIndex(deviceIndex) для чтения температуры с датчика. deviceIndex — это число, указывающее ESP32, с какого датчика считывать данные на шине 1-Wire. Поскольку у нас подключён только один датчик, мы используем 0 для обращения к нему. Если бы у вас было несколько датчиков, вы использовали бы 1, 2, 3 и т.д. в зависимости от их порядка на шине. Затем мы сохраняем это показание в переменную Temperature.
Сразу после этого мы отправляем веб-страницу обратно в браузер с помощью server.send() и, как и раньше, строим эту страницу с помощью функции createHTML().
void handle_OnConnect() {
// Send the command to get temperatures
sensors.requestTemperatures();
Temperature = sensors.getTempCByIndex(0); // Gets the values of the temperature
server.send(200, "text/html", createHTML());
}
Функция createHTML() в этом примере значительно отличается от предыдущих. Основная структура HTML остаётся прежней: мы начинаем с фундаментальных тегов <!DOCTYPE html>, <html>, <head> и <meta>.
String createHTML(){
String str = "<!DOCTYPE html>";
str += "<html lang=\"en\">";
str += "<head>";
str += "<meta charset=\"UTF-8\">";
str += "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">";
Мы также включаем CSS-стили, чтобы страница выглядела привлекательно. Текст центрирован, а значения температуры отображаются крупными, легко читаемыми числами. Шрифт и цвета подобраны для максимальной ясности и читабельности.
str += "<style>";
str += "body {margin: 0;padding: 0;height: 100vh;background: linear-gradient(to bottom, #ff4a73, #ffa05a);font-family: Arial, sans-serif;color: white;}";
str += ".temperature-container {text-align: center;}";
str += ".title {font-size: 18px;font-weight: bold;letter-spacing: 2px;margin: 120px 0 40px;}";
str += ".temperature {font-size: 80px;font-weight: 300;line-height: 1;margin-bottom: 30px;}";
str += ".unit {font-size: 28px;vertical-align: top;}";
str += "</style>";
Для отображения фактических показаний температуры на веб-странице мы преобразуем переменную Temperature (число) в строку, чтобы включить её непосредственно в HTML. Мы форматируем значение с одним десятичным знаком. Для температуры по Фаренгейту мы сначала используем математическую формулу для преобразования из Цельсия в Фаренгейт, а затем также преобразуем результат в строку с одним десятичным знаком.
str += "<div class=\"temperature-container\">";
str += "<h1 class=\"title\">TEMPERATURE</h1>";
str += "<div class=\"temperature celsius\">";
str += String(Temperature, 1);
str += "<span class=\"unit\">°C</span></div>";
str += "<div class=\"temperature fahrenheit\">";
str += String((Temperature * 9.0) / 5.0 + 32.0, 1);
str += "<span class=\"unit\">°F</span></div>";
str += "</div>";
Наконец, как и в других примерах, функция закрывает все необходимые HTML-теги и возвращает полную HTML-строку обратно в функцию server.send(), которая доставляет её в ваш браузер.
HTTP-опрос
В предыдущем примере мы научились отображать показания температуры на веб-странице. Однако для получения обновлённых значений приходилось вручную обновлять браузер. Это нормально, если вы хотите проверить температуру лишь время от времени. Но что если вы хотите, чтобы данные обновлялись автоматически и чаще? Постоянно нажимать кнопку обновления непрактично. Здесь на помощь приходит HTTP-опрос (HTTP Polling).
HTTP-опрос — это метод, при котором браузер автоматически отправляет повторные HTTP-запросы к веб-серверу через регулярные интервалы времени. В нашем случае это означает, что браузер постоянно запрашивает у ESP32 последние показания температуры, снова и снова, без необходимости обновлять страницу.
Есть два способа реализации HTTP-опроса:
Автоматическое обновление страницы
Один простой способ — заставить всю веб-страницу перезагружаться автоматически через определённый промежуток времени. Это можно сделать, добавив специальный тег <meta>. Например, следующий код обновляет страницу каждую секунду:
str += "<meta http-equiv=\"refresh\" content=\"1\" >";
Добавьте этот тег внутри секции <head> вашей веб-страницы.
Динамическая загрузка данных датчика с помощью AJAX
Хотя автоматическое обновление всей страницы работает, это не самый эффективный метод — особенно когда меняется лишь небольшой фрагмент информации, например показание температуры. Дело в том, что при каждом обновлении страницы браузер запрашивает все ресурсы заново: HTML, стили (CSS), JavaScript-код и даже изображения. Это создаёт ненужную нагрузку на сервер и может замедлять загрузку страницы.
Более умный и эффективный метод HTTP-опроса — использование AJAX. AJAX расшифровывается как Asynchronous JavaScript and XML, но вам не нужно особо беспокоиться о значении этих букв. Важно то, что AJAX позволяет вашему браузеру запрашивать только конкретный фрагмент информации — например, только показание температуры — без перезагрузки всей страницы. Остальная часть веб-страницы остаётся неизменной, и обновляется только часть с температурой. Этот метод быстрее, снижает нагрузку на сервер и обеспечивает более плавный пользовательский опыт.
Вот AJAX-скрипт, который мы будем использовать.
setInterval(loadDoc, 1000);
function loadDoc() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("temperature-container").textContent = this.responseText;
}
};
xhttp.open("GET", "/", true);
xhttp.send();
}
В коде Arduino этот скрипт встраивается как строка:
str += "<script>";
str += "setInterval(loadDoc, 1000);";
str += "function loadDoc() {";
str += "var xhttp = new XMLHttpRequest();";
str += "xhttp.onreadystatechange = function() {";
str += "if (this.readyState == 4 && this.status == 200) {";
str += "document.getElementById('temperature-container').textContent = this.responseText;";
str += "}";
str += "};";
str += "xhttp.open('GET', '/', true);";
str += "xhttp.send();";
str += "}";
str += "</script>";
Разместите этот скрипт непосредственно перед закрывающим тегом </head>.
Этот AJAX-код автоматически отправляет запрос на ваш сервер ESP32 каждую секунду, запрашивая текущие данные о температуре. Когда сервер отвечает информацией о температуре, код берёт эти данные и вставляет их в конкретный <div>-контейнер на вашей веб-странице с ID «temperature-container». Это происходит непрерывно в фоновом режиме без обновления всей страницы, поэтому отображение температуры остаётся актуальным, а остальная часть веб-страницы не меняется.
Пример 4 — Управление физическими устройствами через веб-сервер
Давайте перейдём к самому захватывающему примеру! На этот раз мы не просто отображаем информацию — мы будем использовать ESP32 для управления реальными физическими устройствами прямо с веб-страницы.
Обзор проекта
Основная цель этого проекта — управление двумя светодиодами через ESP32 по Wi-Fi. Мы начнём с настройки веб-сервера ESP32 в режиме станции (STA), как и в предыдущих примерах. Этот сервер будет обслуживать веб-страницу, которую вы можете открыть на телефоне или компьютере. На этой веб-странице вы увидите, включены или выключены наши светодиоды, и, что более важно, найдёте кнопки для изменения их состояния! Когда вы нажимаете кнопку на этой веб-странице, ESP32 реагирует, включая или выключая светодиоды в зависимости от вашего действия.
Идея управления устройствами через веб-сервер ESP32
Вы можете задаться вопросом: «Подождите — как веб-сервер, который просто обслуживает веб-страницы, может управлять физическими устройствами, такими как светодиоды?»
На самом деле, это очень просто и остроумно. Мы будем управлять физическими устройствами, просто посещая определённый веб-адрес, также известный как URL.
Вот как это работает: когда вы вводите URL в веб-браузере, ваш браузер отправляет сообщение, называемое HTTP-запросом, на веб-сервер. Затем задача веб-сервера — понять, что означает этот запрос, и ответить соответствующим образом.
Например, представьте, что вы вводите http://192.168.1.1/led1on в адресную строку браузера и нажимаете Enter. Ваш браузер немедленно отправляет HTTP-запрос к ESP32. Когда ESP32 получает этот запрос, он достаточно умён, чтобы понять по части /led1on адреса, что вы хотите включить первый светодиод. Поэтому он включает светодиод и отправляет обратно новую веб-страницу, показывающую, что первый светодиод теперь включён. Довольно просто, не так ли? Это как отправка секретного кода в URL, чтобы сообщить ESP32, что делать.
И это работает не только для светодиодов — вы можете использовать ту же идею для управления двигателями, реле, датчиками или даже умными приборами дома, всё через простой веб-интерфейс!
Подключение светодиодов к ESP32
Чтобы начать наш проект, сначала нужно физически подключить два светодиода к ESP32.
Для начала аккуратно установите плату ESP32 на макетную плату. Убедитесь, что каждая сторона платы ESP32 располагается на разных половинах макетной платы.
Затем возьмите два светодиода и подключите их более длинные ножки (аноды) к GPIO 4 и GPIO 5 на ESP32. Затем подключите более короткие ножки (катоды) светодиодов к одному из выводов GND (земля) на ESP32.
Очень важно включить резистор 220 Ом последовательно с каждым светодиодом. Эти резисторы ограничивают ток, протекающий через светодиоды, и предотвращают их перегорание.
На схеме ниже показано, как именно всё подключить:
Пример кода
Скопируйте код ниже в Arduino IDE. Но перед загрузкой скетча на ESP32 вам нужно изменить две вещи в коде — имя Wi-Fi сети (SSID) и пароль. Так ESP32 узнает, к какой сети подключаться.
/*Put your SSID & Password*/
const char* ssid = "YourNetworkName"; // Enter SSID here
const char* password = "YourPassword"; //Enter Password here
После обновления данных Wi-Fi загрузите скетч.
#include <WiFi.h>
#include <WebServer.h>
/*Put your SSID & Password*/
const char* ssid = "YourNetworkName"; // Enter SSID here
const char* password = "YourPassword"; //Enter Password here
WebServer server(80);
uint8_t LED1pin = 5;
bool LED1status = LOW;
uint8_t LED2pin = 4;
bool LED2status = LOW;
void setup() {
Serial.begin(115200);
delay(100);
pinMode(LED1pin, OUTPUT);
pinMode(LED2pin, OUTPUT);
Serial.println("Connecting to ");
Serial.println(ssid);
//connect to your local wi-fi network
WiFi.begin(ssid, password);
//check wi-fi is connected to wi-fi network
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected..!");
Serial.print("Got IP: ");
Serial.println(WiFi.localIP());
server.on("/", handle_OnConnect);
server.on("/led1on", handle_led1on);
server.on("/led1off", handle_led1off);
server.on("/led2on", handle_led2on);
server.on("/led2off", handle_led2off);
server.onNotFound(handle_NotFound);
server.begin();
Serial.println("HTTP server started");
}
void loop() {
server.handleClient();
}
void handle_OnConnect() {
LED1status = LOW;
LED2status = LOW;
digitalWrite(LED1pin, LOW);
digitalWrite(LED2pin, LOW);
Serial.println("LED1 Status: OFF | LED2 Status: OFF");
server.send(200, "text/html", createHTML());
}
void handle_led1on() {
LED1status = HIGH;
digitalWrite(LED1pin, HIGH);
Serial.println("LED1 Status: ON");
server.send(200, "text/html", createHTML());
}
void handle_led1off() {
LED1status = LOW;
digitalWrite(LED1pin, LOW);
Serial.println("LED1 Status: OFF");
server.send(200, "text/html", createHTML());
}
void handle_led2on() {
LED2status = HIGH;
digitalWrite(LED2pin, HIGH);
Serial.println("LED2 Status: ON");
server.send(200, "text/html", createHTML());
}
void handle_led2off() {
LED2status = LOW;
digitalWrite(LED2pin, LOW);
Serial.println("LED2 Status: OFF");
server.send(200, "text/html", createHTML());
}
void handle_NotFound() {
server.send(404, "text/plain", "Not found");
}
String createHTML() {
String str = "<!DOCTYPE html> <html>";
str += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">";
str += "<style>";
str += "body {font-family: Arial, sans-serif; color: #444; text-align: center;}";
str += ".title {font-size: 30px; font-weight: bold; letter-spacing: 2px; margin: 80px 0 55px;}";
str += ".led-control {display: flex; align-items: center; justify-content: center; margin: 40px 0; gap: 30px;}";
str += ".led-label {font-size: 26px;}";
str += ".toggle-switch {width: 120px; height: 60px;}";
str += ".slider {position: absolute; width: 120px; height: 60px; background-color: #f1f1f1; transition: .4s; border-radius: 60px; border: 1px solid #ddd;}";
str += ".slider:before {content: ''; position: absolute; height: 52px; width: 52px; left: 4px; top: 4px; background-color: white; transition: .4s; border-radius: 50%; box-shadow: 0 2px 5px rgba(0, 0, 0, .3);}";
str += ".slider.on {background-color: #4285f4; border: none;}";
str += ".slider.on:before {transform: translateX(60px);}";
str += "a {display: block; height: 100%; width: 100%; text-decoration: none;}";
str += "</style>";
str += "</head>";
str += "<body>";
str += "<h1 class=\"title\">LED CONTROLLER</h1>";
// LED 1 Control
str += "<div class=\"led-control\">";
str += "<span class=\"led-label\">LED 1</span>";
str += "<div class=\"toggle-switch\">";
if (LED1status) {
str += "<a href=\"/led1off\">";
str += "<div class=\"slider on\"></div>";
str += "</a>";
} else {
str += "<a href=\"/led1on\">";
str += "<div class=\"slider\"></div>";
str += "</a>";
}
str += "</div>";
str += "</div>";
// LED 2 Control
str += "<div class=\"led-control\">";
str += "<span class=\"led-label\">LED 2</span>";
str += "<div class=\"toggle-switch\">";
if (LED2status) {
str += "<a href=\"/led2off\">";
str += "<div class=\"slider on\"></div>";
str += "</a>";
} else {
str += "<a href=\"/led2on\">";
str += "<div class=\"slider\"></div>";
str += "</a>";
}
str += "</div>";
str += "</div>";
str += "</body>";
str += "</html>";
return str;
}
Доступ к веб-серверу
После загрузки скетча откройте монитор порта и убедитесь, что скорость установлена на 115200. Затем нажмите кнопку EN (сброс) на ESP32. Если всё работает правильно, ESP32 подключится к Wi-Fi сети и выведет сообщение «WiFi connected..!» вместе с IP-адресом, полученным от роутера. Вы также увидите «HTTP server started» в мониторе порта.
Теперь возьмите устройство — телефон или ноутбук, подключённое к той же Wi-Fi сети, что и ESP32. Откройте веб-браузер (Chrome или Firefox) и введите IP-адрес из монитора порта. Вы должны увидеть веб-страницу! На ней будет показан текущий статус обоих светодиодов вместе с кнопками для управления ими.
Теперь, следя за URL, нажмите кнопку LED1. Как только вы нажмёте её, браузер отправит запрос к ESP32, используя специальный адрес: /led1on. ESP32 прочитает это, включит LED1 и отправит обратно новую веб-страницу, показывающую, что LED1 теперь включён. Одновременно в мониторе порта появится сообщение «LED1 Status: ON».
Попробуйте также нажать кнопку LED2 и понаблюдайте за реакцией. Вы увидите, что можете управлять обоими светодиодами прямо из браузера, в режиме реального времени.
Теперь давайте разберём код шаг за шагом, чтобы вы поняли, как всё работает — и позже смогли модифицировать его для своих собственных проектов.
Разбор кода
Этот скетч очень похож на предыдущий в плане настройки ESP32 как веб-сервера в режиме STA. Но в этом примере, вместо простого отображения данных, мы управляем светодиодами через веб-интерфейс.
В начале кода мы определяем две переменные для хранения номеров выводов светодиодов — GPIO 4 и GPIO 5 — и ещё две переменные для отслеживания текущего состояния каждого светодиода (включён или выключен). Эти переменные статуса помогают нам знать, что делает светодиод, и отображать правильную информацию на веб-странице.
uint8_t LED1pin = 5;
bool LED1status = LOW;
uint8_t LED2pin = 4;
bool LED2status = LOW;
В функции setup() мы настраиваем оба вывода светодиодов как OUTPUT.
pinMode(LED1pin, OUTPUT);
pinMode(LED2pin, OUTPUT);
Далее мы указываем веб-серверу, что делать при посещении определённых URL. Мы используем для этого функцию server.on(). Как и раньше, при посещении главной страницы (/) ESP32 запустит функцию handle_OnConnect(). Но теперь мы также настраиваем набор новых правил! Например, при посещении /led1on ESP32 запустит специальную функцию (handle_led1on()) для включения LED1. Аналогично, у нас есть правила для выключения LED1 (/led1off), включения LED2 (/led2on) и выключения LED2 (/led2off). Таким образом, каждый уникальный URL запускает определённое действие с нашими светодиодами. И, конечно, у нас по-прежнему есть правило для server.onNotFound() — показать сообщение «Страница не найдена», если кто-то введёт неправильный адрес.
server.on("/", handle_OnConnect);
server.on("/led1on", handle_led1on);
server.on("/led1off", handle_led1off);
server.on("/led2on", handle_led2on);
server.on("/led2off", handle_led2off);
server.onNotFound(handle_NotFound);
Давайте разберём, что делают эти функции.
Функция handle_OnConnect() выполняется каждый раз при посещении главной страницы веб-сервера (IP-адрес ESP32). Внутри этой функции мы убеждаемся, что оба светодиода выключены по умолчанию. Для этого мы сначала устанавливаем их переменные статуса в LOW, а затем используем digitalWrite() для отправки сигнала «выключено» на их физические выводы. Мы также выводим сообщение в монитор порта. Наконец, мы вызываем server.send() для отправки веб-страницы с текущим состоянием светодиодов.
void handle_OnConnect() {
LED1status = LOW;
LED2status = LOW;
digitalWrite(LED1pin, LOW);
digitalWrite(LED2pin, LOW);
Serial.println("LED1 Status: OFF | LED2 Status: OFF");
server.send(200, "text/html", createHTML());
}
Аналогично, у нас есть отдельные функции для включения и выключения каждого светодиода: handle_led1on(), handle_led1off(), handle_led2on() и handle_led2off(). Каждая из этих функций работает одинаково. Сначала обновляется переменная статуса светодиода. Затем используется digitalWrite() для отправки соответствующего сигнала на физический вывод для включения или выключения. Далее выводится сообщение в монитор порта. И, наконец, отправляется обновлённая веб-страница обратно в браузер через server.send().
void handle_led1on() {
LED1status = HIGH;
digitalWrite(LED1pin, HIGH);
Serial.println("LED1 Status: ON");
server.send(200, "text/html", createHTML());
}
void handle_led1off() {
LED1status = LOW;
digitalWrite(LED1pin, LOW);
Serial.println("LED1 Status: OFF");
server.send(200, "text/html", createHTML());
}
void handle_led2on() {
LED2status = HIGH;
digitalWrite(LED2pin, HIGH);
Serial.println("LED2 Status: ON");
server.send(200, "text/html", createHTML());
}
void handle_led2off() {
LED2status = LOW;
digitalWrite(LED2pin, LOW);
Serial.println("LED2 Status: OFF");
server.send(200, "text/html", createHTML());
}
Теперь поговорим о функции createHTML(). Это очень важная функция, потому что её задача — динамически строить всю веб-страницу на основе текущего состояния светодиодов, чтобы веб-страница всегда отражала реальное состояние каждого светодиода.
Основная структура HTML, генерируемого этой функцией, аналогична предыдущим примерам. Мы начинаем с основных тегов <!DOCTYPE html>, <html>, <head> и <meta>.
String createHTML(){
String str = "<!DOCTYPE html> <html>\n";
str +="<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n";
После этого мы включаем CSS-стили для красивого оформления. Мы устанавливаем стили шрифтов, цвета, размеры заголовков и даже дизайн виртуальных переключателей, включая их размер, форму и анимацию при нажатии.
str += "<style>";
str += "body {font-family: Arial, sans-serif; color: #444; text-align: center;}";
str += ".title {font-size: 30px; font-weight: bold; letter-spacing: 2px; margin: 80px 0 55px;}";
str += ".led-control {display: flex; align-items: center; justify-content: center; margin: 40px 0; gap: 30px;}";
str += ".led-label {font-size: 26px;}";
str += ".toggle-switch {width: 120px; height: 60px;}";
str += ".slider {position: absolute; width: 120px; height: 60px; background-color: #f1f1f1; transition: .4s; border-radius: 60px; border: 1px solid #ddd;}";
str += ".slider:before {content: ''; position: absolute; height: 52px; width: 52px; left: 4px; top: 4px; background-color: white; transition: .4s; border-radius: 50%; box-shadow: 0 2px 5px rgba(0, 0, 0, .3);}";
str += ".slider.on {background-color: #4285f4; border: none;}";
str += ".slider.on:before {transform: translateX(60px);}";
str += "a {display: block; height: 100%; width: 100%; text-decoration: none;}";
str += "</style>";
А вот самая интересная часть. Внутри HTML мы используем операторы if для проверки текущего состояния каждого светодиода. Например, если LED1status равен ON, мы показываем синий ползунок (стилизованный как включённый), который ведёт на /led1off — при нажатии светодиод выключится. Если LED1status равен OFF, мы показываем серый ползунок (стилизованный как выключенный), который ведёт на /led1on — при нажатии светодиод включится. Та же логика используется для LED2status. Таким образом, кнопка на странице всегда соответствует реальному состоянию каждого светодиода, и нажатие на неё переключает состояние.
// LED 1 Control
str += "<div class=\"led-control\">";
str += "<span class=\"led-label\">LED 1</span>";
str += "<div class=\"toggle-switch\">";
if (LED1status) {
str += "<a href=\"/led1off\">";
str += "<div class=\"slider on\"></div>";
str += "</a>";
} else {
str += "<a href=\"/led1on\">";
str += "<div class=\"slider\"></div>";
str += "</a>";
}
str += "</div>";
str += "</div>";
// LED 2 Control
str += "<div class=\"led-control\">";
str += "<span class=\"led-label\">LED 2</span>";
str += "<div class=\"toggle-switch\">";
if (LED2status) {
str += "<a href=\"/led2off\">";
str += "<div class=\"slider on\"></div>";
str += "</a>";
} else {
str += "<a href=\"/led2on\">";
str += "<div class=\"slider\"></div>";
str += "</a>";
}
str += "</div>";
str += "</div>";
Наконец, как и во всех предыдущих примерах, функция createHTML() закрывает все необходимые HTML-теги и возвращает всю веб-страницу в виде строки. Эта строка затем передаётся в функцию server.send(), которая отправляет её в ваш браузер.