ESP32 Веб-сервер (WebSocket) с несколькими слайдерами: управление яркостью светодиодов (ШИМ)

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

ESP32 Веб-сервер WebSocket с несколькими слайдерами: управление яркостью светодиодов ШИМ

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

Для этого проекта плата ESP32 будет программироваться с использованием ядра Arduino. Вы можете использовать Arduino IDE, VS Code с PlatformIO или любую другую подходящую IDE.

Для лучшего понимания работы этого проекта рекомендуем ознакомиться со следующими руководствами:

* В этом проекте показано, как создать веб-сервер с одним слайдером, но он использует HTTP-запросы — в данном руководстве мы используем протокол WebSocket.

Аналогичное руководство для платы ESP8266 NodeMCU:

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

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

ESP32 Веб-сервер WebSocket обзор проекта слайдеры ШИМ
  • Веб-страница содержит три карточки;

  • Каждая карточка имеет абзац для отображения заголовка карточки (Fader 1, Fader 2, Fader 3);

  • В каждой карточке есть ползунок, который можно перемещать для установки яркости соответствующего светодиода;

  • В каждой карточке другой абзац отображает текущую яркость светодиода (в процентах);

  • Когда вы устанавливаете новое положение слайдера, все клиенты обновляют значения слайдеров (если у вас открыто несколько вкладок браузера или несколько устройств, они обновляются практически одновременно при любом изменении).

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

  • ESP размещает веб-сервер, который отображает веб-страницу с тремя слайдерами;

  • Когда вы устанавливаете новое положение слайдера, клиент отправляет номер слайдера и значение слайдера на сервер по протоколу WebSocket. Например, если вы устанавливаете слайдер номер 3 в положение 40, он отправит сообщение 3s40 на сервер.

ESP32 Веб-сервер несколько слайдеров как это работает
  • Сервер (ESP) получает номер слайдера и соответствующее значение и соответственно настраивает скважность ШИМ. Кроме того, он также уведомляет все остальные клиенты о новых текущих значениях слайдеров — это позволяет обновлять все клиенты практически мгновенно.

ESP32 Слайдеры Веб-сервер WebSocket обновление клиентов
  • ESP32 выводит ШИМ-сигнал с соответствующей скважностью для управления яркостью светодиода. Скважность 0% означает, что светодиод полностью выключен, скважность 50% означает, что светодиод горит вполсилы, а скважность 100% означает, что светодиод горит на полную мощность;

ESP32 яркость скважность веб-сервер пример ШИМ
  • Каждый раз, когда вы открываете новое окно браузера (когда подключается новый клиент), он отправляет сообщение ESP32 (также по протоколу WebSocket) с текстом getValues. Когда ESP32 получает это сообщение, он отправляет текущие значения слайдеров. Таким образом, при открытии новой вкладки всегда отображаются актуальные обновлённые значения.

ESP32 несколько слайдеров веб-сервер новый клиент обновление значений

Предварительные требования

Перед тем как приступить к этому руководству, убедитесь, что выполнены все следующие предварительные требования.

1) Необходимые компоненты

Для этого проекта вам понадобятся:

Для тестирования этого проекта вам необязательно нужны три светодиода — вы можете просто посмотреть результаты в мониторе порта или использовать другие приводы, требующие ШИМ-сигнала для работы.

2) Arduino IDE и дополнение для плат ESP32

Мы будем программировать ESP32 с использованием Arduino IDE. Поэтому у вас должно быть установлено дополнение ESP32. Следуйте следующему руководству, если вы ещё этого не сделали:

Если вы хотите использовать VS Code с расширением PlatformIO, следуйте следующему руководству:

3) Плагин загрузки файловой системы

Для загрузки файлов HTML, CSS и JavaScript, необходимых для этого проекта, во флеш-память ESP32 (LittleFS) мы используем плагин для Arduino IDE: LittleFS Filesystem uploader. Следуйте следующему руководству для установки плагина загрузки файловой системы, если вы ещё этого не сделали:

Если вы используете VS Code с расширением PlatformIO, прочитайте следующее руководство, чтобы узнать, как загружать файлы в файловую систему:

4) Библиотеки

Для сборки этого проекта вам необходимо установить следующие библиотеки:

Вы можете установить библиотеки через Arduino Library Manager. Перейдите в Sketch > Include Library > Manage Libraries и найдите названия библиотек.

Схема подключения

