ESP32 асинхронный веб-сервер – управление выходами с Arduino IDE (библиотека ESPAsyncWebServer)
В этом руководстве вы узнаете, как создать асинхронный веб-сервер на плате ESP32 для управления её выходами. Плата будет запрограммирована с помощью Arduino IDE, и мы будем использовать библиотеку ESPAsyncWebServer.
Вам также может понравиться: ESP8266 NodeMCU Async Web Server – управление выходами с Arduino IDE (библиотека ESPAsyncWebServer)
Асинхронный веб-сервер
Для создания веб-сервера мы будем использовать библиотеку ESPAsyncWebServer, которая предоставляет простой способ создания асинхронного веб-сервера. Создание асинхронного веб-сервера имеет ряд преимуществ, упомянутых на странице библиотеки в GitHub, таких как:
«Обработка более одного соединения одновременно»;
«Когда вы отправляете ответ, вы сразу же готовы обрабатывать другие соединения, пока сервер заботится об отправке ответа в фоновом режиме»;
«Простой движок обработки шаблонов для работы с шаблонами»;
И многое другое.
Ознакомьтесь с документацией библиотеки на её странице в GitHub.
Необходимые компоненты
В этом руководстве мы будем управлять тремя выходами. В качестве примера мы будем управлять светодиодами. Итак, вам потребуются следующие компоненты:
ESP32 (читайте Лучшие платы разработки ESP32)
3x светодиода
Вы можете воспользоваться приведёнными ссылками или перейти непосредственно на MakerAdvisor.com/tools, чтобы найти все компоненты для ваших проектов по лучшей цене!
Схема подключения
Перед тем как перейти к коду, подключите 3 светодиода к ESP32. Мы подключаем светодиоды к GPIO 2, 4 и 33, но вы можете использовать любые другие GPIO (читайте Справочник по GPIO ESP32).
Установка библиотек – ESP Async Web Server
Для создания веб-сервера мы будем использовать следующие библиотеки:
ESPAsyncWebServer от ESP32Async
AsyncTCP от ESP32Async
Вы можете установить эти библиотеки через менеджер библиотек Arduino. Откройте менеджер библиотек, нажав на значок библиотеки на левой боковой панели.
Найдите ESPAsyncWebServer и установите ESPAsyncWebServer от ESP32Async.
Затем установите библиотеку AsyncTCP. Найдите AsyncTCP и установите AsyncTCP от ESP32Async.
Обзор проекта
Чтобы лучше понять код, давайте посмотрим, как работает веб-сервер.
Веб-сервер содержит один заголовок «ESP Web Server» и три кнопки (переключатели) для управления тремя выходами. Каждая кнопка-ползунок имеет подпись, указывающую на выходной пин GPIO. Вы можете легко удалить или добавить больше выходов.
Когда ползунок красный, это означает, что выход включён (его состояние HIGH). Если вы переключите ползунок, выход выключится (состояние изменится на LOW).
Когда ползунок серый, это означает, что выход выключен (его состояние LOW). Если вы переключите ползунок, выход включится (состояние изменится на HIGH).
Как это работает?
Давайте посмотрим, что происходит при переключении кнопок. Мы рассмотрим пример для GPIO 2. Для остальных кнопок всё работает аналогично.
1. В первом сценарии вы переключаете кнопку, чтобы включить GPIO 2. Когда это происходит, браузер выполняет HTTP GET запрос по URL /update?output= 2 &state= 1. На основе этого URL ESP изменяет состояние GPIO 2 на 1 ( HIGH) и включает светодиод.
2. Во втором примере вы переключаете кнопку, чтобы выключить GPIO 2. Когда это происходит, браузер выполняет HTTP GET запрос по URL /update?output= 2 &state= 0. На основе этого URL мы изменяем состояние GPIO 2 на 0 ( LOW) и выключаем светодиод.
Код для ESP Async Web Server
Скопируйте следующий код в вашу Arduino IDE.
/*********
Rui Santos & Sara Santos - Random Nerd Tutorials
Complete project details at https://RandomNerdTutorials.com/esp32-async-web-server-espasyncwebserver-library/
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*********/
// Import required libraries
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
const char* PARAM_INPUT_1 = "output";
const char* PARAM_INPUT_2 = "state";
// Create AsyncWebServer object on port 80
AsyncWebServer server(80);
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
<title>ESP Web Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
<style>
html {font-family: Arial; display: inline-block; text-align: center;}
h2 {font-size: 3.0rem;}
p {font-size: 3.0rem;}
body {max-width: 600px; margin:0px auto; padding-bottom: 25px;}
.switch {position: relative; display: inline-block; width: 120px; height: 68px}
.switch input {display: none}
.slider {position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; border-radius: 6px}
.slider:before {position: absolute; content: ""; height: 52px; width: 52px; left: 8px; bottom: 8px; background-color: #fff; -webkit-transition: .4s; transition: .4s; border-radius: 3px}
input:checked+.slider {background-color: #b30000}
input:checked+.slider:before {-webkit-transform: translateX(52px); -ms-transform: translateX(52px); transform: translateX(52px)}
</style>
</head>
<body>
<h2>ESP Web Server</h2>
%BUTTONPLACEHOLDER%
<script>function toggleCheckbox(element) {
var xhr = new XMLHttpRequest();
if(element.checked){ xhr.open("GET", "/update?output="+element.id+"&state=1", true); }
else { xhr.open("GET", "/update?output="+element.id+"&state=0", true); }
xhr.send();
}
</script>
</body>
</html>
)rawliteral";
// Replaces placeholder with button section in your web page
String processor(const String& var){
//Serial.println(var);
if(var == "BUTTONPLACEHOLDER"){
String buttons = "";
buttons += "<h4>Output - GPIO 2</h4><label class=\"switch\"><input type=\"checkbox\" onchange=\"toggleCheckbox(this)\" id=\"2\" " + outputState(2) + "><span class=\"slider\"></span></label>";
buttons += "<h4>Output - GPIO 4</h4><label class=\"switch\"><input type=\"checkbox\" onchange=\"toggleCheckbox(this)\" id=\"4\" " + outputState(4) + "><span class=\"slider\"></span></label>";
buttons += "<h4>Output - GPIO 33</h4><label class=\"switch\"><input type=\"checkbox\" onchange=\"toggleCheckbox(this)\" id=\"33\" " + outputState(33) + "><span class=\"slider\"></span></label>";
return buttons;
}
return String();
}
String outputState(int output){
if(digitalRead(output)){
return "checked";
}
else {
return "";
}
}
void setup(){
// Serial port for debugging purposes
Serial.begin(115200);
pinMode(2, OUTPUT);
digitalWrite(2, LOW);
pinMode(4, OUTPUT);
digitalWrite(4, LOW);
pinMode(33, OUTPUT);
digitalWrite(33, LOW);
// Connect to Wi-Fi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi..");
}
// Print ESP Local IP Address
Serial.println(WiFi.localIP());
// Route for root / web page
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, "text/html", index_html, processor);
});
// Send a GET request to <ESP_IP>/update?output=<inputMessage1>&state=<inputMessage2>
server.on("/update", HTTP_GET, [] (AsyncWebServerRequest *request) {
String inputMessage1;
String inputMessage2;
// GET input1 value on <ESP_IP>/update?output=<inputMessage1>&state=<inputMessage2>
if (request->hasParam(PARAM_INPUT_1) && request->hasParam(PARAM_INPUT_2)) {
inputMessage1 = request->getParam(PARAM_INPUT_1)->value();
inputMessage2 = request->getParam(PARAM_INPUT_2)->value();
digitalWrite(inputMessage1.toInt(), inputMessage2.toInt());
}
else {
inputMessage1 = "No message sent";
inputMessage2 = "No message sent";
}
Serial.print("GPIO: ");
Serial.print(inputMessage1);
Serial.print(" - Set to: ");
Serial.println(inputMessage2);
request->send(200, "text/plain", "OK");
});
// Start server
server.begin();
}
void loop() {
}
Как работает код
В этом разделе мы объясним, как работает код. Продолжайте читать, если хотите узнать больше, или перейдите к разделу «Демонстрация», чтобы увидеть конечный результат.
Импорт библиотек
Сначала импортируйте необходимые библиотеки. Вам нужно подключить библиотеки WiFi, ESPAsyncWebserver и AsyncTCP.
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
Настройка сетевых учётных данных
Введите ваши сетевые учётные данные в следующие переменные, чтобы ESP32 мог подключиться к вашей локальной сети.
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
Входные параметры
Для проверки параметров, передаваемых в URL (номер GPIO и его состояние), мы создаём две переменные: одну для выхода и другую для состояния.
const char* PARAM_INPUT_1 = "output";
const char* PARAM_INPUT_2 = "state";
Помните, что ESP32 получает запросы такого вида: /update? output =2& state =0
Объект AsyncWebServer
Создайте объект AsyncWebServer на порту 80.
AsyncWebServer server(80);
Создание веб-страницы
Весь HTML-текст со стилями и JavaScript хранится в переменной index_html. Теперь мы рассмотрим HTML-текст и увидим, что делает каждая часть.
Заголовок находится внутри тегов <title> и </title>. Заголовок – это именно то, что звучит: заголовок вашего документа, который отображается в строке заголовка вашего веб-браузера. В данном случае это «ESP Web Server».
<title>ESP Web Server</title>
Следующий тег <meta> делает вашу веб-страницу адаптивной в любом браузере (ноутбук, планшет или смартфон).
<meta name="viewport" content="width=device-width, initial-scale=1">
Следующая строка предотвращает запросы фавикона. В данном случае у нас нет фавикона. Фавикон – это значок сайта, который отображается рядом с заголовком во вкладке веб-браузера. Если мы не добавим следующую строку, ESP32 будет получать запрос на фавикон каждый раз, когда мы обращаемся к веб-серверу.
<link rel="icon" href="data:,">
Между тегами <style></style> мы добавляем CSS для стилизации веб-страницы. Мы не будем подробно описывать, как работает эта CSS-стилизация.
<style>
html {font-family: Arial; display: inline-block; text-align: center;}
h2 {font-size: 3.0rem;}
p {font-size: 3.0rem;}
body {max-width: 600px; margin:0px auto; padding-bottom: 25px;}
.switch {position: relative; display: inline-block; width: 120px; height: 68px}
.switch input {display: none}
.slider {position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; border-radius: 6px}
.slider:before {position: absolute; content: ""; height: 52px; width: 52px; left: 8px; bottom: 8px; background-color: #fff; -webkit-transition: .4s; transition: .4s; border-radius: 3px}
input:checked+.slider {background-color: #b30000}
input:checked+.slider:before {-webkit-transform: translateX(52px); -ms-transform: translateX(52px); transform: translateX(52px)}
</style>
Тело HTML
Внутри тегов <body></body> мы добавляем содержимое веб-страницы.
Теги <h2></h2> добавляют заголовок на веб-страницу. В данном случае это текст «ESP Web Server», но вы можете добавить любой другой текст.
<h2>ESP Web Server</h2>
После заголовка у нас расположены кнопки. Способ отображения кнопок на веб-странице (красный: если GPIO включён; или серый: если GPIO выключен) зависит от текущего состояния GPIO.
Когда вы обращаетесь к веб-серверу, вы хотите, чтобы он отображал правильные текущие состояния GPIO. Поэтому вместо добавления HTML-текста для создания кнопок мы добавим заполнитель %BUTTONPLACEHOLDER%. Этот заполнитель затем будет заменён фактическим HTML-текстом для создания кнопок с правильными состояниями при загрузке веб-страницы.
%BUTTONPLACEHOLDER%
JavaScript
Далее идёт JavaScript-код, который отвечает за выполнение HTTP GET запроса при переключении кнопок, как мы объясняли ранее.
<script>function toggleCheckbox(element) {
var xhr = new XMLHttpRequest();
if(element.checked){ xhr.open("GET", "/update?output="+element.id+"&state=1", true); }
else { xhr.open("GET", "/update?output="+element.id+"&state=0", true); }
xhr.send();
}
</script>
Вот строка, которая выполняет запрос:
if(element.checked){ xhr.open("GET", "/update?output="+element.id+"&state=1", true); }
element.id возвращает id HTML-элемента. id каждой кнопки будет соответствовать управляемому GPIO, как мы увидим в следующем разделе:
Кнопка GPIO 2 – element.id = 2
Кнопка GPIO 4 – element.id = 4
Кнопка GPIO 33 – element.id = 33
Процессор (Processor)
Теперь нам нужно создать функцию processor(), которая заменяет заполнители в HTML-тексте на то, что мы определим.
Когда запрашивается веб-страница, проверяется, содержит ли HTML какие-либо заполнители. Если найден заполнитель %BUTTONPLACEHOLDER%, возвращается HTML-текст для создания кнопок.
String processor(const String& var){
//Serial.println(var);
if(var == "BUTTONPLACEHOLDER"){
String buttons = "";
buttons += "<h4>Output - GPIO 2</h4><label class=\"switch\"><input type=\"checkbox\" onchange=\"toggleCheckbox(this)\" id=\"2\" " + outputState(2) + "><span class=\"slider\"></span></label>";
buttons += "<h4>Output - GPIO 4</h4><label class=\"switch\"><input type=\"checkbox\" onchange=\"toggleCheckbox(this)\" id=\"4\" " + outputState(4) + "><span class=\"slider\"></span></label>";
buttons += "<h4>Output - GPIO 33</h4><label class=\"switch\"><input type=\"checkbox\" onchange=\"toggleCheckbox(this)\" id=\"33\" " + outputState(33) + "><span class=\"slider\"></span></label>";
return buttons;
}
return String();
}
Вы можете легко удалить или добавить больше строк для создания дополнительных кнопок.
Давайте рассмотрим, как создаются кнопки. Мы создаём строковую переменную buttons, которая содержит HTML-текст для создания кнопок. Мы объединяем HTML-текст с текущим состоянием выхода, чтобы кнопка-переключатель была серой или красной. Текущее состояние выхода возвращается функцией outputState(<GPIO>) (она принимает в качестве аргумента номер GPIO). Смотрите ниже:
buttons += "<h4>Output - GPIO 2</h4><label class=\"switch\"><input type=\"checkbox\" onchange=\"toggleCheckbox(this)\" id=\"2\" " + outputState(2) + "><span class=\"slider\"></span></label>";
Символ \ используется для того, чтобы передать «» внутри строки (String).
Функция outputState() возвращает либо «checked», если GPIO включён, либо пустое поле «», если GPIO выключен.
String outputState(int output){
if(digitalRead(output)){
return "checked";
}
else {
return "";
}
}
Таким образом, HTML-текст для GPIO 2, когда он включён, будет выглядеть так:
<h4>Output - GPIO 2</h4>
<label class="switch">
<input type="checkbox" onchange="toggleCheckbox(this)" id="2" checked><span class="slider"></span>
</label>
Давайте разберём это на более мелкие части, чтобы понять, как это работает.
В HTML переключатель (toggle switch) – это тип ввода (input). Тег <input> определяет поле ввода, в которое пользователь может вводить данные. Переключатель – это поле ввода типа checkbox. Существует множество других типов полей ввода.
<input type="checkbox">
Чекбокс может быть отмечен или нет. Когда он отмечен, код выглядит следующим образом:
<input type="checkbox" checked>
Атрибут onchange – это атрибут события, который срабатывает при изменении значения элемента (чекбокса). Каждый раз, когда вы отмечаете или снимаете отметку с переключателя, вызывается JavaScript-функция toggleCheckbox() для этого конкретного элемента (this).
Атрибут id задаёт уникальный идентификатор для HTML-элемента. id позволяет нам манипулировать элементом с помощью JavaScript или CSS.
<input type="checkbox" onchange="toggleCheckbox(this)" id="2" checked>
setup()
В функции setup() инициализируйте монитор последовательного порта для целей отладки.
Serial.begin(115200);
Установите GPIO, которыми вы хотите управлять, как выходы с помощью функции pinMode() и установите их в LOW при первом запуске ESP32. Если вы добавили больше GPIO, выполните ту же процедуру.
pinMode(2, OUTPUT);
digitalWrite(2, LOW);
pinMode(4, OUTPUT);
digitalWrite(4, LOW);
pinMode(33, OUTPUT);
digitalWrite(33, LOW);
Подключитесь к вашей локальной сети и выведите IP-адрес ESP32.
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi..");
}
// Print ESP Local IP Address
Serial.println(WiFi.localIP());
В функции setup() вам нужно обработать, что происходит, когда ESP32 получает запросы. Как мы видели ранее, вы получаете запрос такого вида:
<ESP_IP>/update?output=<inputMessage1>&state=<inputMessage2>
Итак, мы проверяем, содержит ли запрос значение переменной PARAM_INPUT1 (output) и PARAM_INPUT2 (state), и сохраняем соответствующие значения в переменных input1Message и input2Message.
if (request->hasParam(PARAM_INPUT_1) && request->hasParam(PARAM_INPUT_2)) {
inputMessage1 = request->getParam(PARAM_INPUT_1)->value();
inputMessage2 = request->getParam(PARAM_INPUT_2)->value();
Затем мы управляем соответствующим GPIO с соответствующим состоянием (переменная inputMessage1 сохраняет номер GPIO, а inputMessage2 сохраняет состояние – 0 или 1)
digitalWrite(inputMessage1.toInt(), inputMessage2.toInt());
Вот полный код для обработки HTTP GET запроса /update:
server.on("/update", HTTP_GET, [] (AsyncWebServerRequest *request) {
String inputMessage1;
String inputMessage2;
// GET input1 value on <ESP_IP>/update?output=<inputMessage1>&state=<inputMessage2>
if (request->hasParam(PARAM_INPUT_1) && request->hasParam(PARAM_INPUT_2)) {
inputMessage1 = request->getParam(PARAM_INPUT_1)->value();
inputMessage2 = request->getParam(PARAM_INPUT_2)->value();
digitalWrite(inputMessage1.toInt(), inputMessage2.toInt());
}
else {
inputMessage1 = "No message sent";
inputMessage2 = "No message sent";
}
Serial.print("GPIO: ");
Serial.print(inputMessage1);
Serial.print(" - Set to: ");
Serial.println(inputMessage2);
request->send(200, "text/plain", "OK");
});
Наконец, запустите сервер:
server.begin();
Демонстрация
После загрузки кода на ESP32 откройте монитор последовательного порта с частотой 115200 бод. Нажмите встроенную кнопку RST/EN. Вы должны получить IP-адрес.
Откройте браузер и введите IP-адрес ESP. Вы получите доступ к подобной веб-странице.
Нажимайте кнопки-переключатели для управления GPIO ESP32. Одновременно вы должны видеть следующие сообщения в мониторе последовательного порта, которые помогут вам отладить код.
Вы также можете получить доступ к веб-серверу из браузера на вашем смартфоне. Каждый раз, когда вы открываете веб-сервер, он отображает текущие состояния GPIO. Красный цвет означает, что GPIO включён, а серый – что GPIO выключен.
Заключение
В этом руководстве вы научились создавать асинхронный веб-сервер на ESP32 для управления его выходами с помощью переключателей. Каждый раз, когда вы открываете веб-страницу, она отображает обновлённые состояния GPIO.
У нас есть другие примеры веб-серверов с использованием библиотеки ESPAsyncWebServer, которые могут вам понравиться:
Мы надеемся, что это руководство было полезным. Если у вас есть вопросы, оставьте комментарий ниже, и мы постараемся ответить вам.
Если вам нравится ESP32, вы можете рассмотреть возможность записи на наш курс «Learn ESP32 with Arduino IDE». Вы также можете получить доступ к нашим бесплатным ресурсам по ESP32 здесь.
Спасибо за чтение.
Источник: ESP32 Async Web Server – Control Outputs with Arduino IDE (ESPAsyncWebServer library)