Каталог

ESP32 WebSocket Server

В этом уроке вы узнаете, как создать веб-сервер с ESP32, используя протокол связи WebSocket. В качестве примера мы покажем вам, как создать веб-страницу для удаленного управления выводами ESP32. Состояние вывода отображается на веб-странице и автоматически обновляется на всех клиентах.

ESP32 будет запрограммирован с использованием Arduino IDE и библиотеки ESPAsyncWebServer. У нас также есть похожее руководство по WebSocket для ESP8266.

Если вы следили за некоторыми из наших предыдущих проектов веб-серверов, таких как этот, вы могли заметить, что если у вас открыто несколько вкладок (на том же или на разных устройствах) одновременно, состояние не обновляется во всех вкладках автоматически, если не обновить веб-страницу. Чтобы решить эту проблему, мы можем использовать протокол WebSocket – все клиенты могут быть уведомлены, когда происходит изменение, и обновить веб-страницу соответствующим образом.

Это руководство основано на проекте, созданном и задокументированном одним из наших читателей (Стефан Кальдерони). Вы можете прочитать его отличное руководство здесь.

Введение в WebSocket

WebSocket – это постоянное соединение между клиентом и сервером, которое позволяет двустороннюю связь между обеими сторонами с использованием TCP-соединения. Это означает, что вы можете отправлять данные от клиента на сервер и от сервера на клиента в любое время.

Клиент устанавливает соединение WebSocket с сервером через процесс, известный как WebSocket handshake (рукопожатие WebSocket). Рукопожатие начинается с HTTP-запроса/ответа, позволяя серверам обрабатывать HTTP-соединения, а также соединения WebSocket на одном и том же порту. Как только соединение установлено, клиент и сервер могут отправлять данные WebSocket в режиме полного дуплекса.

Используя протокол WebSocket, сервер (плата ESP32) может отправлять информацию клиенту или всем клиентам без запроса. Это также позволяет нам отправлять информацию в веб-браузер, когда происходит изменение.

Это изменение может быть вызвано чем-то, что произошло на веб-странице (вы нажали кнопку) или чем-то, что произошло на стороне ESP32, например, нажатие физической кнопки на схеме.


Обзор проекта

Вот веб-страница, которую мы создадим в этом проекте:

  1. Веб-сервер ESP32 отображает веб-страницу с кнопкой для переключения состояния GPIO 2.
  2. Для простоты мы управляем GPIO 2 — встроенным светодиодом. Вы можете использовать этот пример для управления любым другим GPIO.
  3. Интерфейс показывает текущее состояние GPIO. Когда состояние GPIO изменяется, интерфейс мгновенно обновляется.
  4. Состояние GPIO автоматически обновляется на всех клиентах. Это означает, что если у вас открыто несколько вкладок браузера на одном или разных устройствах, они все обновляются одновременно.

Как это работает?

На изображении ниже показано, что происходит, когда вы нажимаете кнопку "Toggle" (Переключить):

  1. Нажмите кнопку "Toggle".
  2. Клиент (ваш браузер) отправляет данные по протоколу WebSocket с сообщением "toggle".
  3. ESP32 (сервер) получает это сообщение и знает, что нужно переключить состояние светодиода. Если светодиод был выключен, включите его.
  4. Затем он отправляет данные с новым состоянием светодиода всем клиентам также по протоколу WebSocket.
  5. Клиенты получают сообщение и обновляют состояние светодиода на веб-странице соответственно. Это позволяет мгновенно обновлять всех клиентов при изменении состояния.

Подготовка Arduino IDE

Мы будем программировать плату ESP32 с использованием Arduino IDE, поэтому убедитесь, что у вас установлена последняя версия Arduino IDE.


Установка библиотек – Async Web Server

Для создания веб-сервера мы будем использовать библиотеку ESPAsyncWebServer. Эта библиотека требует библиотеку AsyncTCP для корректной работы. Скачайте библиотеки по следующим ссылкам:

Эти библиотеки недоступны для установки через Arduino Library Manager, поэтому вам нужно скопировать файлы библиотек в папку Libraries установки Arduino. В качестве альтернативы, в Arduino IDE можно перейти в Sketch > Include Library > Add .zip Library и выбрать библиотеки, которые вы только что скачали.


Код для ESP32 WebSocket-сервера