Подключите три светодиода к ESP32. Мы используем GPIO 12, 13 и 14. Вы можете использовать любые другие подходящие GPIO.

ESP32 подключение трёх светодиодов схема

Рекомендуемое чтение: Справочник по распиновке ESP32: какие GPIO использовать?

Организация файлов

Для организации проекта и облегчения понимания мы создадим четыре файла для веб-сервера:

  • Arduino скетч — обрабатывает веб-сервер;

  • index.html — определяет содержимое веб-страницы;

  • style.css — стилизует веб-страницу;

  • script.js — программирует поведение веб-страницы — обрабатывает перемещение слайдера, отправку, получение и интерпретацию сообщений, полученных по протоколу WebSocket.

Организация файлов arduino скетч index html style css script js

Вы должны сохранить файлы HTML, CSS и JavaScript в папке с именем data внутри папки скетча Arduino, как показано на предыдущей диаграмме. Мы загрузим эти файлы в файловую систему ESP32 (LittleFS).

Вы можете скачать все файлы проекта:

HTML файл

Скопируйте следующее содержимое в файл index.html.

<!-- Complete project details: https://randomnerdtutorials.com/esp32-web-server-websocket-sliders/ -->

<!DOCTYPE html>
<html>
<head>
    <title>ESP IOT DASHBOARD</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="icon" type="image/png" href="favicon.png">
    <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
    <div class="topnav">
        <h1>Multiple Sliders</h1>
    </div>
    <div class="content">
        <div class="card-grid">
            <div class="card">
                <p class="card-title">Fader 1</p>
                <p class="switch">
                    <input type="range" onchange="updateSliderPWM(this)" id="slider1" min="0" max="100" step="1" value ="0" class="slider">
                </p>
                <p class="state">Brightness: <span id="sliderValue1"></span> &percnt;</p>
            </div>
            <div class="card">
                <p class="card-title"> Fader 2</p>
                <p class="switch">
                    <input type="range" onchange="updateSliderPWM(this)" id="slider2" min="0" max="100" step="1" value ="0" class="slider">
                </p>
                <p class="state">Brightness: <span id="sliderValue2"></span> &percnt;</p>
            </div>
            <div class="card">
                <p class="card-title"> Fader 3</p>
                <p class="switch">
                    <input type="range" onchange="updateSliderPWM(this)" id="slider3" min="0" max="100" step="1" value ="0" class="slider">
                </p>
                <p class="state">Brightness: <span id="sliderValue3"></span> &percnt;</p>
            </div>
        </div>
    </div>
    <script src="script.js"></script>
</body>
</html>

Посмотреть исходный код

Рассмотрим наиболее важные части HTML-файла.

Создание слайдера

Следующие теги создают карточку для первого слайдера (Fader 1).

<div class="card">
  <p class="card-title">Fader 1</p>
  <p class="switch">
    <input type="range" onchange="updateSliderPWM(this)" id="slider1" min="0" max="100" step="1" value ="0" class="slider">
  </p>
  <p class="state">Brightness: <span id="sliderValue1"></span> &percnt;</p>
</div>

Первый абзац отображает заголовок карточки (Fader 1). Вы можете изменить текст на любой другой.

<p class="card-title">Fader 1</p>

Для создания слайдера в HTML используется тег <input>. Тег <input> определяет поле, в которое пользователь может вводить данные.

Существует множество типов ввода. Для определения слайдера используйте атрибут type со значением range. В слайдере также нужно определить минимальный и максимальный диапазон, используя атрибуты min и max (в данном случае 0 и 100 соответственно).

Вам также нужно определить другие атрибуты:

  • атрибут step задаёт интервал между допустимыми числами. В нашем случае мы установили его равным 1;

  • class для стилизации слайдера (class=»slider»);

  • id для управления значением слайдера с помощью JavaScript (id=»slider1»);

  • атрибут onchange для вызова функции (updateSliderPWM(this)) при установке нового положения слайдера. Эта функция (определённая в файле JavaScript) отправляет текущее значение слайдера по протоколу WebSocket клиенту. Ключевое слово this ссылается на HTML-элемент слайдера.

Слайдер находится внутри абзаца с именем класса switch. Итак, вот теги, которые фактически создают слайдер.

<p class="switch">
  <input type="range" onchange="updateSliderPWM(this)" id="slider1" min="0" max="100" step="1" value ="0" class="slider">
</p>

