ESP, IoT Таймеры на millis и обратный остчет до включения

humaxoid

✩✩✩✩✩✩✩
10 Янв 2022
2
0
Russia
Доброго времени суток! Пока что эксперементальный веб.сервер, основан на websocket. Задача такова: На страничку выводится две конопки вкл/выкл. Этими кнопками можно подавать 0 или 1 на соответсвующие выходы. Так же есть два простых таймера на millis, которые должны управлять этими кнопками. А теперь самое главное! Хочу что бы выводилось время которое осталось до включения таймера. Так сказать обратный отсчет.

index.html:
<!DOCTYPE html>
<html lang="ru">

<head>
    <meta charset="UTF-8" , name="viewport" content="width=device-width, initial-scale=1.0">
</head>

<body>
    <br>
    &nbsp; GPIO25 &nbsp; <button id="button1"><span id="state1">%STATE1%</span></button>&nbsp; &nbsp; до включения осталось...
    <span id="dateTime1"></span>
    <br>
    <br>
    &nbsp; GPIO26 &nbsp; <button id="button2"><span id="state2">%STATE2%</span></button>&nbsp; &nbsp; до включения осталось...
    <span id="dateTime2"></span>
    <script src="script.js"></script>
</body>

</html>
script.js:
var gateway = [ICODE]ws://${window.location.hostname}/ws[/ICODE];
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) {
  // let data = event.data.split(',');
  // document.getElementById("dateTime1").innerHTML = data[0];
  // document.getElementById("dateTime2").innerHTML = data[1];

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

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'); }
main.ccp:
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <SPIFFS.h>

// Задаем сетевые настройки
const char *ssid = "name";
const char *password = "pass";
AsyncWebServer server(80); // Запускаем асинхронный веб-сервер на 80 порту
AsyncWebSocket ws("/ws");  // Создаём объект, который будет обрабатывать websocket-ы:

// Объявляем GPIO.
#define ledPin1 25 // Таймер1
#define ledPin2 26 // Таймер2

// Переменные хранения времени (unsigned long)
uint32_t timer1, timer2;

// На всех выводах GPIO по умолчанию устанавливаем 0.
bool ledState1 = false, ledState2 = false;

// Задаем время для таймеров
int32_t t1 = 5 / 1000L;     // Продолжительность работы для 1го таймера 5 сек.
int32_t t11= 15 / 1000L;  // Переодичность включения для 1го таймера 15 сек.
int32_t t2 = 3 / 1000L;     // Продолжительность работы для 2го таймера 3 сек.
int32_t t22 = 10 / 1000L;  // Переодичность включения для 2го таймера 15 сек.

// Создаем 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, "toggle1") == 0) // Слушаем сообщения от js, если toggle1 = 0, то выполняем код ниже.
    {
      ledState1 = !ledState1;        // Установим значение переменной ledState = 1, но если кнопка не нажата тогда ledState = 0
      ws.textAll(String(ledState1)); // Уведомляем клиентов о переключении кнопки.
    }
    if (strcmp((char *)data, "toggle2") == 0)
    {
      ledState2 = !ledState2;
      ws.textAll(String(ledState2 + 2));
    }
  }
}

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());
    break;
  case WS_EVT_DISCONNECT: // когда клиент вышел из системы
    Serial.printf("WebSocket клиент #%u отключен\n", client->id());
    break;
  case WS_EVT_DATA: // обработка полученных данных
    handleWebSocketMessage(arg, data, len);
    break;
  case WS_EVT_PONG:  // в ответ на запрос ping
  case WS_EVT_ERROR: // при получении ошибки от клиента
    break;
  }
}

// Функция processor() заменяет все заполнители между символами %---%, в HTML-тексте фактическими значениями.
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";
    }
  }
  return String();
}

// ----------------------------------------------------------------
// Инициализация
// ----------------------------------------------------------------

