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

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

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

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

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

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

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

У нас есть аналогичное руководство для платы ESP32:

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

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

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

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

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

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

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

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

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

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

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

ESP8266 NodeMCU слайдер веб-сервер уведомление всех клиентов WebSocket
  • ESP8266 выводит ШИМ-сигнал с соответствующим коэффициентом заполнения для управления яркостью светодиода. Коэффициент заполнения 0% означает, что светодиод полностью выключен, коэффициент заполнения 50% означает, что светодиод горит наполовину, а коэффициент заполнения 100% означает, что светодиод горит;

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

ESP8266 NodeMCU несколько слайдеров веб-сервер обновление значений нового клиента

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

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

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

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

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

Вы можете использовать ссылки выше или перейти непосредственно на MakerAdvisor.com/tools, чтобы найти все комплектующие для своих проектов по лучшей цене!

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • Скетч Arduino, который обрабатывает веб-сервер;

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

  • style.css: для стилизации веб-страницы;

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

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

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

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

HTML-файл

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

<!-- Complete project details: https://randomnerdtutorials.com/esp8266-nodemcu-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/esp8266-nodemcu-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.

Вендорные префиксы являются временными. Как только свойства будут полностью поддержаны браузером, который вы используете, они вам не понадобятся. Вы можете использовать следующий ресурс, чтобы проверить, нужны ли префиксы для используемого вами свойства: http://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;

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

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

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

background: #034078;
cursor: pointer;

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

JavaScript-файл

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

// Complete project details: https://randomnerdtutorials.com/esp8266-nodemcu-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 на сервер для получения текущего значения всех слайдеров. Затем мы должны обработать то, что происходит при получении этого сообщения на стороне сервера (ESP8266).

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

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

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

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

  • slidernumber s slidervalue

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

3s40

Скетч Arduino

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

/*
  Rui Santos
  Complete project details at https://RandomNerdTutorials.com/esp8266-nodemcu-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 <ESP8266WiFi.h>
#include <ESPAsyncTCP.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_PASSWORD";

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

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

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

int dutyCycle1;
int dutyCycle2;
int dutyCycle3;

//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, 1023);
      Serial.println(dutyCycle1);
      Serial.print(getSliderValues());
      notifyClients(getSliderValues());
    }
    if (message.indexOf("2s") >= 0) {
      sliderValue2 = message.substring(2);
      dutyCycle2 = map(sliderValue2.toInt(), 0, 100, 0, 1023);
      Serial.println(dutyCycle2);
      Serial.print(getSliderValues());
      notifyClients(getSliderValues());
    }
    if (message.indexOf("3s") >= 0) {
      sliderValue3 = message.substring(2);
      dutyCycle3 = map(sliderValue3.toInt(), 0, 100, 0, 1023);
      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();

  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() {
  analogWrite(ledPin1, dutyCycle1);
  analogWrite(ledPin2, dutyCycle2);
  analogWrite(ledPin3, dutyCycle3);

  ws.cleanupClients();
}

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

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

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

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

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, 1023);
  Serial.println(dutyCycle1);
  Serial.print(getSliderValues());
  notifyClients(getSliderValues());
}

В функции loop() мы обновляем коэффициент заполнения ШИМ-каналов для регулировки яркости светодиодов.

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

  ws.cleanupClients();
}

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

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

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

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

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

Кнопка загрузки Arduino IDE

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

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

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

ESP8266 загрузка образа файловой системы

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

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

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

ESP8266 NodeMCU веб-сервер WebSocket с несколькими слайдерами управление яркостью светодиодов демонстрация

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

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

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

Вы можете посмотреть видеодемонстрацию (следующее видео показывает демонстрацию для ESP32, но с ESP8266 работает аналогично):

Заключение

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

Чтобы узнать больше о создании веб-серверов с платами ESP8266, мы рекомендуем ознакомиться с нашей электронной книгой:

Узнайте больше об ESP8266 с помощью наших ресурсов:

Спасибо за чтение.