WebSocket - кнопка управления светодиодом.

Adolf_Balalaykin
Offline
Зарегистрирован: 01.02.2021

Доброго времени суток!

Колекционирую интересные решения, что-бы позже использовать в своих пректах. На этот раз решил повторить этот интересный урок https://randomnerdtutorials.com/esp32-websocket-server-arduino/. Но с небольшой модернизацией. Добавил вторую кнопку и реализовал на SPIFFS. Поправил код, всё работает но остался один косяк. Кнопки срабатывают, светодиоды отрабатывают корректно. НО! статус состояния кнопок срабатывает сразу на обе кнопки. Как будто они запаралелены. Например, включаешь первый светодиод, статус показывает что обо светодиода включились, хотя по факту как и положено включился только один. Бьюсь уже пару дней, никак не получается найти выход.

HTML:

<!DOCTYPE HTML><html>
<head>
  <meta content="text/html;charset=utf-8" http-equiv="Content-Type">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" href="data:,">
  <title>ESP Web Server</title>
  <link rel="stylesheet" href="style.css">
  <script src="script.js"></script>
</head>
<body>
  <div class="topnav">
    <h1>ESP WebSocket Server</h1>
  </div>
  <div class="content">
    <div class="card">
      <h2>Вывод - GPIO 32</h2>
      <p class="state">состояние: <span id="state1">%STATE1%</span></p>
      <p><button id="button1" class="button">Кнопка 1</button></p>  
	  <h2>Вывод - GPIO 33</h2>
      <p class="state">состояние: <span id="state2">%STATE2%</span></p>  <!--Текущее состояние GPIO-->
      <p><button id="button2" class="button">Кнопка 2</button></p> <!--Кнопка для переключения состояния GPIO-->

    </div>
  </div>
</body>
</html>

Скетч:

// Import required libraries
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <SPIFFS.h>
//#include <DHT.h>
//#include <BH1750.h>
//#include <Adafruit_Sensor.h>
//#include <Adafruit_BME280.h>
//#include <Wire.h>
//#include <OneWire.h>

// Задаем сетевые настройки
const char* ssid = "Uname";
const char* password = "Pass";
IPAddress local_IP(192, 168, 1, 68);  // Задаем статический IP-адрес:
IPAddress gateway(192, 168, 1, 102);  // Задаем IP-адрес сетевого шлюза:
IPAddress subnet(255, 255, 255, 0);    // Задаем маску сети:
IPAddress primaryDNS(8, 8, 8, 8);      // Основной ДНС (опционально)
IPAddress secondaryDNS(8, 8, 4, 4);    // Резервный ДНС (опционально)
AsyncWebServer server(80);
AsyncWebSocket ws("/ws");

bool ledState1 = 0;
bool ledState2 = 0;
const int ledPin1 = 32;
const int ledPin2 = 33;

// Уведомляем клиентов о текущем состоянии светодиода
void notifyClients1() {
  ws.textAll(String(ledState1));
}

void notifyClients2() {
  ws.textAll(String(ledState2));
}

/* функция обратного вызова, которая запускается всякий раз, когда мы получаем новые
  данные от клиентов по протоколу WebSocket. Если мы получаем сообщение “toggle”, мы
  переключаем значение переменной ledState. Кроме того, мы уведомляем всех клиентов,
  вызывая функцию notifyClients (). Таким образом, все клиенты получают уведомление об
  изменении и соответствующим образом обновляют интерфейс.*/
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, "toggle1") == 0) {
      ledState1 = !ledState1;
      notifyClients1();
    }
    if (strcmp((char*)data, "toggle2") == 0) {
      ledState2 = !ledState2;
      notifyClients2();
    }
  }
}

// Настройка прослушивателя событий для обработки различных асинхронных шагов протокола WebSocket
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:                     // в ответ на запрос ping
    case WS_EVT_ERROR:                    // при получении ошибки от клиента
      break;
  }
}

// Инициализация WebSocket
void initWebSocket() {
  ws.onEvent(onEvent);
  server.addHandler(&ws);
}

String processor(const String& var) {
  Serial.println(var);
  if (var == "STATE1") {
    if (ledState1) {
      return "ON";
    }
    else {
      return "OFF";
    }
  }

  if (var == "STATE2") {
    if (ledState2) {
      return "ON";
    }
    else {
      return "OFF";
    }
  }
}

void setup() {
  // Serial port for debugging purposes
  Serial.begin(115200);

  pinMode(ledPin1, OUTPUT);
  digitalWrite(ledPin1, LOW);
  pinMode(ledPin2, OUTPUT);
  digitalWrite(ledPin2, LOW);

  // Настраиваем статический IP-адрес:
  if (!WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS)) {
    Serial.println("Режим клиента не удалось настроить");
  }

  if (!SPIFFS.begin()) {
    Serial.println("An Error has occurred while mounting SPIFFS");
    return;
  }

  // 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(SPIFFS, "/index.html", "text/html");
  });

  server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest * request) {
    request->send(SPIFFS, "/style.css", "text/css");
  });

  server.on("/script.js", HTTP_GET, [](AsyncWebServerRequest * request) {
    request->send(SPIFFS, "/script.js", "text/javascript");
  });

  // Start server
  server.begin();
}