Наконец, есть абзац с тегом <span>, чтобы мы могли вставить текущее значение слайдера в этот абзац, обратившись к его id (id=»sliderValue1»).

<p class="state">Brightness: <span id="sliderValue1"></span> &percnt;</p>

Создание дополнительных слайдеров

Для создания дополнительных слайдеров необходимо скопировать все HTML-теги, создающие полную карточку. Однако сначала нужно учесть, что для каждого слайдера и значения слайдера нужен уникальный id. В нашем случае у нас три слайдера с id: slider1, slider2, slider3 и три заполнителя для значений слайдеров с id: sliderValue1, sliderValue2, sliderValue3.

Например, вот карточка для слайдера номер 2.

<div class="card">
  <p class="card-title"> Fader 2</p>
  <p class="switch">
    <input type="range" onchange="updateSliderPWM(this)" id="slider2" min="0" max="100" step="1" value ="0" class="slider">
  </p>
  <p class="state">Brightness: <span id="sliderValue2"></span> &percnt;</p>
</div>

CSS файл

Скопируйте следующее содержимое в файл style.css.

/*  Complete project details: https://randomnerdtutorials.com/esp32-web-server-websocket-sliders/  */

html {
    font-family: Arial, Helvetica, sans-serif;
    display: inline-block;
    text-align: center;
  }
  h1 {
    font-size: 1.8rem;
    color: white;
  }
  p {
    font-size: 1.4rem;
  }
  .topnav {
    overflow: hidden;
    background-color: #0A1128;
  }
  body {
    margin: 0;
  }
  .content {
    padding: 30px;
  }
  .card-grid {
    max-width: 700px;
    margin: 0 auto;
    display: grid;
    grid-gap: 2rem;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  }
  .card {
    background-color: white;
    box-shadow: 2px 2px 12px 1px rgba(140,140,140,.5);
  }
  .card-title {
    font-size: 1.2rem;
    font-weight: bold;
    color: #034078
  }
  .state {
    font-size: 1.2rem;
    color:#1282A2;
  }
  .slider {
    -webkit-appearance: none;
    margin: 0 auto;
    width: 100%;
    height: 15px;
    border-radius: 10px;
    background: #FFD65C;
    outline: none;
  }
  .slider::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    background: #034078;
    cursor: pointer;
  }
  .slider::-moz-range-thumb {
    width: 30px;
    height: 30px;
    border-radius: 50% ;
    background: #034078;
    cursor: pointer;
  }
  .switch {
    padding-left: 5%;
    padding-right: 5%;
  }

Посмотреть исходный код

Рассмотрим ключевые части CSS-файла, стилизующие слайдер. В этом примере нам нужно использовать вендорные префиксы для атрибута appearance.

.slider {
  -webkit-appearance: none;
  margin: 0 auto;
  width: 100%;
  height: 15px;
  border-radius: 10px;
  background: #FFD65C;
  outline: none;
}
.slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: #034078;
  cursor: pointer;
}
.slider::-moz-range-thumb {
  width: 30px;
  height: 30px;
  border-radius: 50% ;
  background: #034078;
  cursor: pointer;
}
.switch {
  padding-left: 5%;
  padding-right: 5%;
}

Вендорные префиксы

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

  • -webkit- Chrome, Safari, новые версии Opera, почти все браузеры iOS,

  • -moz- Firefox,

  • -o- Старые версии Opera,

  • -ms- Microsoft Edge и Internet Explorer.

Вендорные префиксы являются временными. Как только свойства будут полностью поддерживаться используемым вами браузером, они вам не понадобятся. Вы можете использовать следующий справочник, чтобы проверить, нужны ли префиксы для используемого вами свойства: shouldiprefix.com

Рассмотрим селектор .slider (стилизует сам слайдер):

.slider {
  -webkit-appearance: none;
  margin: 0 auto;
  width: 100%;
  height: 15px;
  border-radius: 10px;
  background: #FFD65C;outline: none;
}

Установка -webkit-appearance в none переопределяет стили CSS по умолчанию, применённые к слайдеру в браузерах Google Chrome, Safari и Android.

-webkit-appearance: none;

Установка margin в 0 auto выравнивает слайдер внутри родительского контейнера.

margin: 0 auto;

Ширина слайдера установлена на 100%, а высота — на 15px. Радиус скругления границ установлен на 10px.

margin: 0 auto;
width: 100%;
height: 15px;
border-radius: 10px;

Установите цвет фона для слайдера и установите outline в none.

