Простой веб-сервер на ESP32. Создание первого веб-сервера на ESP32

Всем привет =)
В этой статье я расскажу как создать свой первый веб-сервер на основе очень популярной платы с микроконтроллером ESP32.
Сделаем веб-сервер для управления светодиодами — внешними и встроенным на плате.
Погнали =)
ВНИМАНИЕ!!!! Первым делом, если вы только начинаете работу с ESP32, необходимо установить ядро для плат ESP32 в Arduino IDE!!! Ознакомиться с инструкцией по установке ядра можно по этой ссылке! Ссылка для установки ядра доступна там же. Если вы знаете, как установить ядро, то вот ссылка: https://github.com/espressif/arduino-esp32
Сначала разберемся, что такое веб-сервер.
Веб-сервер — это устройство, которое подключено к WiFi сети или даже к Интернету, имеющее свой IP-адрес или даже доменное имя (например, ya.ru). Веб-сервер получает запросы на свой IP-адрес со сведениями об необходимых данных и посылает в ответ необходимые данные. Сейчас чаще всего веб-сервера используются для хранения сайтов и баз данных, а обычные пользователи посылают запросы на сервер, просто переходя по ссылке или используя приложение для телефона с какой-либо информацией (например, бот в Telegram).

Мы сделаем простой локальный веб-сервер, который будет доступен только в нашей домашней сети. В роли сервера будет выступать наша любимая плата ESP32. при включении она сообщит свой IP-адрес, мы перейдём по нему как по ссылке и попадём на веб-страницу, загруженную с платы. Это очень интересно и круто!
Ниже приведённый код вы можете вставить в проект Arduino IDE, залить в свою плату ESP32 и наслаждаться результатом!
Необходимо подключить на пины 12 и 14 два светодиода через резисторы (подходящее сопротивление от 300 до 500 Ом, если будет меньше, светодиоды сгорят, если больше, будут тускло светиться), как показано на схеме. Наш веб-сервер будет управлять двумя светодиодами по нажатию кнопок на локальной веб-странице сервера.
#include <WiFi.h> // Подключаем библиотеку Wi-Fi
// Введите данные вашей СУЩЕСТВУЮЩЕЙ WiFi-сети в кавычках
// ВАЖНО!! Ваш компьютер/телефон должен быть подключен к этой же сети!
const char* ssid = "";
const char* password = "";
// Установка номера порта сервера на 80
WiFiServer server(80);
// Переменная для хранения HTTP запроса
String header;
// Вспомогательные переменные для хранения текущего состояния вывода
String output12State = "off";
String output14State = "off";
// Назначаем выходные переменные контактам GPIO
const int output12 = 12;
const int output14 = 14;
// Текущее время
unsigned long currentTime = millis();
// Предыдущее время
unsigned long previousTime = 0;
// Указываем время задержки в миллисекундах (пример: 2000 мс = 2 сек)
const long timeoutTime = 2000;
void setup() {
Serial.begin(115200);
// Устанавливаем пины 12 и 14 на ВЫХОД сигнала
pinMode(output12, OUTPUT);
pinMode(output14, OUTPUT);
// Устанавливаем контакты в состояние LOW, то есть ВЫКЛ
digitalWrite(output12, LOW);
digitalWrite(output14, LOW);
// Соединяемся с Wi-Fi сетью с нашим SSID и паролем
Serial.print("Подключаюсь к сети '");
Serial.print(ssid);
Serial.print("', пожалуйста, дождитесь появления IP-адреса ");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
// Выводим IP адрес платы в SerialMonitor и запускаем веб-сервер
Serial.println("");
Serial.println("Подключение установлено.");
Serial.println("IP-адрес этого сервера: ");
Serial.println(WiFi.localIP());
Serial.println("Перейдите по этому адресу в браузере для продолжения взаимодействия с сервером ESP32!");
server.begin();
}
void loop(){
WiFiClient client = server.available(); // Отслеживаем входящих клиентов
if (client) { // Если подключается новый клиент...
currentTime = millis();
previousTime = currentTime;
Serial.println("Новый клиент!"); // ...выводим сообщение в SerialMonitor...
String currentLine = ""; // ...создаём строковую переменную для данных от клиента...
while (client.connected() && currentTime - previousTime <= timeoutTime) { // Цикл, пока клиент подключён...
currentTime = millis();
if (client.available()) { // ...если поступили данные от клиента...
char c = client.read(); // ...читаем данные...
Serial.write(c); // ...выводим в SerialMonitor...
header += c;
if (c == '\n') { // Если поступил символ \n...
// Если текущая строка пуста, создаём ещё одну строку.
// Конец запроса от клиента, посылаем ответ...
if (currentLine.length() == 0) {
// HTTP заголовки всегда начинаются с кода ответа (например, HTTP/1.1 200 OK)
// ...и тип контента:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println("Connection: close");
client.println();
// переключение пинов - ВКЛ и ВЫКЛ
if (header.indexOf("GET /12/on") >= 0) {
Serial.println("Светодиод на пине 12 включен");
output12State = "on";
digitalWrite(output12, HIGH);
} else if (header.indexOf("GET /12/off") >= 0) {
Serial.println("Светодиод на пине 12 выключен");
output12State = "off";
digitalWrite(output12, LOW);
} else if (header.indexOf("GET /14/on") >= 0) {
Serial.println("Светодиод на пине 14 включен");
output14State = "on";
digitalWrite(output14, HIGH);
} else if (header.indexOf("GET /14/off") >= 0) {
Serial.println("Светодиод на пине 14 выключен");
output14State = "off";
digitalWrite(output14, LOW);
}
// Отображение HTML веб-страницы
client.println("<!DOCTYPE html><html>");
client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
client.println("<link rel=\"icon\" href=\"data:,\">");
// CSS для стилизации ВКЛ/ВЫКЛ кнопок
// Можете изменить фоновый цвет и шрифт
client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
client.println(".button { background-color: #4CAF50; border: none; color: white; padding: 16px 40px;");
client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");
client.println(".button2 {background-color: #555555;}</style></head>");
// Заголовок веб-страницы
client.println("<body><h1>ESP32 Web Server</h1>");
// Отображение текущего состояния пина 12
client.println("<p>GPIO 12 - State " + output12State + "</p>");
// Если пин 12 ВЫКЛ, тогда выводим кнопку включения...
if (output12State=="off") {
client.println("<p><a href=\"/12/on\"><button class=\"button\">ON</button></a></p>");
} else {
client.println("<p><a href=\"/12/off\"><button class=\"button button2\">OFF</button></a></p>");
}
// Отображение текущего состояния пина 14
client.println("<p>GPIO 14 - State " + output14State + "</p>");
// Если пин 14 ВЫКЛ, тогда выводим кнопку включения...
if (output14State=="off") {
client.println("<p><a href=\"/14/on\"><button class=\"button\">ON</button></a></p>");
} else {
client.println("<p><a href=\"/14/off\"><button class=\"button button2\">OFF</button></a></p>");
}
client.println("</body></html>");
// Ответ HTTP заканчивается еще одной пустой строкой
client.println();
// Выходим из цикла while
break;
} else { // Если появилась новая строка, очистим текущую строку
currentLine = "";
}
} else if (c != '\r') { // Если у вас еще есть что-то кроме символа возврата курсора,
currentLine += c; // Добавляем его в конец текущей строки
}
}
}
// Очищаем переменную заголовка
header = "";
// Завершаем соединение
client.stop();
Serial.println("Client disconnected.");
Serial.println("");
}
}
Если вам не хочется подключать что-то к плате, или просто нет светодиодов или резисторов, можно сделать схему управления встроенным светодиодом. В этом случае ничего подключать к плате не нужно (кроме USB-кабеля для прошивки и питания, разумеется). Код представлен ниже.
#include <WiFi.h> // Подключаем библиотеку Wi-Fi
// Введите данные вашей СУЩЕСТВУЮЩЕЙ WiFi-сети в кавычках
// ВАЖНО!! Ваш компьютер/телефон должен быть подключен к этой же сети!
const char* ssid = "";
const char* password = "";
// Установка номера порта сервера на 80
WiFiServer server(80);
// Переменная для хранения HTTP запроса
String header;
// Вспомогательные переменные для хранения текущего состояния вывода
String ledState = "off";
// Назначаем выходную переменную контакту GPIO
const int c = LED_BUILTIN;
// Текущее время
unsigned long currentTime = millis();
// Предыдущее время
unsigned long previousTime = 0;
// Указываем время задержки в миллисекундах (пример: 2000 мс = 2 сек)
const long timeoutTime = 2000;
void setup() {
Serial.begin(115200);
// Устанавливаем пин LED_BUILTIN на ВЫХОД сигнала
pinMode(LED_BUILTIN, OUTPUT);
// Устанавливаем контакт в состояние LOW, то есть ВЫКЛ
digitalWrite(LED_BUILTIN, LOW);
// Соединяемся с Wi-Fi сетью с нашим SSID и паролем
Serial.print("Подключаюсь к сети '");
Serial.print(ssid);
Serial.print("', пожалуйста, дождитесь появления IP-адреса ");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
// Выводим IP адрес платы в SerialMonitor и запускаем веб-сервер
Serial.println("");
Serial.println("Подключение установлено.");
Serial.println("IP-адрес этого сервера: ");
Serial.println(WiFi.localIP());
Serial.println("Перейдите по этому адресу в браузере для продолжения взаимодействия с сервером ESP32!");
server.begin();
}
void loop(){
WiFiClient client = server.available(); // Отслеживаем входящих клиентов
if (client) { // Если подключается новый клиент...
currentTime = millis();
previousTime = currentTime;
Serial.println("Новый клиент!"); // ...выводим сообщение в SerialMonitor...
String currentLine = ""; // ...создаём строковую переменную для данных от клиента...
while (client.connected() && currentTime - previousTime <= timeoutTime) { // Цикл, пока клиент подключён...
currentTime = millis();
if (client.available()) { // ...если поступили данные от клиента...
char c = client.read(); // ...читаем данные...
Serial.write(c); // ...выводим в SerialMonitor...
header += c;
if (c == '\n') { // Если поступил символ \n...
// Если текущая строка пуста, создаём ещё одну строку.
// Конец запроса от клиента, посылаем ответ...
if (currentLine.length() == 0) {
// HTTP заголовки всегда начинаются с кода ответа (например, HTTP/1.1 200 OK)
// ...и тип контента:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println("Connection: close");
client.println();
// переключение пинов - ВКЛ и ВЫКЛ
if (header.indexOf("GET /led/on") >= 0) {
Serial.println("Светодиод включен.");
ledState = "on";
digitalWrite(ledPin, LOW);
} else if (header.indexOf("GET /led/off") >= 0) {
Serial.println("Светодиод выключен");
ledState = "off";
digitalWrite(ledPin, HIGH);
}
// Отображение HTML веб-страницы
client.println("<!DOCTYPE html><html>");
client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
client.println("<link rel=\"icon\" href=\"data:,\">");
// CSS для стилизации ВКЛ/ВЫКЛ кнопок
// Можете изменить фоновый цвет и шрифт
client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
client.println(".button { background-color: #4CAF50; border: none; color: white; padding: 16px 40px;");
client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");
client.println(".button2 {background-color: #555555;}</style></head>");
// Заголовок веб-страницы
client.println("<body><h1>ESP32 Web Server</h1>");
// Отображение текущего состояния встроенного светодиода
client.println("<p>LED - State " + output14State + "</p>");
// Если светодиод ВЫКЛ, тогда выводим кнопку включения...
if (outputLedState=="off") {
client.println("<p><a href=\"/led/on\"><button class=\"button\">ON</button></a></p>");
} else {
client.println("<p><a href=\"/led/off\"><button class=\"button button2\">OFF</button></a></p>");
}
client.println("</body></html>");
// Ответ HTTP заканчивается еще одной пустой строкой
client.println();
// Выходим из цикла while
break;
} else { // Если появилась новая строка, очистим текущую строку
currentLine = "";
}
} else if (c != '\r') { // Если у вас еще есть что-то кроме символа возврата курсора,
currentLine += c; // Добавляем его в конец текущей строки
}
}
}
// Очищаем переменную заголовка
header = "";
// Завершаем соединение
client.stop();
Serial.println("Клиент отключён.");
Serial.println("");
}
}
Здесь логика включения светодиода немного другая. Встроенный светодиод уже подключен через резистор к плюсу, и нам нужно подать минус. Поэтому для включения этого светодиода нужно подать не HIGH, a LOW. Для выключения светодиода подадим HIGH.
Спасибо большое за внимание к статье! 🙂