Асинхронный веб-сервер на ESP32 (с выводом IP на LCD)
В этом проекте ESP32 превращается в асинхронный веб-сервер, которым можно управлять с телефона или компьютера в той же сети Wi-Fi. Благодаря библиотеке ESPAsyncWebServer сервер может обрабатывать несколько запросов одновременно (в отличие от классического WebServer.h, который блокируется на одном запросе). Открыв IP-адрес ESP32 в браузере, ты нажимаешь кнопки ON/OFF (маршруты /on и /off) и включаешь и выключаешь светодиод на GPIO 5.

Видео
Схема

Исходный код
1// ─────────────────────────────────────────
2// BlueGrays · bluegrays.com
3// Асинхронный веб-сервер на ESP32 (с выводом IP на LCD)
4// ─────────────────────────────────────────
5// ESP32 запускает асинхронный веб-сервер. Открой его IP-адрес с телефона
6// или компьютера в той же сети Wi-Fi и переключай светодиод кнопками
7// ON/OFF. I2C LCD показывает IP-адрес в верхней строке и состояние
8// светодиода в нижней.
9//
10// Пины:
11// GPIO 5 -> Светодиод (с резистором 220 ohm, катод к GND)
12// GPIO 21 -> LCD SDA
13// GPIO 22 -> LCD SCL
14// LCD VCC -> VIN (5V), GND -> GND
15
16#include <Wire.h>
17#include <LiquidCrystal_I2C.h>
18#include <WiFi.h>
19#include <AsyncTCP.h>
20#include <ESPAsyncWebServer.h>
21
22// Данные Wi-Fi (по умолчанию для Wokwi). На реальном железе используй свою сеть
23const char* WIFI_SSID = "Wokwi-GUEST";
24const char* WIFI_PASS = "";
25
26// Определения пинов
27#define LED_PIN 5
28
29bool ledState = false; // Светодиод горит?
30AsyncWebServer server(80); // Веб-сервер на порту 80
31LiquidCrystal_I2C lcd(0x27, 16, 2); // Адрес 0x27, 16x2
32
33// Обновляет LCD: IP и состояние светодиода
34void updateLcd() {
35 lcd.clear();
36 lcd.setCursor(0, 0);
37 // Пропустить "IP:", если IP длиннее 13 символов
38 String ip = WiFi.localIP().toString();
39 if (ip.length() <= 13) lcd.print("IP:");
40 lcd.print(ip);
41 lcd.setCursor(0, 1);
42 lcd.print(ledState ? "LED: ACIK " : "LED: KAPALI ");
43}
44
45// HTML-страница во флеш-памяти (PROGMEM). processor() заполняет заполнитель %STATE%
46const char INDEX_HTML[] PROGMEM = R"rawliteral(
47<!DOCTYPE html>
48<html lang="tr">
49<head>
50<meta charset="utf-8">
51<meta name="viewport" content="width=device-width,initial-scale=1">
52<title>BlueGrays LED Kontrol</title>
53<style>
54body{font-family:Arial;text-align:center;padding:20px;background:#1a1a2e;color:#fff}
55h1{color:#4ea8de}
56.state{font-size:28px;margin:30px}
57button{padding:18px 40px;font-size:18px;margin:8px;border:none;border-radius:8px;cursor:pointer;color:#fff}
58.on{background:#2ecc71}.off{background:#e74c3c}
59</style>
60</head>
61<body>
62<h1>BlueGrays LED Kontrol</h1>
63<div class="state">LED: %STATE%</div>
64<a href="/on"><button class="on">ON</button></a>
65<a href="/off"><button class="off">OFF</button></a>
66</body>
67</html>
68)rawliteral";
69
70// Заменяет %STATE% на странице состоянием светодиода
71String processor(const String& var) {
72 if (var == "STATE") return ledState ? "ACIK" : "KAPALI";
73 return String();
74}
75
76void setup() {
77 pinMode(LED_PIN, OUTPUT);
78 digitalWrite(LED_PIN, LOW);
79
80 // I2C по умолчанию на V1: SDA=21 SCL=22
81 Wire.begin(21, 22);
82 lcd.init();
83 lcd.backlight();
84
85 // Заставка, 3 с
86 lcd.setCursor(3, 0);
87 lcd.print("BlueGrays");
88 lcd.setCursor(2, 1);
89 lcd.print("WebServer v1");
90 delay(3000);
91 lcd.clear();
92
93 // Подключиться к Wi-Fi, ждать подключения
94 lcd.setCursor(0, 0);
95 lcd.print("WiFi baglaniyor");
96 lcd.setCursor(0, 1);
97 lcd.print("Connecting...");
98
99 WiFi.mode(WIFI_STA);
100 WiFi.begin(WIFI_SSID, WIFI_PASS);
101 while (WiFi.status() != WL_CONNECTED) delay(500);
102
103 // Маршруты: "/" страница, "/on" вкл, "/off" выкл
104 server.on("/", HTTP_GET, [](AsyncWebServerRequest *r) {
105 r->send_P(200, "text/html", INDEX_HTML, processor);
106 });
107
108 server.on("/on", HTTP_GET, [](AsyncWebServerRequest *r) {
109 ledState = true;
110 digitalWrite(LED_PIN, HIGH);
111 updateLcd();
112 r->redirect("/");
113 });
114
115 server.on("/off", HTTP_GET, [](AsyncWebServerRequest *r) {
116 ledState = false;
117 digitalWrite(LED_PIN, LOW);
118 updateLcd();
119 r->redirect("/");
120 });
121
122 // Запустить сервер, показать IP на LCD
123 server.begin();
124 updateLcd();
125}
126
127void loop() {
128 // Асинхронный сервер работает в фоне
129 delay(100);
130}
131Частые вопросы
Чем асинхронный веб-сервер лучше классического WebServer?
Классический WebServer обрабатывает запросы по очереди и требует постоянно вызывать `handleClient()` в цикле. ESPAsyncWebServer обрабатывает запросы в фоне; несколько браузеров могут подключаться одновременно, а цикл освобождается для других задач.
Как установить библиотеки?
Понадобятся библиотеки ESPAsyncWebServer и AsyncTCP. Найди «ESPAsyncWebServer» (ESP32Async) в менеджере библиотек Arduino IDE и установи; AsyncTCP установится как зависимость.
Можно ли открыть страницу из интернета снаружи?
По умолчанию доступ ограничен той же локальной сетью. Для доступа снаружи нужен проброс портов, но это риск для безопасности; вместо этого рекомендуются Blynk, MQTT или облачные решения.
Что значит встроить HTML через PROGMEM?
HTML-страница хранится во flash-памяти как текст внутри кода и не занимает RAM. Так веб-интерфейс можно отдавать, не настраивая отдельную файловую систему (LittleFS).
