Введение
Прежде чем переходить к MQTT (статьи 311+), разберём более простой и наглядный первый шаг работы с сетью на ESP32 — подключение к домашней Wi-Fi сети и создание простого встроенного веб-сервера, к которому можно обратиться через обычный браузер для проверки состояния устройства или отправки простых команд, без необходимости в Qt-приложении вовсе на этом начальном этапе.
Концепция
WiFi.h предоставляет простой интерфейс подключения к существующей Wi-Fi сети (режим station, как обычное устройство-клиент) — после успешного подключения ESP32 получает IP-адрес в локальной сети, через который к нему можно обратиться. Встроенная библиотека WebServer (для ESP32) позволяет легко создать минимальный HTTP-сервер, обрабатывающий запросы на конкретные пути (/, /status, /led/on) — что даёт простой, не требующий специального клиента способ взаимодействия с устройством для отладки и простых сценариев.
Пример кода: Подключение к Wi-Fi и веб-сервер на ESP32
// firmware/src/main.cpp — подключение к Wi-Fi и простой веб-сервер с управлением светодиодом
#include <WiFi.h>
#include <WebServer.h>
const char *WIFI_SSID = "MyHomeNetwork";
const char *WIFI_PASSWORD = "secure_password_here";
WebServer server(80);
const int LED_PIN = 2;
bool ledState = false;
void setup()
{
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
Serial.print("Подключение к Wi-Fi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.print("Подключено! IP-адрес: ");
Serial.println(WiFi.localIP());
server.on("/", handleRoot);
server.on("/led/on", handleLedOn);
server.on("/led/off", handleLedOff);
server.on("/status", handleStatus);
server.begin();
}
void loop()
{
server.handleClient(); // обязательный вызов — без него сервер не обрабатывает входящие запросы
}
void handleRoot()
{
server.send(200, "text/html",
"<h1>ESP32 Control Panel</h1>"
"<a href='/led/on'>Включить LED</a><br>"
"<a href='/led/off'>Выключить LED</a>");
}
void handleLedOn()
{
digitalWrite(LED_PIN, HIGH);
ledState = true;
server.send(200, "text/plain", "LED включён");
}
void handleLedOff()
{
digitalWrite(LED_PIN, LOW);
ledState = false;
server.send(200, "text/plain", "LED выключен");
}
void handleStatus()
{
// Простой JSON-ответ для программного обращения, в дополнение к человекочитаемому HTML
String json = "{\"led\":" + String(ledState ? "true" : "false") + "}";
server.send(200, "application/json", json);
}
Пояснения к коду
WiFi.begin(WIFI_SSID, WIFI_PASSWORD) инициирует подключение асинхронно, и цикл while (WiFi.status() != WL_CONNECTED) с короткими паузами явно ожидает завершения этого процесса перед продолжением — на практике подключение занимает от долей секунды до нескольких секунд в зависимости от условий сети. После подключения WiFi.localIP() сообщает полученный IP-адрес — именно по этому адресу затем нужно обращаться браузером или Qt-приложением, и поскольку адрес обычно выдаётся динамически роутером (DHCP), он может изменяться между перезагрузками устройства, если не настроена статическая привязка на самом роутере.
server.on(path, handler) регистрирует обработчик для конкретного URL-пути — структурно это похоже на MessageDispatcher из статьи 288 (паттерн диспетчеризации по ключу), но применённый к HTTP-путям вместо префиксов текстового протокола последовательного порта. handleStatus() показывает, что один и тот же сервер может одновременно обслуживать как простые HTML-страницы для просмотра в браузере человеком, так и JSON-эндпоинты, предназначенные для программного обращения (например, будущим Qt-приложением через QNetworkAccessManager, аналогично основному циклу статей про сеть).
Подводные камни
- Хранение пароля Wi-Fi непосредственно в виде открытой строки в исходном коде прошивки — для учебных и домашних проектов это обычно приемлемо, но для прошивки, которая может попасть в публичный репозиторий или быть скопирована неавторизованными людьми, пароль сети оказывается раскрыт; для более чувствительных сценариев credentials стоит выносить в отдельный, не публикуемый конфигурационный файл или использовать механизм настройки Wi-Fi через временный режим точки доступа самого ESP32 (WiFiManager-подобные библиотеки) вместо жёсткого кодирования.
- Забытый вызов
server.handleClient()внутриloop()— без этого вызова веб-сервер, формально запущенный черезserver.begin(), не будет реально обрабатывать входящие HTTP-запросы; это частая первая ошибка, аналогичная забытомуIrReceiver.resume()из статьи 302 — компонент инициализирован, но не вызывается регулярно для реальной обработки событий. - Блокирующий цикл ожидания подключения к Wi-Fi (
while (WiFi.status() != WL_CONNECTED)) без тайм-аута — если сеть с указанным именем недоступна (неверный пароль, сеть выключена, устройство физически вне зоны покрытия), устройство зависнет в этом цикле бесконечно, не переходя к остальной части прошивки вовсе; для надёжных, автономных устройств стоит добавить тайм-аут с последующей логикой повторных попыток или альтернативным режимом работы (например, локальной точкой доступа для первоначальной настройки). - Динамически выдаваемый IP-адрес устройства, меняющийся между перезагрузками, затрудняет последующее автоматическое обнаружение устройства Qt-приложением, если адрес жёстко закодирован где-либо на стороне приложения — для надёжного обнаружения устройств в локальной сети без необходимости вручную каждый раз искать новый IP-адрес обычно используют отдельные протоколы обнаружения (mDNS/Bonjour через библиотеку
ESPmDNS) или, что особенно естественно для следующих статей цикла, центральный MQTT-брокер с известным, фиксированным адресом, к которому подключаются все устройства, вместо необходимости знать IP-адрес каждого отдельного устройства напрямую.