ESP32 WebSocket сервер: управление выходами (Arduino IDE)
В этом руководстве вы узнаете, как создать веб-сервер на ESP32 с использованием протокола связи WebSocket. В качестве примера мы покажем, как создать веб-страницу для удалённого управления выходами ESP32. Состояние выхода отображается на веб-странице и автоматически обновляется у всех клиентов.
ESP32 будет программироваться с использованием Arduino IDE и ESPAsyncWebServer. У нас также есть аналогичное руководство по WebSocket для ESP8266.
Если вы следили за некоторыми из наших предыдущих проектов веб-серверов, таких как этот, вы, возможно, заметили, что если у вас открыто несколько вкладок (на одном или на разных устройствах) одновременно, состояние не обновляется автоматически во всех вкладках, пока вы не обновите веб-страницу. Чтобы решить эту проблему, мы можем использовать протокол WebSocket – все клиенты могут быть уведомлены при изменении и обновить веб-страницу соответствующим образом.
Это руководство основано на проекте, созданном и задокументированном одним из наших читателей (Stephane Calderoni). Вы можете прочитать его отличное руководство здесь.
Введение в WebSocket
WebSocket – это постоянное соединение между клиентом и сервером, которое обеспечивает двунаправленную связь между обеими сторонами с использованием TCP-соединения. Это означает, что вы можете отправлять данные от клиента к серверу и от сервера к клиенту в любой момент времени.
Клиент устанавливает WebSocket-соединение с сервером через процесс, известный как рукопожатие WebSocket (WebSocket handshake). Рукопожатие начинается с HTTP-запроса/ответа, что позволяет серверам обрабатывать HTTP-соединения и WebSocket-соединения на одном и том же порту. Как только соединение установлено, клиент и сервер могут обмениваться данными WebSocket в режиме полного дуплекса.
С помощью протокола WebSocket сервер (плата ESP32) может отправлять информацию клиенту или всем клиентам без запроса. Это также позволяет нам отправлять информацию в веб-браузер при возникновении изменения.
Это изменение может быть чем-то, что произошло на веб-странице (вы нажали кнопку), или чем-то, что произошло на стороне ESP32, например, нажатие физической кнопки в цепи.
Обзор проекта
Вот веб-страница, которую мы создадим для этого проекта.
Веб-сервер ESP32 отображает веб-страницу с кнопкой для переключения состояния GPIO 2;
Для простоты мы управляем GPIO 2 – встроенным светодиодом. Вы можете использовать этот пример для управления любым другим GPIO;
Интерфейс показывает текущее состояние GPIO. При любом изменении состояния GPIO интерфейс обновляется мгновенно;
Состояние GPIO обновляется автоматически у всех клиентов. Это означает, что если у вас открыто несколько вкладок браузера на одном устройстве или на разных устройствах, все они обновляются одновременно.
Как это работает?
На следующем изображении показано, что происходит при нажатии на кнопку «Toggle».
Вот что происходит, когда вы нажимаете на кнопку «Toggle»:
Нажатие на кнопку «Toggle»;
Клиент (ваш браузер) отправляет данные по протоколу WebSocket с сообщением «toggle»;
ESP32 (сервер) получает это сообщение, поэтому он знает, что нужно переключить состояние светодиода. Если светодиод был выключен, включить его;
Затем он отправляет данные с новым состоянием светодиода всем клиентам также по протоколу WebSocket;
Клиенты получают сообщение и обновляют состояние светодиода на веб-странице соответственно. Это позволяет нам обновлять все клиенты практически мгновенно при изменении.
Подготовка Arduino IDE
Мы будем программировать плату ESP32 с использованием Arduino IDE, поэтому убедитесь, что она установлена в вашей Arduino IDE.
Установка библиотек – Async Web Server
Мы создадим веб-сервер, используя следующие библиотеки:
ESPAsyncWebServer от ESP32Async
AsyncTCP от ESP32Async
Вы можете установить эти библиотеки через менеджер библиотек Arduino. Откройте менеджер библиотек, нажав на значок библиотеки в левой боковой панели.
Найдите ESPAsyncWebServer и установите ESPAsyncWebServer от ESP32Async.
Затем установите библиотеку AsyncTCP. Найдите AsyncTCP и установите AsyncTCP от ESP32Async.
Код для ESP32 WebSocket сервера
Скопируйте следующий код в вашу Arduino IDE.
/*********
Rui Santos & Sara Santos - Random Nerd Tutorials
Complete project details at https://RandomNerdTutorials.com/esp32-websocket-server-arduino/
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";
bool ledState = 0;
const int ledPin = 2;
// Create AsyncWebServer object on port 80
AsyncWebServer server(80);
AsyncWebSocket ws("/ws");
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, Helvetica, sans-serif;
text-align: center;
}
h1 {
font-size: 1.8rem;
color: white;
}
h2{
font-size: 1.5rem;
font-weight: bold;
color: #143642;
}
.topnav {
overflow: hidden;
background-color: #143642;
}
body {
margin: 0;
}
.content {
padding: 30px;
max-width: 600px;
margin: 0 auto;
}
.card {
background-color: #F8F7F9;;
box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);
padding-top:10px;
padding-bottom:20px;
}
.button {
padding: 15px 50px;
font-size: 24px;
text-align: center;
outline: none;
color: #fff;
background-color: #0f8b8d;
border: none;
border-radius: 5px;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
/*.button:hover {background-color: #0f8b8d}*/
.button:active {
background-color: #0f8b8d;
box-shadow: 2 2px #CDCDCD;
transform: translateY(2px);
}
.state {
font-size: 1.5rem;
color:#8c8c8c;
font-weight: bold;
}
</style>
<title>ESP Web Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
</head>
<body>
<div class="topnav">
<h1>ESP WebSocket Server</h1>
</div>
<div class="content">
<div class="card">
<h2>Output - GPIO 2</h2>
<p class="state">state: <span id="state">%STATE%</span></p>
<p><button id="button" class="button">Toggle</button></p>
</div>
</div>
<script>
var gateway = `ws://${window.location.hostname}/ws`;
var websocket;
window.addEventListener('load', onLoad);
function initWebSocket() {
console.log('Trying to open a WebSocket connection...');
websocket = new WebSocket(gateway);
websocket.onopen = onOpen;
websocket.onclose = onClose;
websocket.onmessage = onMessage; // <-- add this line
}
function onOpen(event) {
console.log('Connection opened');
}
function onClose(event) {
console.log('Connection closed');
setTimeout(initWebSocket, 2000);
}
function onMessage(event) {
var state;
if (event.data == "1"){
state = "ON";
}
else{
state = "OFF";
}
document.getElementById('state').innerHTML = state;
}
function onLoad(event) {
initWebSocket();
initButton();
}
function initButton() {
document.getElementById('button').addEventListener('click', toggle);
}
function toggle(){
websocket.send('toggle');
}
</script>
</body>
</html>
)rawliteral";
void notifyClients() {
ws.textAll(String(ledState));
}
void handleWebSocketMessage(void *arg, uint8_t *data, size_t len) {
AwsFrameInfo *info = (AwsFrameInfo*)arg;
if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
data[len] = 0;
if (strcmp((char*)data, "toggle") == 0) {
ledState = !ledState;
notifyClients();
}
}
}
void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type,
void *arg, uint8_t *data, size_t len) {
switch (type) {
case WS_EVT_CONNECT:
Serial.printf("WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str());
break;
case WS_EVT_DISCONNECT:
Serial.printf("WebSocket client #%u disconnected\n", client->id());
break;
case WS_EVT_DATA:
handleWebSocketMessage(arg, data, len);
break;
case WS_EVT_PONG:
case WS_EVT_ERROR:
break;
}
}
void initWebSocket() {
ws.onEvent(onEvent);
server.addHandler(&ws);
}
String processor(const String& var){
Serial.println(var);
if(var == "STATE"){
if (ledState){
return "ON";
}
else{
return "OFF";
}
}
return String();
}
void setup(){
// Serial port for debugging purposes
Serial.begin(115200);
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, 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());
initWebSocket();
// Route for root / web page
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, "text/html", index_html, processor);
});
// Start server
server.begin();
}
void loop() {
ws.cleanupClients();
digitalWrite(ledPin, ledState);
}
Вставьте данные вашей сети в следующие переменные, и код будет работать сразу.
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
Как работает код
Продолжайте чтение, чтобы узнать, как работает код, или перейдите к разделу Демонстрация.
Импорт библиотек
Импортируйте необходимые библиотеки для создания веб-сервера.
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
Учётные данные сети
Вставьте данные вашей сети в следующие переменные:
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
Выход GPIO
Создайте переменную ledState для хранения состояния GPIO и переменную ledPin, которая ссылается на GPIO, которым вы хотите управлять. В данном случае мы будем управлять встроенным светодиодом (который подключен к GPIO 2).
bool ledState = 0;
const int ledPin = 2;
AsyncWebServer и AsyncWebSocket
Создайте объект AsyncWebServer на порту 80.
AsyncWebServer server(80);
Библиотека ESPAsyncWebServer включает плагин WebSocket, который упрощает обработку WebSocket-соединений. Создайте объект AsyncWebSocket с именем ws для обработки соединений по пути /ws.
AsyncWebSocket ws("/ws");
Создание веб-страницы
Переменная index_html содержит HTML, CSS и JavaScript, необходимые для создания и стилизации веб-страницы, а также для обработки взаимодействия клиент-сервер с использованием протокола WebSocket.
Примечание
Мы размещаем всё необходимое для создания веб-страницы в переменной index_html, которую используем в скетче Arduino. Обратите внимание, что может быть более практично иметь отдельные файлы HTML, CSS и JavaScript, которые затем вы загружаете в файловую систему ESP32 и ссылаетесь на них в коде.
Рекомендуем прочитать: ESP32 веб-сервер с использованием SPIFFS (SPI Flash File System)
Вот содержимое переменной index_html:
<!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, Helvetica, sans-serif;
text-align: center;
}
h1 {
font-size: 1.8rem;
color: white;
}
h2{
font-size: 1.5rem;
font-weight: bold;
color: #143642;
}
.topnav {
overflow: hidden;
background-color: #143642;
}
body {
margin: 0;
}
.content {
padding: 30px;
max-width: 600px;
margin: 0 auto;
}
.card {
background-color: #F8F7F9;;
box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);
padding-top:10px;
padding-bottom:20px;
}
.button {
padding: 15px 50px;
font-size: 24px;
text-align: center;
outline: none;
color: #fff;
background-color: #0f8b8d;
border: none;
border-radius: 5px;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
.button:active {
background-color: #0f8b8d;
box-shadow: 2 2px #CDCDCD;
transform: translateY(2px);
}
.state {
font-size: 1.5rem;
color:#8c8c8c;
font-weight: bold;
}
</style>
<title>ESP Web Server</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
</head>
<body>
<div class="topnav">
<h1>ESP WebSocket Server</h1>
</div>
<div class="content">
<div class="card">
<h2>Output - GPIO 2</h2>
<p class="state">state: <span id="state">%STATE%</span></p>
<p><button id="button" class="button">Toggle</button></p>
</div>
</div>
<script>
var gateway = `ws://${window.location.hostname}/ws`;
var websocket;
function initWebSocket() {
console.log('Trying to open a WebSocket connection...');
websocket = new WebSocket(gateway);
websocket.onopen = onOpen;
websocket.onclose = onClose;
websocket.onmessage = onMessage; // <-- add this line
}
function onOpen(event) {
console.log('Connection opened');
}
function onClose(event) {
console.log('Connection closed');
setTimeout(initWebSocket, 2000);
}
function onMessage(event) {
var state;
if (event.data == "1"){
state = "ON";
}
else{
state = "OFF";
}
document.getElementById('state').innerHTML = state;
}
window.addEventListener('load', onLoad);
function onLoad(event) {
initWebSocket();
initButton();
}
function initButton() {
document.getElementById('button').addEventListener('click', toggle);
}
function toggle(){
websocket.send('toggle');
}
</script>
</body>
</html>
CSS
Между тегами <style></style> мы включаем стили для оформления веб-страницы с помощью CSS. Вы можете изменить их, чтобы веб-страница выглядела так, как вам нравится. Мы не будем объяснять, как работает CSS для этой веб-страницы, поскольку это не относится к данному руководству по WebSocket.
<style>
html {
font-family: Arial, Helvetica, sans-serif;
text-align: center;
}
h1 {
font-size: 1.8rem;
color: white;
}
h2 {
font-size: 1.5rem;
font-weight: bold;
color: #143642;
}
.topnav {
overflow: hidden;
background-color: #143642;
}
body {
margin: 0;
}
.content {
padding: 30px;
max-width: 600px;
margin: 0 auto;
}
.card {
background-color: #F8F7F9;;
box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);
padding-top:10px;
padding-bottom:20px;
}
.button {
padding: 15px 50px;
font-size: 24px;
text-align: center;
outline: none;
color: #fff;
background-color: #0f8b8d;
border: none;
border-radius: 5px;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
.button:active {
background-color: #0f8b8d;
box-shadow: 2 2px #CDCDCD;
transform: translateY(2px);
}
.state {
font-size: 1.5rem;
color:#8c8c8c;
font-weight: bold;
}
</style>
HTML
Между тегами <body></body> мы добавляем содержимое веб-страницы, видимое пользователю.
<div class="topnav">
<h1>ESP WebSocket Server</h1>
</div>
<div class="content">
<div class="card">
<h2>Output - GPIO 2</h2>
<p class="state">state: <span id="state">%STATE%</span></p>
<p><button id="button" class="button">Toggle</button></p>
</div>
</div>
Есть заголовок первого уровня с текстом «ESP WebSocket Server». Вы можете изменить этот текст.
<h1>ESP WebSocket Server</h1>
Затем идёт заголовок второго уровня с текстом «Output – GPIO 2».
<h2>Output - GPIO 2</h2>
После этого у нас есть параграф, который отображает текущее состояние GPIO.
<p class="state">state: <span id="state">%STATE%</span></p>
%STATE% – это заполнитель для состояния GPIO. Он будет заменён текущим значением ESP32 в момент отправки веб-страницы. Заполнители в HTML-тексте должны быть между знаками %. Это означает, что текст %STATE% похож на переменную, которая затем будет заменена фактическим значением.
После отправки веб-страницы клиенту состояние должно динамически изменяться при любом изменении состояния GPIO. Мы получим эту информацию по протоколу WebSocket. Затем JavaScript обрабатывает, что делать с полученной информацией, чтобы соответствующим образом обновить состояние. Чтобы иметь возможность манипулировать этим текстом с помощью JavaScript, текст должен иметь id, на который мы можем ссылаться. В данном случае id – это state (<span id="state">).
Наконец, есть параграф с кнопкой для переключения состояния GPIO.
<p><button id="button" class="button">Toggle</button></p>
Обратите внимание, что мы присвоили кнопке id (id="button").
JavaScript – обработка WebSocket
JavaScript находится между тегами <script></script>. Он отвечает за инициализацию WebSocket-соединения с сервером, как только веб-интерфейс полностью загружен в браузере, и за обработку обмена данными через WebSocket.
<script>
var gateway = `ws://${window.location.hostname}/ws`;
var websocket;
function initWebSocket() {
console.log('Trying to open a WebSocket connection...');
websocket = new WebSocket(gateway);
websocket.onopen = onOpen;
websocket.onclose = onClose;
websocket.onmessage = onMessage; // <-- add this line
}
function onOpen(event) {
console.log('Connection opened');
}
function onClose(event) {
console.log('Connection closed');
setTimeout(initWebSocket, 2000);
}
function onMessage(event) {
var state;
if (event.data == "1"){
state = "ON";
}
else{
state = "OFF";
}
document.getElementById('state').innerHTML = state;
}
window.addEventListener('load', onLoad);
function onLoad(event) {
initWebSocket();
initButton();
}
function initButton() {
document.getElementById('button').addEventListener('click', toggle);
}
function toggle(){
websocket.send('toggle');
}
</script>
Давайте рассмотрим, как это работает.
Шлюз (gateway) – это точка входа в интерфейс WebSocket.
var gateway = `ws://${window.location.hostname}/ws`;
window.location.hostname получает текущий адрес страницы (IP-адрес веб-сервера).
Создайте новую глобальную переменную с именем websocket.
var websocket;
Добавьте обработчик событий, который вызовет функцию onload при загрузке веб-страницы.
window.addEventListener('load', onload);
Функция onload() вызывает функцию initWebSocket() для инициализации WebSocket-соединения с сервером и функцию initButton() для добавления обработчиков событий к кнопкам.
function onload(event) {
initWebSocket();
initButton();
}
Функция initWebSocket() инициализирует WebSocket-соединение на ранее определённом шлюзе. Мы также назначаем несколько callback-функций для обработки открытия, закрытия соединения и получения сообщения.
function initWebSocket() {
console.log('Trying to open a WebSocket connection...');
websocket = new WebSocket(gateway);
websocket.onopen = onOpen;
websocket.onclose = onClose;
websocket.onmessage = onMessage;
}
Когда соединение открывается, мы просто выводим сообщение в консоль и отправляем сообщение «hi». ESP32 получает это сообщение, поэтому мы знаем, что соединение было инициализировано.
function onOpen(event) {
console.log('Connection opened');
websocket.send('hi');
}
Если по какой-то причине WebSocket-соединение закрывается, мы снова вызываем функцию initWebSocket() через 2000 миллисекунд (2 секунды).
function onClose(event) {
console.log('Connection closed');
setTimeout(initWebSocket, 2000);
}
Метод setTimeout() вызывает функцию или вычисляет выражение после указанного количества миллисекунд.
Наконец, нам нужно обработать, что происходит при получении нового сообщения. Сервер (ваша плата ESP) отправит либо «1», либо «0». В соответствии с полученным сообщением мы хотим отобразить сообщение «ON» или «OFF» в параграфе, отображающем состояние. Помните тег <span> с id="state"? Мы получим этот элемент и установим его значение в ON или OFF.
function onMessage(event) {
var state;
if (event.data == "1"){
state = "ON";
}
else{
state = "OFF";
}
document.getElementById('state').innerHTML = state;
}
Функция initButton() получает кнопку по её id (button) и добавляет обработчик событий типа „click“.
function initButton() {
document.getElementById('button').addEventListener('click', toggle);
}
Это означает, что при нажатии на кнопку вызывается функция toggle.
Функция toggle отправляет сообщение через WebSocket-соединение с текстом „toggle“.
function toggle(){
websocket.send('toggle');
}
Затем ESP32 должен обработать, что происходит при получении этого сообщения – переключить текущее состояние GPIO.
Обработка WebSocket – сервер
Ранее вы видели, как обрабатывать WebSocket-соединение на стороне клиента (браузера). Теперь давайте рассмотрим, как обрабатывать его на стороне сервера.
Уведомление всех клиентов
Функция notifyClients() уведомляет всех клиентов сообщением, содержащим то, что вы передаёте в качестве аргумента. В данном случае мы хотим уведомить всех клиентов о текущем состоянии светодиода при каждом изменении.
void notifyClients() {
ws.textAll(String(ledState));
}
Класс AsyncWebSocket предоставляет метод textAll() для одновременной отправки одного и того же сообщения всем клиентам, подключённым к серверу.
Обработка сообщений WebSocket
Функция handleWebSocketMessage() – это callback-функция, которая будет выполняться при получении новых данных от клиентов по протоколу WebSocket.
void handleWebSocketMessage(void *arg, uint8_t *data, size_t len) {
AwsFrameInfo *info = (AwsFrameInfo*)arg;
if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
data[len] = 0;
if (strcmp((char*)data, "toggle") == 0) {
ledState = !ledState;
notifyClients();
}
}
}
Если мы получаем сообщение «toggle», мы переключаем значение переменной ledState. Кроме того, мы уведомляем всех клиентов, вызывая функцию notifyClients(). Таким образом, все клиенты уведомляются об изменении и обновляют интерфейс соответственно.
if (strcmp((char*)data, "toggle") == 0) {
ledState = !ledState;
notifyClients();
}
Настройка WebSocket сервера
Теперь нам нужно настроить обработчик событий для обработки различных асинхронных этапов протокола WebSocket. Этот обработчик событий может быть реализован путём определения функции onEvent() следующим образом:
void onEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type,
void *arg, uint8_t *data, size_t len) {
switch (type) {
case WS_EVT_CONNECT:
Serial.printf("WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str());
break;
case WS_EVT_DISCONNECT:
Serial.printf("WebSocket client #%u disconnected\n", client->id());
break;
case WS_EVT_DATA:
handleWebSocketMessage(arg, data, len);
break;
case WS_EVT_PONG:
case WS_EVT_ERROR:
break;
}
}
Аргумент type представляет событие, которое происходит. Он может принимать следующие значения:
WS_EVT_CONNECT– когда клиент подключился;WS_EVT_DISCONNECT– когда клиент отключился;WS_EVT_DATA– когда от клиента получен пакет данных;WS_EVT_PONG– в ответ на запрос ping;WS_EVT_ERROR– когда от клиента получена ошибка.
Инициализация WebSocket
Наконец, функция initWebSocket() инициализирует протокол WebSocket.
void initWebSocket() {
ws.onEvent(onEvent);
server.addHandler(&ws);
}
processor()
Функция processor() отвечает за поиск заполнителей в HTML-тексте и замену их нужными значениями перед отправкой веб-страницы в браузер. В нашем случае мы заменим заполнитель %STATE% на ON, если ledState равен 1. В противном случае заменим его на OFF.
String processor(const String& var){
Serial.println(var);
if(var == "STATE"){
if (ledState){
return "ON";
}
else{
return "OFF";
}
}
}
setup()
В setup() инициализируйте Serial Monitor для отладки.
Serial.begin(115200);
Установите ledPin как OUTPUT и установите его в LOW при первом запуске программы.
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);
Инициализируйте Wi-Fi и выведите IP-адрес ESP32 в Serial Monitor.
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());
Инициализируйте протокол WebSocket, вызвав ранее созданную функцию initWebSocket().
initWebSocket();
Обработка запросов
Отправляйте текст, сохранённый в переменной index_html, при получении запроса по корневому URL / – вам нужно передать функцию processor в качестве аргумента для замены заполнителей текущим состоянием GPIO.
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send_P(200, "text/html", index_html, processor);
});
Наконец, запустите сервер.
server.begin();
loop()
Светодиод будет физически управляться в loop().
void loop() {
ws.cleanupClients();
digitalWrite(ledPin, ledState);
}
Обратите внимание, что мы также вызываем метод cleanupClients(). Вот почему (объяснение со страницы GitHub библиотеки ESPAsyncWebServer):
Браузеры иногда не закрывают WebSocket-соединение корректно, даже когда функция close() вызвана в JavaScript. Это в конечном итоге исчерпает ресурсы веб-сервера и приведёт к его сбою. Периодический вызов функции cleanupClients() из основного цикла loop() ограничивает количество клиентов, закрывая самого старого клиента при превышении максимального числа клиентов. Эту функцию можно вызывать каждый цикл, однако, если вы хотите использовать меньше энергии, то вызова раз в секунду вполне достаточно.
Демонстрация
После ввода данных вашей сети в переменные ssid и password вы можете загрузить код на вашу плату. Не забудьте проверить, что выбраны правильная плата и COM-порт.
После загрузки кода откройте Serial Monitor на скорости 115200 бод и нажмите встроенную кнопку EN/RST. IP-адрес ESP должен быть выведен.
Откройте браузер в вашей локальной сети и введите IP-адрес ESP32. Вы должны получить доступ к веб-странице для управления выходом.
Нажмите на кнопку, чтобы переключить светодиод. Вы можете открыть несколько вкладок браузера одновременно или получить доступ к веб-серверу с разных устройств, и состояние светодиода будет автоматически обновляться у всех клиентов при любом изменении.
Заключение
В этом руководстве вы узнали, как настроить WebSocket-сервер на ESP32. Протокол WebSocket обеспечивает полнодуплексную связь между клиентом и сервером. После инициализации сервер и клиент могут обмениваться данными в любой момент времени.
Это очень полезно, потому что сервер может отправлять данные клиенту всякий раз, когда что-то происходит. Например, вы можете добавить физическую кнопку к этой установке, которая при нажатии уведомит всех клиентов об обновлении веб-интерфейса.
В этом примере мы показали, как управлять одним GPIO ESP32. Вы можете использовать этот метод для управления большим количеством GPIO. Вы также можете использовать протокол WebSocket для отправки показаний датчиков или уведомлений в любой момент времени.
Мы надеемся, что это руководство было для вас полезным. Мы планируем создать больше руководств и примеров с использованием протокола WebSocket. Так что следите за обновлениями.
Узнайте больше об ESP32 с помощью наших ресурсов:
Источник: ESP32 WebSocket Server: Control Outputs (Arduino IDE)