Скопируйте следующий код в вашу Arduino IDE.

// Импорт необходимых библиотек
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>

// Замените на ваши сетевые данные
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

bool ledState = 0;
const int ledPin = 2;

// Создайте объект AsyncWebServer на порту 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;
    user-select: none;
  }
   .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;
  }
  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.begin(115200);

  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);
  
  // Подключение к Wi-Fi
  WiFi::begin(ssid, password);
  while (WiFi::status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Подключение к Wi-Fi..");
  }

  // Печать локального IP-адреса ESP
  Serial.println(WiFi::localIP());

  initWebSocket();

  // Маршрут для корня / веб-страницы
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", index_html, processor);
  });

  // Запуск сервера
  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 Output

Создайте переменную 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 и ссылаются на них в коде.

Вот содержимое переменной 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;
    user-select: none;
  }
   .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;
  }
  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>

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;
    user-select: none;
  }
   .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>

Здесь есть заголовок 1 с текстом «ESP WebSocket Server». Вы можете изменить этот текст.

<h1>ESP WebSocket Server</h1>

Затем идет заголовок 2 с текстом «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, текст должен иметь идентификатор, на который мы можем ссылаться. В данном случае идентификатором является state (<span id="state">).

Наконец, есть абзац с кнопкой для переключения состояния GPIO.

<p><button id="button" class="button">Toggle</button></p>

Обратите внимание, что мы дали кнопке идентификатор (id="button").

JavaScript – Обработка WebSocket

JavaScript располагается между тегами <script></script>. Он отвечает за инициализацию соединения WebSocket с сервером, как только веб-интерфейс полностью загружен в браузере, и обработку обмена данными через WebSocket.

<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;
  }
  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>

Рассмотрим, как это работает.

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 на ранее определенном gateway. Мы также назначаем несколько функций обратного вызова для случаев, когда соединение WebSocket открыто, закрыто или когда получено сообщение.

  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() получает кнопку по ее идентификатору (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() является функцией обратного вызова, которая будет выполняться всякий раз, когда мы получаем новые данные от клиентов через протокол 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";
    }
  }
  return String();
}

setup()

В функции setup() инициализируйте Серийный монитор для отладки.

Serial.begin(115200);

Настройте ledPin как OUTPUT и установите его в LOW при первом запуске программы.

  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);

Инициализируйте Wi-Fi и выведите IP-адрес ESP32 на Серийный монитор.

WiFi::begin(ssid, password);
while (WiFi::status() != WL_CONNECTED) {
  delay(1000);
  Serial.println("Подключение к Wi-Fi..");
}

// Печать локального IP-адреса ESP
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() из основного цикла ограничивает количество клиентов, закрывая самого старого клиента, когда превышено максимальное количество клиентов. Это можно вызывать на каждом цикле, однако, если вы хотите потреблять меньше энергии, то достаточно вызывать хотя бы раз в секунду.

Демонстрация

После вставки ваших сетевых данных в переменные ssid и password, вы можете загрузить код на свою плату. Не забудьте проверить, что у вас выбрана правильная плата и COM-порт.

После загрузки кода откройте Серийный монитор на скорости 115200 и нажмите кнопку EN/RST на плате. Должен быть напечатан IP-адрес ESP.

Откройте браузер в вашей локальной сети и введите IP-адрес ESP32. Вы должны получить доступ к веб-странице для управления выводом.

Нажмите на кнопку, чтобы переключить светодиод. Вы можете открыть несколько вкладок веб-браузера одновременно или получить доступ к веб-серверу с разных устройств, и состояние светодиода будет автоматически обновляться на всех клиентах, когда происходит изменение.

Заключение

В этом уроке вы узнали, как настроить WebSocket-сервер с помощью ESP32. Протокол WebSocket позволяет двустороннюю связь между клиентом и сервером. После инициализации сервер и клиент могут обмениваться данными в любое время.

Комментарии
Отзывов еще никто не оставлял
Предзаказ
Предзаказ успешно отправлен!
Имя *
Телефон *
Добавить в корзину
Название товара
100 тг
1 шт.
Перейти в корзину
Обратный звонок
Запрос успешно отправлен!
Имя *
Телефон *
Заказ в один клик
С помощью уведомлений о заказе можно не только получать актуальную информацию по заказу, но и иметь быстрый канал связи с магазином