• Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

ESP8266 и несколько клиентов

Melandr

Member
Доброй ночи!
Такой вопрос. Как часто можно обновлять значения на html-странице, хранящейся во флеше этой ESP. И допустим к ней подключено два клиента, как правильно обновлять значения переменных, допустим на одном клиенте изменили значение, оно должно также поменять на html-странице на втором клиенте. Попробовал использовать server sent event, работает, но идут непонятные фризы. Понимаю, что можно использовать websocket, но пока не хочу разбираться с сокетами. Еще я так понимаю, можно на странице использовать javascript для обновления отображаемых переменных, не подскажите, как правильно реализовать? Посмотреть примеры реализации.
 

Melandr

Member
Спасибо за наводку. Еще на форуме нашел такое описание алгоритма работы с html-страницей пост 18
И у меня возникает вопрос, для получения обновленных данных при выводе на страницу js запускается по таймеру на клиенте. А если у меня допустим таких клиентов 2 или 3. И на каждом будет крутиться скрипт обновления данных на странице. Не логичней было бы по таймеру отдавать значения переменных ESP всем подключенным клиентам. Правда не могу понять, как это должно реализовываться.
 

EvgeniyS

Member
Еще на форуме нашел такое описание алгоритма работы с html-страницей пост 18
Там человек объясняет реализацию AJAX-запросов. Сама технология неплохая, но если ее использовать для отслеживания изменений состояний датчиков и т.п. на веб морде в реальном времени, то это жуткий костыль. Дело в том, что ajax работает по принципу: клиент делает запрос - сервер отвечает, при этом сервер сам не может ничего отправить по своей инициативе, поэтому приходится на клиенте с интервалом времени постоянно делать запрос на сервер о состоянии датчиков. Каждый раз когда клиент шлет запрос сервер открывает соединение, шлет ответ, закрывает соединение, естественно каждому клиенту индивидуально. Из своей практики могу сказать, что если сделать на клиенте интервал запросов 1 раз в секунду, то 3-4 подключенных клиента уже заставляют есп-шку "тупить". Поэтому не советую повторять мой путь поиска подходяших технологий, а сразу перейти к вебсокетам ;)
 

Melandr

Member
Это как прям "Все дороги ведут в Рим". Я тоже с server sent event провозился и результат тоже не удовлетворил. Для того, чтобы была возможность делать запросы с клиента, приходится разрывать соединение и переподключение требует времени, передает 2 секунды нормально, а потом 5 секунд завтык. Где то просто видел проект, в котором на вэб-странице отображается время uptime и как мне показалось, фризы отсутствовали.
 

EvgeniyS

Member
Ивенты я тоже пробовал, они у меня работали (насколько я помню), но не все браузеры их поддерживали и я от них отказался.
 

EvgeniyS

Member
Где то просто видел проект, в котором на вэб-странице отображается время uptime и как мне показалось, фризы отсутствовали.
Uptime можно реализовать так: при подключении клиента сервер однократно передает значение uptime, а дальше клиент скриптом сам прибавляет время и ничего не будет тормозить.
 

Melandr

Member
Вопрос не только в uptime, мысль была передавать несколько переменных, в том числе и uptime раз 0,5-1 сек. Больше двух клиентов в принципе не планирую. А у Вас эвенты работали без фризов? Опять же есть две реализации вэб-сервера на ESP - синхронный и асинхронный. Какой использовать предпочтительней?
 

EvgeniyS

Member
Вопрос не только в uptime, мысль была передавать несколько переменных, в том числе и uptime раз 0,5-1 сек. Больше двух клиентов в принципе не планирую. А у Вас эвенты работали без фризов?
Точно не помню, насчёт фризов, вроде не было, но это давно было, больше 2 лет назад.
 

Melandr