background: #FFD65C;
outline: none;

Затем отформатируйте ручку слайдера. Используйте -webkit- для браузеров Chrome, Opera, Safari и Edge, а -moz- для Firefox.

.slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: #034078;
  cursor: pointer;
}
.slider::-moz-range-thumb {
  width: 30px;
  height: 30px;
  border-radius: 50% ;
  background: #034078;
  cursor: pointer;
}

Установите свойства -webkit-appearance и appearance в none, чтобы переопределить свойства по умолчанию.

-webkit-appearance: none;
appearance: none;

Установите определённую ширину, высоту и радиус скругления для ручки. Установка одинаковых ширины и высоты с border-radius 50% создаёт круг.

width: 30px;
height: 30px;
border-radius: 50%;

Затем установите цвет фона и курсор в виде указателя.

background: #034078;
cursor: pointer;

Не стесняйтесь экспериментировать со свойствами слайдера, чтобы придать ему другой вид.

JavaScript файл

Скопируйте следующее содержимое в файл script.js.

// Complete project details: https://randomnerdtutorials.com/esp32-web-server-websocket-sliders/

var gateway = `ws://${window.location.hostname}/ws`;
var websocket;
window.addEventListener('load', onload);

function onload(event) {
    initWebSocket();
}

function getValues(){
    websocket.send("getValues");
}

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');
    getValues();
}

function onClose(event) {
    console.log('Connection closed');
    setTimeout(initWebSocket, 2000);
}

function updateSliderPWM(element) {
    var sliderNumber = element.id.charAt(element.id.length-1);
    var sliderValue = document.getElementById(element.id).value;
    document.getElementById("sliderValue"+sliderNumber).innerHTML = sliderValue;
    console.log(sliderValue);
    websocket.send(sliderNumber+"s"+sliderValue.toString());
}

function onMessage(event) {
    console.log(event.data);
    var myObj = JSON.parse(event.data);
    var keys = Object.keys(myObj);

    for (var i = 0; i < keys.length; i++){
        var key = keys[i];
        document.getElementById(key).innerHTML = myObj[key];
        document.getElementById("slider"+ (i+1).toString()).value = myObj[key];
    }
}

Посмотреть исходный код

Вот список того, что делает этот код:

  • инициализирует WebSocket-соединение с сервером;

  • отправляет сообщение серверу для получения текущих значений слайдеров;

  • использует ответ для обновления значений слайдеров на веб-странице;

  • обрабатывает обмен данными по протоколу WebSocket.

Рассмотрим этот JavaScript-код подробнее.

Переменная gateway — это точка входа в интерфейс WebSocket. window.location.hostname получает текущий адрес страницы (IP-адрес веб-сервера).

var gateway = `ws://${window.location.hostname}/ws`;

Создайте новую глобальную переменную с именем websocket.

var websocket;

Добавьте слушатель событий, который вызовет функцию onload при загрузке веб-страницы.

window.addEventListener('load', onload);

Функция onload() вызывает функцию initWebSocket() для инициализации WebSocket-соединения с сервером.

function onload(event) {
  initWebSocket();
}

Функция initWebSocket() инициализирует WebSocket-соединение через шлюз, определённый ранее. Мы также назначаем несколько функций обратного вызова для случаев, когда WebSocket-соединение открывается, закрывается или получено сообщение.

function initWebSocket() {
  console.log('Trying to open a WebSocket connection...');
  websocket = new WebSocket(gateway);
  websocket.onopen = onOpen;
  websocket.onclose = onClose;
  websocket.onmessage = onMessage;
}

Обратите внимание, что при открытии WebSocket-соединения мы вызываем функцию getValues.

function onOpen(event) {
  console.log('Connection opened');
  getValues();
}

Функция getValues() отправляет сообщение getValues серверу для получения текущего значения всех слайдеров. Затем мы должны обработать, что произойдёт, когда мы получим это сообщение на стороне сервера (ESP32).

function getStates(){
  websocket.send("getValues");
}

Мы обрабатываем сообщения, полученные по протоколу WebSocket, в функции onMessage().

function onMessage(event) {
  console.log(event.data);
  var myObj = JSON.parse(event.data);
  var keys = Object.keys(myObj);

  for (var i = 0; i < keys.length; i++){
    var key = keys[i];
    document.getElementById(key).innerHTML = myObj[key];
    document.getElementById("slider"+ (i+1).toString()).value = myObj[key];
  }
}

