Сбрасывается цвет кнопки

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

Проблема скорей комсетическая. И так, есть вэб панель управления кнопками. Когда кнопки в состоянии OFF, то должны быть красного цвета. Переключаем в стояние ON, кнопка как и положено меняет цвет на зеленной. Вроде всё нормально. Но стоит обновить страницу, зеленный цвет сбрасывается, хотя статус так и остался ON.

// Импортируем библиотеки
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include "SPIFFS.h"
#include <AsyncElegantOTA.h>

// Задаем сетевые настройки
const char* ssid = "uname";
const char* password = "password";
AsyncWebServer server(80);             // Создаем сервер через 80 порт
AsyncWebSocket ws("/ws");              // Создаем объект WebSocket

// Запускаем SPIFFS
void initSPIFFS() {
  if (!SPIFFS.begin(true)) {
    Serial.println("Произошла ошибка при монтировании SPIFFS");
  }
  Serial.println("SPIFFS примонтировано успешно");
}

// Запускаем WiFi
void initWiFi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.print("Подключаюсь к WiFi ...");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
      delay(500);
  }
  Serial.println(WiFi.localIP());
}

bool ledState1 = 0;
bool ledState2 = 0;
bool ledState3 = 0;
bool ledState4 = 0;
bool ledState5 = 0;

// Уведомляем клиентов о текущем состоянии светодиода
void notifyClients1() {ws.textAll(String(ledState1));}
void notifyClients2() {ws.textAll(String(ledState2 + 2));}
void notifyClients3() {ws.textAll(String(ledState3 + 4));}
void notifyClients4() {ws.textAll(String(ledState4 + 6));}
void notifyClients5() {ws.textAll(String(ledState5 + 8));}