Member
Посмотрел несколько примеров кода реализации на синхронном вэб-сервере, передача данных от ESP - клиентам. Используется AJAX. Но инициатором получения данных является клиент, на вэб-странице используется js с периодом опроса, задаваемым функцией setInterval(function().
Вопрос как можно используя js передавать по таймеру, реализуемому на ESP? В принципе пробовал использовать таймер js 500 мс, достаточно резво обновляются данные на странице.
Но корректней все таки будет, мне кажется, чтобы задавал период обновления сервер.
 

pvvx

Активный участник сообщества
Но корректней все таки будет, мне кажется, чтобы задавал период обновления сервер.
В web/http такого не бывает.
Реализация такого частично возможна в WebSocket.
Только в BLE и прочих отходящих от технологии BT (Zigbee и т.д.) сервер задает "период обновлений" - как самый простой пример включение notify атрибутов у сервиса, и вплоть до интервалов соединения и маяков.
 

EvgeniyS

Member
В принципе пробовал использовать таймер js 500 мс, достаточно резво обновляются данные на странице.
А на вебсокетах данные обновляются практически мгновенно, и не грузят есп-шку бесполезными запросами. AJAX не для такого извращения был придуман :D
 

Melandr

Member
На вебсокетах есть как мне кажется только один недостаток, если необходимо получить доступ к данным извне, то необходимо делать проброс портов на роутере.
EvgeniyS, а нетрудно будет Вам посмотреть в Ваших исходниках реализацию на SSE, хотелось бы глянуть, как Вы реализовали. Так как все реализации, которые мне попались в инете не сильно обрадовали.
 

Melandr

Member
А еще такой вопрос, по отображению данных программы на веб-странице. Смотрю реализации, так страница передается клиенту строкой со вставкой переменных в строку. Как по мне не особо комильфо. На асинхронном сервере используется функция processor(), которая подставляет на место плейсхолдера определенную строку. Возможно правильней будет формировать json и передавать по запросу с клиента и далее его парсить на странице.
 

EvgeniyS

Member
На вебсокетах есть как мне кажется только один недостаток, если необходимо получить доступ к данным извне, то необходимо делать проброс портов на роутере.
EvgeniyS, а нетрудно будет Вам посмотреть в Ваших исходниках реализацию на SSE, хотелось бы глянуть, как Вы реализовали. Так как все реализации, которые мне попались в инете не сильно обрадовали.
У меня не осталось исходников, т.к. давно было, помню что руководствовался документацией по библиотеке.
А еще такой вопрос, по отображению данных программы на веб-странице. Смотрю реализации, так страница передается клиенту строкой со вставкой переменных в строку. Как по мне не особо комильфо. На асинхронном сервере используется функция processor(), которая подставляет на место плейсхолдера определенную строку. Возможно правильней будет формировать json и передавать по запросу с клиента и далее его парсить на странице.
Что касается данных на веб страницы, я пишу веб приложение на фремворке Svelte (можно также Vue), загружаю в SPIFFS, а для обмена данными websocket+json. В результате веб приложение это просто статический javascript файл (упакованный и кэшируемый) а динамический обмен данными осуществляется по вебсокету в максимально лаконичной форме без всяких кусков html.
 

EvgeniyS

Member
C++:
#include <ESPAsyncWebServer.h>
//#include <ArduinoJson.h>

IPAddress ipAP{192, 168, 4, 1};
AsyncWebServer server(80);
AsyncWebSocket ws1("/ws");

void readJsonData(const char *data, AsyncWebSocketClient * client){
  Serial.printf("from ws data: %s client: %u\n", data, client->id());
  client->printf(data); // отпавляем клиенту то что получили (эхо сервер) для примера
  // здесь парсер json
}

// Приемник ws сообщений (упрощенный на 1 фрейм)
void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) {
  if(type == WS_EVT_CONNECT){
      Serial.printf("client id: %u connected\n", client->id());
    } else if(type == WS_EVT_DATA){
    AwsFrameInfo * info = (AwsFrameInfo*)arg;
    data[info->len] = '\0';     
    readJsonData((char*)data,client);
  }
}

// Обработчик несуществующей страницы
void notFound(AsyncWebServerRequest *request) {
  request->send(404, "text/plain", "Not found");
  }
// Инициализация сервера
void serverInit(){
      ws1.onEvent(onWsEvent);
      server.addHandler(&ws1);
      server.serveStatic("/", SPIFFS, "/").setCacheControl("max-age=31536000"); // В корень SPIFFS надо загрузить веб приложение
      server.onNotFound(notFound);
      server.begin();
  }

// Инициализация wifi сети
void wifiInit(){
    WiFi.mode(WIFI_AP);
    WiFi.softAPConfig(ipAP, ipAP, IPAddress(255, 255, 255, 0));
    WiFi.softAP("ssid", "pass");
}

void setup() {
  Serial.begin(115200);
  Serial.println();
  SPIFFS.begin();
  serverInit();
}

void loop() {}
Вот упрощенный пример сервера на вебсокетах
 
Сверху Снизу