ESP8266 Server-Sent Events (SSE) – przykład kodu Arduino i JavaScript, ESPAsyncWebServer

Pytanie

ESP8266 SSE javascript przykład kodu w arduino

Odpowiedź Sztucznej Inteligencji


Bezpośrednia odpowiedź na pytanie

  • Najprostszy, stabilny sposób wysyłania danych z ESP8266 do przeglądarki w czasie rzeczywistym to Server-Sent Events (SSE).
  • Poniżej znajdziesz kompletny szkic Arduino (ESP8266) oparty na bibliotece ESPAsyncWebServer oraz odpowiadający mu plik HTML/JS, który pokazuje odbiór i wizualizację danych.
  • Kod działa w każdej współczesnej przeglądarce (Chrome, Edge, Firefox, Safari) i nie wymaga odświeżania strony.

Szczegółowa analiza problemu

1. Architektura i dlaczego SSE

  1. ESP8266 występuje jako serwer HTTP z dwoma endpointami:
     • / – strona z kodem JS;
     • /events – strumień SSE.
  2. Po załadowaniu strony przeglądarka otwiera długotrwałe połączenie (EventSource).
  3. Serwer cyklicznie wysyła pakiety tekstowe zgodne ze specyfikacją SSE:
    event: <nazwa>
    data: <payload>
    id: <opcjonalny_ID>
    \n
  4. Mechanizm jest jednokierunkowy, ale wystarczający do live-podglądu sensorów lub logów systemowych.
  5. W porównaniu z WebSocket:
     • mniejsze zużycie RAM, brak handshake, automatyczne reconnecty;
     • ograniczenie do komunikacji serwer→klient.

2. Przygotowanie środowiska

  1. Arduino IDE ≥ 2.1 lub PlatformIO.
  2. Menadżer płytek: dodaj URL http://arduino.esp8266.com/stable/package_esp8266com_index.json.
  3. Biblioteki (Menedżer bibliotek):
     • ESPAsyncWebServer by me-no-dev
     • ESPAsyncTCP (zależność)
     • (ew.) ArduinoJson, jeśli planujesz JSON-y.

3. Kompletny szkic ESP8266 (asynchroniczny)

#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
const char* ssid     = "Twoja_SSID";
const char* password = "Twoje_HASLO";
AsyncWebServer  server(80);
AsyncEventSource events("/events");      // endpoint SSE
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE html><html lang="pl">
<head><meta charset="utf-8"><title>ESP8266 SSE</title>
<style>
 body{font-family:Arial;text-align:center;margin-top:40px;background:#f0f0f0}
 .card{display:inline-block;background:#fff;padding:30px 50px;border-radius:12px;
       box-shadow:0 4px 12px rgba(0,0,0,.1)}
 #temp,#hum{font-size:3rem;color:#007BFF}
</style></head>
<body>
 <h1>Dane z ESP8266 (SSE)</h1>
 <div class="card">
   <p>Temperatura: <span id="temp">--.-</span> °C</p>
   <p>Wilgotność:  <span id="hum">--.-</span> %</p>
   <p>Status: <span id="status" style="color:#888">Łączenie…</span></p>
 </div>
<script>
 if (!!window.EventSource) {
   const src = new EventSource('/events');
   src.onopen =      _=>document.getElementById('status').textContent   = 'Połączono';
   src.onerror =     _=>document.getElementById('status').textContent   = 'Rozłączono';
   src.addEventListener('sensor', e=>{
       const d = JSON.parse(e.data);
       document.getElementById('temp').textContent = d.t.toFixed(1);
       document.getElementById('hum').textContent  = d.h.toFixed(1);
   });
 } else { alert("Przeglądarka nie wspiera SSE"); }
</script></body></html>
)rawliteral";
float temp=24.0, hum=55.0;               // przykładowe dane
unsigned long lastSent=0;
void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid,password);
  while (WiFi.status()!=WL_CONNECTED) { delay(500); Serial.print('.'); }
  Serial.println("\nIP: "+WiFi.localIP().toString());
  // strony
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *r){ r->send_P(200,"text/html",index_html); });
  // callback po podłączeniu nowego klienta SSE
  events.onConnect([](AsyncEventSourceClient *c){
     Serial.printf("Nowy klient SSE: %s\n", c->remoteIP().toString().c_str());
     c->send("connected", NULL, millis(), 1000);
  });
  server.addHandler(&events);
  server.begin();
}
void loop() {
  // symulacja zmiany wartości co 2 s
  if (millis()-lastSent>2000){
     temp += random(-3,4)/10.0;
     hum  += random(-5,6)/10.0;
     temp = constrain(temp,0,50); hum = constrain(hum,0,100);
     // pakiet JSON ⇒ mniejszy narzut po stronie klienta
     String payload = "{\"t\":"+String(temp,1)+",\"h\":"+String(hum,1)+"}";
     events.send(payload.c_str(),"sensor",millis());
     lastSent = millis();
  }
}

Kluczowe elementy:
AsyncEventSource events("/events"); ‑ deklaruje endpoint SSE.
events.send(payload, "sensor", id); ‑ wysyła zdarzenie o nazwie sensor.
index_html osadzony w PROGMEM, aby oszczędzić RAM.