Сервер отправляет состояния в формате JSON, например:

{
  sliderValue1 : 20;
  sliderValue2: 50;
  sliderValue3: 0;
}

Функция onMessage() просто проходит по всем значениям и размещает их в соответствующих местах на HTML-странице.

Функция updateSliderPWM() запускается при перемещении слайдеров.

function updateSliderPWM(element) {
  var sliderNumber = element.id.charAt(element.id.length-1);
  var sliderValue = document.getElementById(element.id).value;
  document.getElementById("sliderValue"+sliderNumber).innerHTML = sliderValue;
  console.log(sliderValue);
  websocket.send(sliderNumber+"s"+sliderValue.toString());
}

Эта функция получает значение слайдера и обновляет соответствующий абзац правильным значением. Эта функция также отправляет сообщение серверу, чтобы ESP32 обновил яркость светодиода.

websocket.send(sliderNumber+"s"+sliderValue.toString());

Сообщение отправляется в следующем формате:

  • номерслайдера s значениеслайдера

Например, если вы перемещаете слайдер номер 3 в положение 40, будет отправлено следующее сообщение:

3s40

Скетч Arduino

Скопируйте следующий код в Arduino IDE или в файл main.cpp, если вы используете PlatformIO.

/*
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp32-web-server-websocket-sliders/
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files.
  The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
#include <Arduino.h>
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include "LittleFS.h"
#include <Arduino_JSON.h>

// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_SSID";

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);
// Create a WebSocket object

AsyncWebSocket ws("/ws");
// Set LED GPIO
const int ledPin1 = 12;
const int ledPin2 = 13;
const int ledPin3 = 14;

String message = "";
String sliderValue1 = "0";
String sliderValue2 = "0";
String sliderValue3 = "0";

int dutyCycle1;
int dutyCycle2;
int dutyCycle3;

// setting PWM properties
const int freq = 5000;
const int ledChannel1 = 0;
const int ledChannel2 = 1;
const int ledChannel3 = 2;

const int resolution = 8;

//Json Variable to Hold Slider Values
JSONVar sliderValues;

//Get Slider Values
String getSliderValues(){
  sliderValues["sliderValue1"] = String(sliderValue1);
  sliderValues["sliderValue2"] = String(sliderValue2);
  sliderValues["sliderValue3"] = String(sliderValue3);

  String jsonString = JSON.stringify(sliderValues);
  return jsonString;
}

// Initialize LittleFS
void initFS() {
  if (!LittleFS.begin()) {
    Serial.println("An error has occurred while mounting LittleFS");
  }
  else{
   Serial.println("LittleFS mounted successfully");
  }
}

// Initialize WiFi
void initWiFi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi ..");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(1000);
  }
  Serial.println(WiFi.localIP());
}

void notifyClients(String sliderValues) {
  ws.textAll(sliderValues);
}

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;
    message = (char*)data;
    if (message.indexOf("1s") >= 0) {
      sliderValue1 = message.substring(2);
      dutyCycle1 = map(sliderValue1.toInt(), 0, 100, 0, 255);
      Serial.println(dutyCycle1);
      Serial.print(getSliderValues());
      notifyClients(getSliderValues());
    }
    if (message.indexOf("2s") >= 0) {
      sliderValue2 = message.substring(2);
      dutyCycle2 = map(sliderValue2.toInt(), 0, 100, 0, 255);
      Serial.println(dutyCycle2);
      Serial.print(getSliderValues());
      notifyClients(getSliderValues());
    }
    if (message.indexOf("3s") >= 0) {
      sliderValue3 = message.substring(2);
      dutyCycle3 = map(sliderValue3.toInt(), 0, 100, 0, 255);
      Serial.println(dutyCycle3);
      Serial.print(getSliderValues());
      notifyClients(getSliderValues());
    }
    if (strcmp((char*)data, "getValues") == 0) {
      notifyClients(getSliderValues());
    }
  }
}
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);
}

void setup() {
  Serial.begin(115200);
  pinMode(ledPin1, OUTPUT);
  pinMode(ledPin2, OUTPUT);
  pinMode(ledPin3, OUTPUT);
  initFS();
  initWiFi();

  // Set up LEDC pins
  ledcAttachChannel(ledPin1, freq, resolution, ledChannel1);
  ledcAttachChannel(ledPin2, freq, resolution, ledChannel2);
  ledcAttachChannel(ledPin3, freq, resolution, ledChannel3);

  initWebSocket();

  // Web Server Root URL
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(LittleFS, "/index.html", "text/html");
  });

  server.serveStatic("/", LittleFS, "/");

  // Start server
  server.begin();
}

void loop() {
  ledcWrite(ledPin1, dutyCycle1);
  ledcWrite(ledPin2, dutyCycle2);
  ledcWrite(ledPin3, dutyCycle3);

  ws.cleanupClients();
}

Посмотреть исходный код

Как работает код

Рассмотрим ключевые части кода этого проекта. Для лучшего понимания работы кода рекомендуем ознакомиться с этим руководством о протоколе WebSocket с ESP32 и этим руководством о ШИМ с ESP32.

Вставьте свои сетевые учётные данные в следующие переменные, чтобы подключить ESP32 к вашей локальной сети:

const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Функция getSliderValues() создаёт JSON-строку с текущими значениями слайдеров.

String getSliderValues(){
  sliderValues["sliderValue1"] = String(sliderValue1);
  sliderValues["sliderValue2"] = String(sliderValue2);
  sliderValues["sliderValue3"] = String(sliderValue3);

  String jsonString = JSON.stringify(sliderValues);
  return jsonString;
}

Функция notifyClients() уведомляет все клиенты о текущих значениях слайдеров. Вызов этой функции позволяет нам уведомлять об изменениях все клиенты при установке нового положения слайдера.

void notifyClients(String sliderValues) {
  ws.textAll(sliderValues);
}

Функция handleWebSocketMessage(), как следует из названия, обрабатывает действия при получении сервером сообщения от клиента по протоколу WebSocket. Мы видели в файле JavaScript, что сервер может получить сообщение getValues или сообщение с номером и значением слайдера.

Когда он получает сообщение getValues, он отправляет текущие значения слайдеров.

if (strcmp((char*)data, "getValues") == 0) {
  notifyClients(getSliderValues());
}

Если он получает другое сообщение, мы проверяем, какому слайдеру соответствует сообщение, и обновляем соответствующее значение скважности. Наконец, мы уведомляем все клиенты об изменении. Вот пример для слайдера 1:

if (message.indexOf("1s") >= 0) {
  sliderValue1 = message.substring(2);
  dutyCycle1 = map(sliderValue1.toInt(), 0, 100, 0, 255);
  Serial.println(dutyCycle1);
  Serial.print(getSliderValues());
  notifyClients(getSliderValues());
}

В loop() мы обновляем скважность каналов ШИМ для регулировки яркости светодиодов.

void loop() {
  ledcWrite(ledPin1, dutyCycle1);
  ledcWrite(ledPin2, dutyCycle2);
  ledcWrite(ledPin3, dutyCycle3);

  ws.cleanupClients();
}

Загрузка кода и файлов

После ввода сетевых учётных данных сохраните код. Перейдите в Sketch > Show Sketch Folder и создайте папку с именем data.

Arduino IDE открытие папки скетча для создания папки data

Внутри этой папки вы должны сохранить файлы HTML, CSS и JavaScript.

Затем загрузите код на плату ESP32. Убедитесь, что выбраны правильная плата и COM-порт. Также убедитесь, что вы добавили свои сетевые учётные данные.

После загрузки кода вам нужно загрузить файлы в файловую систему.

Нажмите [Ctrl] + [Shift] + [P] на Windows или [] + [Shift] + [P] на MacOS, чтобы открыть палитру команд. Найдите команду Upload LittleFS to Pico/ESP8266/ESP32 и нажмите на неё.

Если у вас нет этой опции, значит вы не установили плагин загрузки файловой системы. Ознакомьтесь с этим руководством.

ESP32 загрузка данных LittleFS Arduino IDE

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

Когда всё будет успешно загружено, откройте монитор порта на скорости 115200 бод. Нажмите кнопку EN/RST на ESP32, и он должен вывести IP-адрес ESP32.

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

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

ESP32 Веб-сервер демонстрация WebSocket несколько слайдеров управление яркостью светодиодов ШИМ

Перемещайте слайдеры для управления яркостью светодиодов.

ESP32 несколько слайдеров веб-сервер WebSocket Arduino

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

Заключение

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

Для углублённого изучения создания веб-серверов с ESP32 настоятельно рекомендуем ознакомиться с электронной книгой:

Узнайте больше об ESP32 из наших ресурсов:

Оригинальная статья: Rui Santos & Sara Santos — Random Nerd Tutorials