/* функция обратного вызова, которая запускается всякий раз, когда мы получаем новые
  данные от клиентов по протоколу 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();}
    if (strcmp((char*)data, "toggle3") == 0) {ledState3 = !ledState3;notifyClients3();}
    if (strcmp((char*)data, "toggle4") == 0) {ledState4 = !ledState4;notifyClients4();}
    if (strcmp((char*)data, "toggle5") == 0) {ledState5 = !ledState5;notifyClients5();}
  }
}

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

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";}}
  if (var == "STATE3") {if (ledState3) {return "ON";} else {return "OFF";}}
  if (var == "STATE4") {if (ledState4) {return "ON";} else {return "OFF";}}
  if (var == "STATE5") {if (ledState5) {return "ON";} else {return "OFF";}}
}

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

void setup() {
  // Последовательный порт для отладки
  Serial.begin(115200);

  pinMode(32, OUTPUT); digitalWrite(32, LOW);
  pinMode(33, OUTPUT); digitalWrite(33, LOW);
  pinMode(25, OUTPUT); digitalWrite(25, LOW);
  pinMode(26, OUTPUT); digitalWrite(26, LOW);
  pinMode(27, OUTPUT); digitalWrite(27, LOW);


  initSPIFFS();
  initWiFi();
  initWebSocket();

  // Начальная страница
  server.on("/", HTTP_GET, [](AsyncWebServerRequest * request) {
    request->send(SPIFFS, "/index.html", "text/html", false, processor);
  });
  server.serveStatic("/", SPIFFS, "/");

  // Запускаем ElegantOTA
  AsyncElegantOTA.begin(&server);

  // Запускаем сервер
  server.begin();
}

void loop() {
  AsyncElegantOTA.loop();
  ws.cleanupClients();

  digitalWrite(32, ledState1);
  digitalWrite(33, ledState2);
  digitalWrite(25, ledState3);
  digitalWrite(26, ledState4);
  digitalWrite(27, ledState5);
}

JavaScript

  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) { 
    switch(event.data) {
      case '0': document.getElementById("state1").innerHTML = "OFF"; document.getElementById('button1').style.backgroundColor = "#c90411"; break;
      case '1': document.getElementById("state1").innerHTML = "ON &nbsp;"; document.getElementById('button1').style.backgroundColor = "#04b50a"; break;
      case '2': document.getElementById("state2").innerHTML = "OFF"; document.getElementById('button2').style.backgroundColor = "#c90411"; break;
      case '3': document.getElementById("state2").innerHTML = "ON &nbsp;"; document.getElementById('button2').style.backgroundColor = "#04b50a"; break;
      case '4': document.getElementById("state3").innerHTML = "OFF"; document.getElementById('button3').style.backgroundColor = "#c90411"; break;
      case '5': document.getElementById("state3").innerHTML = "ON &nbsp;"; document.getElementById('button3').style.backgroundColor = "#04b50a"; break;
      case '6': document.getElementById("state4").innerHTML = "OFF"; document.getElementById('button4').style.backgroundColor = "#c90411"; break;
      case '7': document.getElementById("state4").innerHTML = "ON &nbsp;"; document.getElementById('button4').style.backgroundColor = "#04b50a"; break;
      case '8': document.getElementById("state5").innerHTML = "OFF"; document.getElementById('button5').style.backgroundColor = "#c90411"; break;
      case '9': document.getElementById("state5").innerHTML = "ON &nbsp;"; document.getElementById('button5').style.backgroundColor = "#04b50a"; break;
      }
    }
  
  function onLoad(event) {initWebSocket(); initButton();}

  function initButton() {
    document.getElementById('button1').addEventListener('click', toggle1);
	document.getElementById('button2').addEventListener('click', toggle2);
	document.getElementById('button3').addEventListener('click', toggle3);
	document.getElementById('button4').addEventListener('click', toggle4);
	document.getElementById('button5').addEventListener('click', toggle5);
  }
  
  function toggle1(){websocket.send('toggle1');}
  function toggle2(){websocket.send('toggle2');}
  function toggle3(){websocket.send('toggle3');}
  function toggle4(){websocket.send('toggle4');}
  function toggle5(){websocket.send('toggle5');}

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:,">
  <link rel="stylesheet" type="text/css" href="style.css">
  <title>ESP Web Server</title>
</head>

<body>
  <div class="topnav">
    <h1>ESP WebSocket Server</h1>
  </div>
  <div class="content">
    <div class="card">
      <h2>Out - GPIO 32</h2>
      <p class="state">Status: <span id="state1">%STATE1%</span></p>
      <p><button id="button1" class="button">Button 1</button></p>
      <h2>Out - GPIO 33</h2>
      <p class="state">Status: <span id="state2">%STATE2%</span></p>
      <p><button id="button2" class="button">Button 2</button></p>
      <h2>Out - GPIO 25</h2>
      <p class="state">Status: <span id="state3">%STATE3%</span></p>
      <p><button id="button3" class="button">Button 3</button></p>
      <h2>Out - GPIO 26</h2>
      <p class="state">Status: <span id="state4">%STATE4%</span></p>
      <p><button id="button4" class="button">Button 4</button></p>
      <h2>Out - GPIO 27</h2>
      <p class="state">Status: <span id="state5">%STATE5%</span></p>
      <p><button id="button5" class="button">Button 5</button></p>
      <br>
      <br>
      <div><a href="/update" class="ota">Update</a></div>
      <br>
    </div>
  </div>
  <script src="/script.js"></script>
</body>

</html>

Как сделать чтобы обновление страницы не изменяло цвет кнопок? Цвет кнопок должен быть привязан исключительно к состоянию ON или OFF.

sadman41
Offline
Зарегистрирован: 19.10.2016

На onload сразу запросить состояние с вебсокета, а дальше связанные функции сами справятся. Ну, или в куках хранить, но это так себе способ.

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

Адольф, ты чего опять тупишь то на ровном месте? Ты html откуда «отдаёшь»? С esp? Менять там же бэкграунд  можешь? Намёк понят? ))))

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

BOOM пишет:

Адольф, ты чего опять тупишь то на ровном месте? Ты html откуда «отдаёшь»? С esp? Менять там же бэкграунд  можешь? Намёк понят? ))))

Бум, тупить мое нормальное состояние. Бекграунд там указан. Ты его в html не видишь, потому как он указан в  файле стилей style.css параметр background-color: #c90411; Ну вот представь теперь. Допустим кнопка в положении ON (зеленненькая), обновил страницу она бац, стала красной. Потому как раз перечитали из беграунда.

sadman41 пишет:
На onload сразу запросить состояние с вебсокета, а дальше связанные функции сами справятся. Ну, или в куках хранить, но это так себе способ.

Куки это вообще не вариант. Функция onMessage event принимает состояние кнопки с сервера ESP и корректно отправляет отправляет клиенту. Надо будет подумать как при обновлении страницы организовать повторную отправку.
BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

Adolf_Balalaykin пишет:

Бум, тупить мое нормальное состояние. Бекграунд там указан. Ты его в html не видишь, потому как он указан в  файле стилей style.css параметр background-color: #c90411; Ну вот представь теперь. Допустим кнопка в положении ON (зеленненькая), обновил страницу она бац, стала красной. Потому как раз перечитали из беграунда.

Я имел в виду динамически менять сам html-код, чтобы отдавать сразу "правильный", вот так:

      <h2>Out - GPIO 32</h2>
      <p class="state">Status: <span id="state1">%STATE1%</span></p>
      <p><button id="button1" class="button_off">Button 1</button></p>
      <h2>Out - GPIO 33</h2>
      <p class="state">Status: <span id="state2">%STATE2%</span></p>
      <p><button id="button2" class="button_on">Button 2</button></p>
      <h2>Out - GPIO 25</h2>
      <p class="state">Status: <span id="state3">%STATE3%</span></p>
      <p><button id="button3" class="button_off">Button 3</button></p>
      <h2>Out - GPIO 26</h2>
      <p class="state">Status: <span id="state4">%STATE4%</span></p>
      <p><button id="button4" class="button_off">Button 4</button></p>
      <h2>Out - GPIO 27</h2>
      <p class="state">Status: <span id="state5">%STATE5%</span></p>
      <p><button id="button5" class="button_off">Button 5</button></p>
      <br>

В css добавить стиль button_on с бэкграундом включенной кнопки (стиль button лучше переименовать в button_off, чтобы не путаться). Ну и кури str_replace () какой-нибудь.

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

Ну чего там? Или ещё продолжение празднования?)))))

ЗЫ: #4 не исключает всего ранее проделанного, а дополняет.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

BOOM пишет:

Ну чего там? Или ещё продолжение празднования?)))))

ЗЫ: #4 не исключает всего ранее проделанного, а дополняет.

BOOM ты в NodeJS и REACT шаришь? На  хостинг устанавливал?

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

ua6em пишет:

BOOM ты в NodeJS и REACT шаришь? На  хостинг устанавливал?

Не, когда я веб говнокодил еще их и в проекте не было )))

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

BOOM пишет:

Ну чего там? Или ещё продолжение празднования?)))))

ЗЫ: #4 не исключает всего ранее проделанного, а дополняет.

Оглядывается... Что день победы уже кончился? Мы уже всех победили???
  Бум, твоя идея понятна. Рассотрим в максимально упощенном виде, на примере одного канала.
JavaScript
function onMessage(event) {  // функция просслушивания состояние кнопок
  switch (event.data) 
   {
	case '0': document.getElementById('button1_off').style.backgroundColor = "#c90411"; break;
	case '1': document.getElementById('button1_on').style.backgroundColor = "#04b50a"; break;
   }
 }	

В html создаем два id, каждый для своего класса.

html

<p><button id="button1_off" class="button_off">Button 1</button></p>
<p><button id="button1_on" class="button_on">Button 1</button></p>

Style.css

.button_off {
  background-color: #c90411;     /* красим кнопку в красный цвет */
 }
 .button_on {
  background-color: ##04b50a;    /* красим кнопку в зеленный цвет */
 }
