Livedaten vom NanoESP mit ServerSentEvents

In vorangegangen Projekten mit dem NanoESP, z.B. dem Lichtsensor Projekt vom Adventskalender, konnten im Browser neue Messwerte  nur durch neues Laden der kompletten, veränderten Webseite dargestellt werden. Dies ist logischerweise sehr ineffizient. Auf der Suche nach besseren Möglichkeiten bin ich schließlich auf eine HTML5-Funktion ServerSentEvents gestoßen. Kurz gesagt handelt es sich hierbei um eine sehr praktische Funktion um Daten von einem Webserver zu empfangen, auch wenn die Webseite schon komplett geladen ist  Und das Beste ist, wenn die Verbindung einmal steh, kann der Server jederzeit und kontinuierlich neue Daten senden.

Im HTML Quelltext wird dies folgendermaßen realisiert:

var source = new EventSource(„document.URL“);

Mit dieser kleinen Zeile wird eine neue Verbindung zu einem TCP-Server aufgebaut. Hier würde normalerweise eine URL stehen, hinter der sich z.B. ein PHP-Script verbirgt, wie in dem Beispiel der folgenden Seite:

http://www.w3schools.com/html/html5_serversentevents.asp

In unserem Fall steht hier document.URL (also die aktuellen Adresse die auch in der Adressleiste steht). Da Sie eine Verbindung zum NanoESP öffnen, ist die URL gleich der IP des Boards. Es wird also automatisch eine weitere Verbindung zum Board aufgebaut und Sie müssen die IP nicht individuell anpassen.

Beim Verbindungsaufbau wird nun folgender Request vom Browser gesendet (aus dem Seriellen Monitor kopiert):

GET / HTTP/1.1
Host: 192.168.178.41
Connection: keep-alive
Accept: text/event-stream
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36
Referer: http://192.168.178.41/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8,de;q=0.6

Das spannende an diesem Request ist die Stelle: Accept: text/event-stream. Anhand dieser wird auch im Programm eine event-stream Anfrage erkannt und die ID gesondert gespeichert. Der Server, also hier unser NanoESP, muss nun passend auf die Anfrage reagieren und zwar in etwa  so:

HTTP/1.1 200 OK \n
Content-Type: text/event-stream\n
Connection: close\n
Access-Control-Allow-Origin: *\n
Cache-Control: no-cache
\r\n\r\n

Ich habe den Antwort-Header bewusst mit den New-Line Symbolen geschrieben, denn die New-Line Zeichen, besonder die am Ende, haben mir bei den Versuchen einige Schwierigkeiten bereitet. Nun ist die Verbindung zustande gekommen und bleibt auch bestehen. Sollte es eine Trennung geben, baut der Browser die Verbindung selbstständig wieder auf (!). Der Server (aka. NanoESP) kann nun jederzeit Daten senden, z.B. in dieser Form:

data: Connection Established\n\n

Sowohl das data: am Anfang als auch die beiden New Lines am Ende sind sehr wichtig und werden interpretiert. Alles dazwischen kann man frei anpassen. Diese Daten könnten nun einfach eingelesen werden, wie in dem oben verlinkten Beispiel vorgestellt. Dies ist auch praktisch wenn Sie einfache Statusnachrichten empfangen wollen. Es gebt aber noch besser!

Neben diesen einfachen Nachrichten können nämlich noch sogenannt events mitgeschickt werden, mit denen man Nachrichten-Typen unterscheiden kann. Ein Beispiel wie die Sache aussehen kann:

Der Server sendet mit

event: msg\n
data: Wichtige Nachricht! \n\n

eine einfache Nachrichr oder aber mit

event: ao\n
data: analogRead(A0) \n\n

den aktuellen Wert von A0 an den Browser. Im Browser kann nun mit EventListener auf die eine oder andere Art der Nachricht reagiert werden

source.addEventListener(‚a0‘, function(e) {
document.getElementById(„a0“).value = e.data;
}, false);

source.addEventListener(‚msg‘, function(e) {
document.getElementById(„msg“).value += e.data + „\n“;
}, false);

In diesem Fall wird also eine Nachricht die das Event-Label a0 trägt in der Webseite als Value des Elements mit der ID a0 eingetragen, was in dem Fall ein Slider ist. Nachrichten mit dem Tag msg werden dagegen dem Textarea-Feld mit der ID msg hinzugefügt. Was noch alles möglicht ist mit data und event (z.B. sogar JSON-Codierung) lässt sich am Besten auf der  folgenden Webseite nachlesen:

https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events

Das Beste an der ganzen Geschichte ist nun aber die Geschwindigkeit, mit der Daten vom NanoESP zum Browser gesendet werden können. Ich habe die Datenrate nicht genau gemessen aber ich schätze das mindestens 4 Messwerte pro Sekunde übertragen werden können, und das mit dem ineffektiven Code den ich hingeschmiert habe!

Übrigens: Windows Internet Explorer funktioniert nicht, auch nicht der neue Edge Browser. Wohl aber andere gängige PC und Mobile Browser.

Das Programm:

Das Programm lädt, ähnlich dem vorherigen Projekt, die Webseite mit einer JS-Datei von meiner Internetseite nach (eine ungewöhnliche Methode die aber Ressourcen auf dem NanoESP spart). Es muss also unbedingt eine Internetverbindung bestehen. Sie können das Script betrachten unter

http://www.temp.fkainka.de/EventStream1.js

Laden Sie das Programm runter und öffnen Sie es in einer Arduino IDE ab Version 1.6.5. Passen Sie Ihre WLAN-Daten an und laden Sie es hoch. Dann öffnen Sie die IP des Boards im Browser. Die IP wird beim Start des Boards im Seriellen Monitor angezeigt. Die LED D3 leuchtet wenn eine Verbindung zum WLAN hergestellt wurde.

Beim Öffnen der Seite gibt es eine Meldung im Text-Area-Feld, dass die SerialEvent-Verbindung besteht. Im Browser wird nun permanent der aktuelle Wert von A0 empfangen und mit der Position des Sliders dargestellt. Wenn D9 auf High geschaltet wird, gibt es zusätzlich eine Statusmeldung im Textarea-Feld.