void loop() {
  ws.cleanupClients();
  digitalWrite(ledPin1, ledState1);
  digitalWrite(ledPin2, ledState2);
}

Java scrypt:



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 state1;
  if (event.data == "1"){
    state1 = "ON";
	  document.getElementById('button1').style.backgroundColor = "#04b50a";
    }
    else{
      state1 = "OFF";
	  document.getElementById('button1').style.backgroundColor = "#c90411";
    }
    document.getElementById('state1').innerHTML = state1;  
//----	
	 var state2;   
    if (event.data == "1"){
      state2 = "ON";
	  document.getElementById('button2').style.backgroundColor = "#04b50a";
    }
    else{
     state2 = "OFF";
	  document.getElementById('button2').style.backgroundColor = "#c90411";
    }
    document.getElementById('state2').innerHTML = state2;
 }
 
  function onLoad(event) {
    initWebSocket();
	initButton();
  }

  function initButton() {
    document.getElementById('button1').addEventListener('click', toggle1);
	document.getElementById('button2').addEventListener('click', toggle2);
  }
   
  function toggle1(){
    websocket.send('toggle1');
  }
  
    function toggle2(){
    websocket.send('toggle2');
  }

Спасибо!

 

rkit
Offline
Зарегистрирован: 23.11.2016

Так у тебя нету никакого разделения между уведомлениями о состоянии пинов.

Logik
Offline
Зарегистрирован: 05.08.2014

да вроде есть.

045   if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
046     data[len] = 0;
047     if (strcmp((char*)data, "toggle1") == 0) {
048       ledState1 = !ledState1;
049       notifyClients1();
050     }
051     if (strcmp((char*)data, "toggle2") == 0) {
052       ledState2 = !ledState2;
053       notifyClients2();
054     }
055   }
 

Условие там правда мутное...

Logik
Offline
Зарегистрирован: 05.08.2014

Уведомления стр.31 и 35. Одинаковые, без различения кнопок. Коим хером в джаве различат к какой кнопке оно относится?

Adolf_Balalaykin
Offline
Зарегистрирован: 01.02.2021

Logik пишет:

Уведомления стр.31 и 35. Одинаковые, без различения кнопок. Коим хером в джаве различат к какой кнопке оно относится?

Можно пример?

getElementById('button1') Разве не то?

Logik
Offline
Зарегистрирован: 05.08.2014

нет.

21   function onMessage(event) {
22     var state1;
23   if (event.data == "1"){
24     state1 = "ON";
25       document.getElementById('button1').style.backgroundColor = "#04b50a";
26     }
27     else{
28       state1 = "OFF";
29       document.getElementById('button1').style.backgroundColor = "#c90411";
30     }
31     document.getElementById('state1').innerHTML = state1; 
32 //---- 
33      var state2;  
34     if (event.data == "1"){
35       state2 = "ON";
36       document.getElementById('button2').style.backgroundColor = "#04b50a";
 

в event.data приезжает "1", и оба условия стр.23 и 34 срабатывают. Надо както отличать, когда приезжает "1" для одной, а когда для второй кнопки.

Проще всего 

034 void notifyClients2() {
035   ws.textAll(String(ledState2+2));

Тогда для кнопки 1 будут по старому ехать 1  и 0, а для второй кнопки  - 2 и 3. 

Ну и 

 

32 //---- 
33      var state2;  
34     if (event.data == "3"){
35       state2 = "ON";
36       document.getElementById('button2').style.backgroundColor = "#04b50a";
 

 

Adolf_Balalaykin
Offline
Зарегистрирован: 01.02.2021

Спасибо от всей души! Довольно четко объяснили. Сработало! Только вот статус показывает либо по одной кнопке, либо по другой. Если вкючены обе, то статус "включено" сработает  лишь на одной из двух.

rkit
Offline
Зарегистрирован: 23.11.2016

Может ты попытаешься разобраться в собственном коде? Проблема - ерунда, если ты умеешь читать код. А если не умеешь, то зачем вообще лезть? Учиться нужно по порядку.

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

Да надо то вчера и сразу! ) 

Adolf_Balalaykin
Offline
Зарегистрирован: 01.02.2021

Решение:

Собственно кусок кода

// Оповещаяем клиентов
void notifyClients1() {ws.textAll(String(ledState1));}
void notifyClients2() {ws.textAll(String(ledState2+2));}
void notifyClients3() {ws.textAll(String(ledState3+4));}

Java script

  function onMessage(event) { 
    switch(event.data) {
      case '0': document.getElementById("state1").innerHTML = "OFF"; break
      case '1': document.getElementById("state1").innerHTML = "ON"; break
      case '2': document.getElementById("state2").innerHTML = "OFF"; break
      case '3': document.getElementById("state2").innerHTML = "ON"; break
      case '4': document.getElementById("state3").innerHTML = "OFF"; break
      case '5': document.getElementById("state3").innerHTML = "ON"; break
    }
    }