Вроде всё гладно и красиво. С одним большим НО! При обновлении страницы кнопка не окрасится в нужный цвет. Дело в том что функция onMessage вызывается только при переключении кнопки. Необходимо создать отдельную функцию, которая вызывалась бы при каждом обновлении страницы.

ЗЫ. Похоже  при обновлении страницы таки вызывает  "function onOpen(event)". До дома доберусь попробую запустить из этой функции.

xDriver
xDriver аватар
Offline
Зарегистрирован: 14.08.2015

Adolf_Balalaykin пишет:
Оглядывается... Что день победы уже кончился? Мы уже всех победили???

на данном форуме острить по этому поводу крайне НЕ рекомендуется.

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

Адольф! Ещё раз - не тупи. Зачем ты лезешь в сложности с Явой? Я же написал выше - изменяй отдаваемый с esp html, ты же его с esp отдаёшь «клиенту» или откуда? Меняй сразу сам отдаваемый клиенту html.

Не стесняйся спрашивать.

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

BOOM пишет:

Адольф! Ещё раз - не тупи. Зачем ты лезешь в сложности с Явой? Я же написал выше - изменяй отдаваемый с esp html, ты же его с esp отдаёшь «клиенту» или откуда? Меняй сразу сам отдаваемый клиенту html.

Не стесняйся спрашивать.

Да с esp конечно. Весь код лежит в трех файлах, index.html, style.css и script.js. Бум, по твоему рецепту нифига не выходит.

style.css

.button_off {
  background-color: #c90411;     /* красим кнопку в красный цвет */
 }
 .button_on {
  background-color: ##04b50a;    /* красим кнопку в зеленный цвет */
 }

index.html