4. Minimalny wariant bez ESPAsyncWebServer (blokujący)

Jeżeli zależy Ci na absolutnym minimum lub nie możesz dodać bibliotek, użyj klasycznego WiFiServer. (Patrz sekcja „Kod minimum” w załączniku). W praktyce przy >1 kliencie lub dłuższym stanie online asynchroniczna biblioteka jest bezpieczniejsza (mniejsze ryzyko watchdog-reset).

5. Teoria formatowania strumienia

Każde zdarzenie SSE składa się z co najmniej jednej linii data: i musi się kończyć pustą linią (\n\n). Dopuszczalne atrybuty:
id: – identyfikator umożliwiający wznowienie po utracie połączenia;
event: – nazwana kategoria (klient rejestruje addEventListener("nazwazdarzenia", …));
retry: – suges­tia czasu ponownego łączenia w ms.

Aktualne informacje i trendy

  • Biblioteka ESPAsyncWebServer jest dziś de-facto standardem dla SSE i WebSocket na ESP8266/ESP32. W 2023/24 rozwijana jest przez społeczność po forku z repo me-no-dev (brak oficjalnych update’ów upstream).
  • Dla nowych projektów warto rozważyć ESP32-C3 lub S3 – posiadają więcej RAM i natywny multiple HTTP/SSE/WS poprzez ESP-IDF.
  • Coraz powszechniej łączy się SSE z HTTP/2 (serwer LiteSpeed, Node.js) – ESP8266 pozostaje przy HTTP/1.1.

Wspierające wyjaśnienia i detale

  • SSE korzysta z mechanizmu „chunked transfer” – Content-Length: unknown lub brak nagłówka; ważne, aby NIE zamykać gniazda, dopóki chcemy streamować.
  • Przeglądarki automatycznie wznawiają połączenie po ~3-5 s od przerwania; identyfikator id: pozwala na kontynuację bez utraty elementów.
  • ESP8266 ma ~40 kB RAM-u dla użytkownika; jeden klient SSE zużywa ok. 2–4 kB bufora TCP. Przy kilku klientach należy ograniczyć rozmiar payloadu.

Aspekty etyczne i prawne

  • Ujawnianie serwera w sieci publicznej wymaga uwierzytelniania i szyfrowania. ESP8266 nie obsługuje SSE przez TLS w trybie Async (tylko HTTP); dla ruchu przez Internet stosuj reverse-proxy (np. NGINX) z TLS.
  • Dane sensorów mogą ujawniać wzorce bytności domowników → zadbaj o autoryzację.

Praktyczne wskazówki

  1. Debuguj w Chrome → DevTools → zakładka Network → śledź /events.
  2. Jeśli w kodzie JS nic nie przychodzi, sprawdź, czy pakiety są zakończone \n\n oraz Content-Type: text/event-stream.
  3. Ustaw WiFi.setSleepMode(WIFI_NONE_SLEEP);, gdy obserwujesz zrywanie po 3 min.
  4. Przy większej ilości danych pakuj je do JSON i rozbijaj na kilka eventów zamiast wysyłki co ~100 ms jednego dużego blobu.

Ewentualne zastrzeżenia lub uwagi dodatkowe

  • Internet Explorer i starsze Android WebView (< 4.4) nie obsługują SSE.
  • ESPAsyncWebServer nie otrzymuje oficjalnych poprawek od me-no-dev – polecam forki ESPAsyncWebServer-esphome lub joshmossman/ESPAsyncWebServer.
  • Połączenia keep-alive mogą zostać ubite przez niektóre routery SOHO po 5-10 min; dla aplikacji krytycznych implementuj heartbeat (wysyłaj puste zdarzenie co ~15 s).

Sugestie dalszych badań

  • Porównanie SSE vs MQTT-over-WebSocket na ESP-klient.
  • Integracja z frameworkiem PWA + ServiceWorker → cache offline oraz push-notifications.
  • Migracja na ESP32 z esp-idf, gdzie dostępne są natywne FreeRTOS tasks i HTTP/2.

Krótkie podsumowanie

Server-Sent Events to najlżejszy sposób „live-streamu” danych z ESP8266 do przeglądarki. Wystarczy:

  1. biblioteka ESPAsyncWebServer,
  2. endpoint /events z nagłówkiem text/event-stream,
  3. cykliczne events.send() w pętli,
  4. EventSource po stronie JS.
    Poprawnie skonstruowany strumień (data + \n\n) gwarantuje natychmiastowe, automatycznie odnawiane dostarczanie danych przy minimalnym zużyciu zasobów układu.

Oceń odpowiedź:

Zadaj dodatkowe pytanie Sztucznej Inteligencji

Czekaj (1min)...
Zastrzeżenie: Odpowiedzi udzielone przez sztuczną inteligencję (model językowy) mogą być niedokładne i wprowadzające w błąd. Elektroda nie ponosi odpowiedzialności za dokładność, rzetelność ani kompletność prezentowanych informacji. Wszystkie odpowiedzi powinny być zweryfikowane przez użytkownika.