void initSPIFFS()
{
  if (!SPIFFS.begin())
  {
    Serial.println("При монтировании SPIFFS произошла ошибка");
    while (1)
      ;
  }
}

void initWiFi()
{
  WiFi.begin(ssid, password);
  Serial.print("Подключаемся к WiFi...");
  while (WiFi.status() != WL_CONNECTED)
  {
    Serial.print(".");
    delay(1000);
  }
  Serial.println();
  Serial.printf("Подключился к сети %s\n", ssid);
  Serial.print("ESP32 IP адрес: ");
  Serial.println(WiFi.localIP());
}

void initWebServer()
{
  server.begin();
}

void initWebSocket()
{
  ws.onEvent(onEvent);
  server.addHandler(&ws);
}

void setup()
{
  // Зададим скорость последовательного порта.
  Serial.begin(115200);
  delay(1000);

  initSPIFFS();    // Инициализация SPIFFS
  initWiFi();      // Инициализация WiFi
  initWebServer(); // Инициализация Web сервера
  initWebSocket(); // Инициализация Websocket

  // Объявим GPIO выходы (по умолчанию LOW)
  pinMode(ledPin1, OUTPUT);
  digitalWrite(ledPin1, LOW);
  pinMode(ledPin2, OUTPUT);
  digitalWrite(ledPin2, LOW);

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

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

void loop()
{

  digitalWrite(ledPin1, ledState1);
  digitalWrite(ledPin2, ledState2);

  // ############################ Таймер №1 ####################################

  if (millis() / 1000L - timer1 >= (ledState1 ? t1 : t11)) // 5 сек-продолжительность, 15 сек-периодичность
  {
    timer1 = millis() / 1000L;        // количество секунд с момента старта
    ledState1 = !ledState1;
    digitalWrite(ledPin1, ledState1); // Подаем на GPIO25, высокий/низкий уровень
    // Serial.println(timer3);        // Выводим количество секунд с момента включения
  }

  // Вывод обратного отсчета времени
  int8_t timeleft1 = (t11 - (millis() / 1000L - timer1)); // Создаем переменную timeleft1, обратного остчета времени (где 15, это период)
  if (!ledState1)                                        // если таймер выключен (период ожидания), то..
  {
    Serial.println(timeleft1);     // то выводим обратный остчет до включения таймера
    ws.textAll(String(timeleft1)); // отправляем данные в JavaScript
  }

  // ############################ Таймер №2 ####################################
  if (millis() / 1000L - timer2 >= (ledState2 ? t2 : t22)) // 5 сек-продолжительность, 10 сек-периодичность
  {
    timer2 = millis() / 1000L;        // количество секунд с момента старта
    ledState2 = !ledState2;
    digitalWrite(ledPin2, ledState2); // Подаем на GPIO26, высокий/низкий уровень
  }

  // Вывод обратного отсчета времени
  int8_t timeleft2 = (t22 - (millis() / 1000L - timer2));
  if (!ledState2)
  {
    ws.textAll(String(timeleft2)); // отправляем данные в JavaScript
  }
}
Никак не получается отправить две строки с данными в функцию onMessage джава скипта.
Строки 182 и 197, каша получается. Джава должен их как то различать.
 
Изменено:

Lumenjer

★★★✩✩✩✩
10 Дек 2020
220
112
@humaxoid, так вы просто данные передаете, как JS должен понимать, какой таймер к какому относится?
Снабдите их какими нибудь айди, ArduinoJSON вам в помощь
 

humaxoid

✩✩✩✩✩✩✩
10 Янв 2022
2
0
Russia
@humaxoid, так вы просто данные передаете, как JS должен понимать, какой таймер к какому относится?
Снабдите их какими нибудь айди, ArduinoJSON вам в помощь
Я про это и говорю. Да и без Json можно обойтись. В js я закоментировал строки 22,23,24 не спроста. В прошиве строки 182 и 197 нужно как то идентифицировать.