<p><button id="button1_off" class="button_off">Button 1</button></p>
<?php $resulat = str_replace('button_off', 'button_on', $class=; ?>

PHP не знаю, поэтому не уверен в корректности второй строки. Кроме того не понятно как переменная $resulat при обновлении страницы вызывается?

 

sadman41
Offline
Зарегистрирован: 19.10.2016

ESP научилась работать интерпретатором PHP и таскать его из HTML? Чего только не узнаешь на форуме Arduino.

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

Adolf_Balalaykin пишет:

Да с esp конечно. Весь код лежит в трех файлах, index.html, style.css и script.js. Бум, по твоему рецепту нифига не выходит.

index.html

<p><button id="button1_off" class="button_off">Button 1</button></p>
<?php $resulat = str_replace('button_off', 'button_on', $class=; ?>

Это, конечно же, не верный подход. Во первых - на сколько я знаю php не портировали никогда на ESP. Во вторых - какой смысл использовать интерпретатор там, где он не нужен? Ну и в третьих - "шаблон" (html) должен иметь четкие однозначные "метки".

Я только еще заказал esp себе и точно утверждать не могу в правильности кода, но надеюсь хотя бы основную мысль донесу. Собственно так как я не знаю как у тебя организовано "хранение всех трех файлов" и как организована "отдача их клиенту" (кстати, напиши и то и другое ... ), поэтому я просто беру пример из интернета.

index.h

const char MAIN_page[] PROGMEM = R"=====(
<HTML>
    <HEAD>
            <TITLE>My first web page</TITLE>
    </HEAD>
<BODY>
    <CENTER>
        <p><button id="button1" class="button1_status">Button 1</button></p>
        <p><button id="button2" class="button2_status">Button 2</button></p>
    </CENTER>    
</BODY>
</HTML>
)=====";

Дальше, считываем содержимое прогмем (так ты делаешь?) и меняем в шаблоне значения "меток" на конкретные значения. Ну, например, так:

String s = FPSTR(MAIN_page);

// получаем значения для кнопок 1 и 2 (on or off) в переменные button1_state и button2_state
// меняем эти значения:

s.replace ("button1_status", button1_status); // если включена 1 кнопка, то поменяется "button1_status" на "button_on"
s.replace ("button2_status", button2_status); // если выключена 2 кнопка, то поменяется "button2_status" на "button_off"

// Отдаем s клиенту.

Не уверен в правильности, но суть такая.

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

SAB
Offline
Зарегистрирован: 27.12.2016
       setInterval(function (){
            // Call a function repetatively with 1 Second interval
            getData();//функции которые надо обновлять в JS
            getData2();
        }, 1000); //1000mSeconds update rate

Я так делал, и всё автоматом обновляется (ajax)

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

Ajax это же не только ява-скрипт на стороне клиента, но еще и ответная часть на "сервере". Поэтому в итоге получается сложнее (на мой взгляд).

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

Добрался таки  до форума к выходным.)

BOOM пишет:

Собственно так как я не знаю как у тебя организовано "хранение всех трех файлов" и как организована "отдача их клиенту" (кстати, напиши и то и другое ... )

Прогемов нет. Все три файла index.html, style.css и style.js хранятся в FS SPIFFS флеш памяти. Аля виртуальная SD карта )).

Отдается клиенту так

#include "SPIFFS.h"

  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
            { request->send(SPIFFS, "/index.html", "text/html", false, processor); });

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

SAB пишет:

       setInterval(function (){
            // Call a function repetatively with 1 Second interval
            getData();//функции которые надо обновлять в JS
            getData2();
        }, 1000); //1000mSeconds update rate

Я так делал, и всё автоматом обновляется (ajax)

 IMHO Крутить каждую секунду функцию с целью проверки на предмет изменилось ли там состояние кнопки или нет, не очень рационально. 

 В моем случае изменениям должны подвергнутся 86 и 87 строки (смотри 1й пост)
 
// Функция processor() заменяет все заполнители между символами %---%, в HTML-тексте фактическими значениями.
String processor(const String &var)
{
  if (var == "STATE1")
  {
    if (ledState1)
    {
      return "ON";
	  // если включена 1 кнопка, то поменяется "button1_status" на "button_on"
	  // что сюда прописать для изменения "button1_status" ?
    }
    else
    {
      return "OFF";
    }
  }

Правильно?

 

sadman41
Offline
Зарегистрирован: 19.10.2016

Бл..

или запроси в онлоаде через ajax с ESP https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onl...  
или формируй html программно на стороне ESP. 

Других вариантов у тебя нет.

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

Решено! В функции onEvent после строки 70 добавил условный оператор    

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 клиент #%u подключен с %s\n", client->id(), client->remoteIP().toString().c_str());
    // при обновлении страницы (переподключение) обновляем статус кнопки.
    if (ledState1)
    {
      ws.textAll(String(1));
    }
break;
    }
}
Т.е при обновлении страницы, заново проверяетеся статус. Если включен, посылаем клиенту оповещение о состоянии кнопки.
Всем кто принимал участи в обсуждении огромное спасибо за помощь!
BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

А если войти с нового устройства?

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

BOOM пишет:

А если войти с нового устройства?

Не не, куки непричем. Неделя полет нормальный. Только с утюга лишь не заходил.