Empfangen von SignalK-Daten mit dem ESP8266

Auf dieser Seite werden wir beschreiben, wie man mit einem ESP8266 Daten von einem SignalK-Server empfangen und dann auf einem angeschlossenen Display anzeigen kann. Für die Interpretation der SignalK-Botschaften müssen Strings dekodiert werden, was mehr CPU-Ressourcen als das Erstellen von Strings benötigt. Daher nutzen wir die Gelegenheit, einen ESP8266 in unser Projekt einzuführen, der einige Ressourcen mehr als ein AVR-basierter Arduino mitbringt. Er lässt sich aber immer noch über die Arduino IDE programmieren und viele der Bibliotheken sind kompatibel, so dass er gut in unser Projekt passt.

Verwendete Hardware und Software

  • PC mit Debian GNU Linux 10.0
  • Einen Raspberry Pi 3 mit Raspbian Buster Lite (02.12.2020) (Konfiguration ist hier beschrieben)
  • Arduino 1.8.13
  • Einen Wemo D1 miniPro V1.0.0 mit einem OLED 0.66 Shield
  • Das EPS8266-Package für Arduino Version 2.7.4
  • ArduinoJson Version 6.17.2
  • StreamUtils für Arduino Version 1.6.0
  • Adafruit GFX Library Version 1.10.5
  • Adafruit BusIO-Library Version 1.7.2
  • Adafruit SSD1306 Wemos Mini OLED Version 1.1.2

Daten vom SignalK-Server abbonieren

Das Abbonieren von Daten vom SignalK-Server erfolgt mit einem „Subscription Protocol„. Erst werden wir mit Kommandozeilen-Tools sicher stellen, dass wir dieses Protokoll richtig anwenden und Daten vom Server bekommen. Anschließend machen wir uns an die Implementierung in einem Arduino-Sketch.

Als erstes starten wir den SignalK-Server. Für die ersten Tests verwenden wir wieder eine Messung und starten den SignalK-Server in der entsprechenden Konfiguration:

pi@raspberrypi:~/signalk-server-node $ ./bin/nmea-from-file

Jetzt können wir uns mit „netcat“ mit dem SignalK-Server verbinden. In diesem Schritt verwenden wir eine TCP-Verbindung (UDP wäre aber auch möglich). Nachdem sich das Tool verbunden hat, empfängt es die „SignalK Hello Message“ vom Server und zeigt sie an:

ralph@debian:~$ nc raspberrypi 8375
{"name":"signalk-server","version":"1.37.6","self":"vessels.urn:mrn:signalk:uuid:1cb3f5ae-95c5-4850-98be-f5909ba34b4f","roles":["master","main"],"timestamp":"2021-02-11T19:41:11.555Z"}

In das Terminal eingegebene Zeichen werden jetzt an den SignalK-Server übertragen. Um Daten zu abbonieren, müssen wir mit einem „subscription string“ Pfade vom Server abbonieren. Als ersten simplen Test abbonieren wir alle Daten. Dafür geben wir den folgenden „subscription string“ in das Terminal-Fenster ein (ihr könnt es wieder von hier mit Strg+c kopieren und mit Strg+Shift+v ins Terminal einfügen):

{"context":"*","subscribe":[{"path":"*"}]}

Wenn ihr „Return“ drückt, wird die Zeichenkette abgeschickt und der SignalK-Server sendet uns alle Nachrichten. Als Ergebniss erhaltet ihr einen kontinuerlichen Strom von SignalK-Messages, der wir die folgende Ausgabe aussieht:

{"context":"vessels.urn:mrn:signalk:uuid:1cb3f5ae-95c5-4850-98be-f5909ba34b4f","updates":[{"$source":"defaults","timestamp":"2021-02-11T19:36:59.456Z","values":[{"path":"","value":{"uuid":"urn:mrn:signalk:uuid:1cb3f5ae-95c5-4850-98be-f5909ba34b4f"}}]}]}
{"context":"vessels.urn:mrn:signalk:uuid:1cb3f5ae-95c5-4850-98be-f5909ba34b4f","updates":[{"source":{"sentence":"VHW","talker":"II","type":"NMEA0183","label":"nmeaFromFile"},"$source":"nmeaFromFile.II","timestamp":"2021-02-11T19:44:12.819Z","values":[{"path":"navigation.speedThroughWater","value":3.2512897125489495}]}]}
{"context":"vessels.urn:mrn:signalk:uuid:1cb3f5ae-95c5-4850-98be-f5909ba34b4f","updates":[{"source":{"sentence":"VPW","talker":"II","type":"NMEA0183","label":"nmeaFromFile"},"$source":"nmeaFromFile.II","timestamp":"2021-02-11T19:44:12.819Z","values":[{"path":"performance.velocityMadeGood","value":0.8642668856142777}]}]}
{"context":"vessels.urn:mrn:signalk:uuid:1cb3f5ae-95c5-4850-98be-f5909ba34b4f","updates":[{"source":{"sentence":"VTG","talker":"II","type":"NMEA0183","label":"nmeaFromFile"},"$source":"nmeaFromFile.II","timestamp":"2021-02-11T19:44:12.918Z","values":[{"path":"navigation.courseOverGroundMagnetic","value":3.6976545541194707}]}]}
{"context":"vessels.urn:mrn:signalk:uuid:1cb3f5ae-95c5-4850-98be-f5909ba34b4f","updates":[{"source":{"sentence":"VTG","talker":"II","type":"NMEA0183","label":"nmeaFromFile"},"$source":"nmeaFromFile.II","timestamp":"2021-02-11T19:44:12.918Z","values":[{"path":"navigation.courseOverGroundTrue","value":3.6976545541194707}]}]}
{"context":"vessels.urn:mrn:signalk:uuid:1cb3f5ae-95c5-4850-98be-f5909ba34b4f","updates":[{"source":{"sentence":"VTG","talker":"II","type":"NMEA0183","label":"nmeaFromFile"},"$source":"nmeaFromFile.II","timestamp":"2021-02-11T19:44:12.918Z","values":[{"path":"navigation.speedOverGround","value":3.1381119060994607}]}]}
{"context":"vessels.urn:mrn:signalk:uuid:1cb3f5ae-95c5-4850-98be-f5909ba34b4f","updates":[{"source":{"sentence":"MWV","talker":"II","type":"NMEA0183","label":"nmeaFromFile"},"$source":"nmeaFromFile.II","timestamp":"2021-02-11T19:44:12.519Z","values":[{"path":"environment.wind.speedApparent","value":4.496245583493326}]}]}
.
.
.

Als nächstes wählen wir uns einen Satz Nachrichten aus, die wir abbonieren. Um das zu testen, beenden wir die Ausgabe von netcat mit Strg+c. Anschließend starten wir es erneut und geben einen „subscription string“ ein, der statt dem Wildcard (*) die gewünschten Pfade enthält:

ralph@debian:~$ nc raspberrypi 8375
{"name":"signalk-server","version":"1.37.6","self":"vessels.urn:mrn:signalk:uuid:1cb3f5ae-95c5-4850-98be-f5909ba34b4f","roles":["master","main"],"timestamp":"2021-02-11T19:51:31.156Z"}

Nachdem wir die SignalK Hello Message empfangen haben, geben wir den folgenden „subscription string“ ein:

{"context":"vessels.self","subscribe":[{"path":"navigation.speedThroughWater"},{"path":"environment.depth.belowTransducer"}]}

Jetzt den String noch mit „Return“ abschicken, dann empfangen wir die gewünschten Pfade:

{"context":"vessels.urn:mrn:signalk:uuid:1cb3f5ae-95c5-4850-98be-f5909ba34b4f","updates":[{"source":{"sentence":"VHW","talker":"II","type":"NMEA0183","label":"nmeaFromFile"},"$source":"nmeaFromFile.II","timestamp":"2021-02-11T19:54:32.542Z","values":[{"path":"navigation.speedThroughWater","value":0.7356557419216768}]}]}
{"context":"vessels.urn:mrn:signalk:uuid:1cb3f5ae-95c5-4850-98be-f5909ba34b4f","updates":[{"source":{"sentence":"DBT","talker":"II","type":"NMEA0183","label":"nmeaFromFile"},"$source":"nmeaFromFile.II","timestamp":"2021-02-11T19:54:32.743Z","values":[{"path":"environment.depth.belowTransducer","value":7.65}]}]}
{"context":"vessels.urn:mrn:signalk:uuid:1cb3f5ae-95c5-4850-98be-f5909ba34b4f","updates":[{"source":{"sentence":"VHW","talker":"II","type":"NMEA0183","label":"nmeaFromFile"},"$source":"nmeaFromFile.II","timestamp":"2021-02-11T19:54:32.944Z","values":[{"path":"navigation.speedThroughWater","value":0.7305112961739728}]}]}
{"context":"vessels.urn:mrn:signalk:uuid:1cb3f5ae-95c5-4850-98be-f5909ba34b4f","updates":[{"source":{"sentence":"DBT","talker":"II","type":"NMEA0183","label":"nmeaFromFile"},"$source":"nmeaFromFile.II","timestamp":"2021-02-11T19:54:33.242Z","values":[{"path":"environment.depth.belowTransducer","value":7.67}]}]}

Den ESP8266 der Arduino-IDE hinzufügen

Das „Board Support Package“ für den ESP8266 ist nicht in der Standard-Installation der Arduino-IDE enthalten. Wir müssen es daher nachinstallieren. Das originale Paket und die Dokumentation findet ihr auf dem ESP8266-Github. Hier sind die benötigten Schritte für die Installation noch mal zusammengefasst.

Als erstes öffnen wir „Datei->Voreinstellungen“, klicken auf das Icon hinter „Zusätzliche Boardverwalter-URLs“ und fügen in dem sich öffnenden Fenster die URL: „https://arduino.esp8266.com/stable/package_esp8266com_index.json“ in einer separaten Zeile hinzu (siehe Screenshot unten).

Screenshot der Voreinstellungen für Boardverwalter-URLs
Boardverwalter-URL für ESP8266 zur Arduino-IDE hinzufügen

Danach öffnen wir „Werkzeuge->Board: XXXX->Boardverwalter“, suchen dort das „ESP8266“-Packet und installieren es (siehe Screenshot unten). Jetzt wollten wir unter „Werkzeuge->Board: XXXX->ESP8266 2.7.4“ das WeMos D1 R1 (bzw. euer Board) auswählen können. Der erste Teil der Installation ist geschafft – wir können für das WeMos D1 kompilieren.

Screenshot des Boardverwalters mit dem esp8266-Paket
ESP8266-Packet mit dem Boardverwalter zur Arduino-IDE hinzufügen

Als nächstes müssen wir die benötigten Bibliotheken installieren. Die ersten sind „ArduinoJson“ und „StreamUtils“, die wir verwenden, um die SignalK-JSON-Botschaften zu dekodieren. Um sie zu installieren, klicken wir auf „Werkzeuge->Bibliotheksverwalter“, geben im Suchfeld des sich öffnenden Dialog „ArduinoJson“ ein, wählen die gewünschte Version aus und klicken auf „Installieren“ (siehe Screenshot unten). Mit der Bibliotheken „StreamUtils“ verfahren wir genauso.

Arduino Bibliotheksverwalter mit der ArduinoJson-Bibliothek
Hinzufügen der ArduinoJson-Bibliothek zur Arduino IDE mit dem Bibliotheksverwalter

Für das 0.66″ OLED Display brauchen wir die Bibliotheken „Adafruit BusIO“, „Adafruit GFX Library“ und die spezielle Version der „Adafruit SSD 1306 Wemos Mini OLED“. Achtung – nicht die „Adafruit SSD 1306“-Bibliothek installieren! Die Installation der aufgeführten Bibliotheken kann wie oben beschrieben über den Bibliotheksverwalter durchgeführt werden.

Kurze Übersicht über den Sketch

Den Sketch für den ESP8266 könnt ihr von unserem Github herunterladen. Am besten nehmt ihr den Tag v0.0.2, der auch in dieser Dokumentation verwendet wird. Ihr könnt ein Archiv herunterladen und entpacken oder mit dem Kommando git direkt ausschecken:

git clone -b v0.2.0 https://github.com/Vehicle-Hacks/Arduino-SignalK.git

Ihr findet den Sketch unter „Arduino-SignalK/Arduino-ESP8266-SignalK/Arduino-ESP8266-SignalK.ino“. Im oberen Teil werden die Bibliotheken inkludiert und notwendige globale Variablen angelegt. Darunter folgt der konfigurierbare Teil:

#define DATA_FROM_FILE
//#define DATA_FROM_ARDUINO

// For SignalK data
IPAddress signalkServer(192, 168, 4, 1);
const unsigned int signalkPort = 8375;      // SignalK Port which is the same for TCP and UDP

Mit den ersten beiden Zeilen lässt sich zwischen zwei „Abonnements“ umschalten, die implementiert sind: mit „#define DATA_FROM_FILE“ werden die Pfade abonniert, die wir im obigen Test mit netcat aus der NMEA-Datei verwendet haben. Mit „#define DATA_FROM_ARDUINO“ werden zwei Signale von dem Arduino UNO, den wir im letzten Schritt eingerichtet haben, verwendet. Die nächsten beiden Zeilen enthalten die IP-Adresse des Raspberry PI – in diesem Fall für die WLan-Schnittstelle, wie beim einrichten vergeben – und den Port, auf dem die TCP-Verbindung aufgebaut wird.

In der setup()-Funktion wird als erstes das Display initialisiert, damit wir während dem Verbindungsaufbau Status- oder Fehlermeldungen ausgeben können. Anschließend verbindet sich der ESP8266 mit dem WLan des Raspberry PI. Falls der Verbindungsaufbau erfolgreich war, wird anschließend eine Verbindung zum SignalK-Server aufgebaut. Wenn vom SignalK-Server erfolgreich eine „SignalK Hello Message“ empfangen wurde, wird der Name des Servers auf dem Display ausgegeben und der „subscription string“ gesendet. Anschließend ist die Initialisierung beendet.

In der loop()-Funktion prüfen wir, ob Daten vom SignalK-Server beim (TCP)-client angekommen sind. Wenn das der Fall ist, werden sie mit ArduinoJson dekodiert und daraus die Werte für die beiden abbonierten Pfade „environment.depth.belowTransducer“ und „navigation.speedThroughWater“ ausgelesen. Diese werden dann auf dem Display ausgegeben.

Test des Sketches

Als ersten Test verwenden wir die Signale aus der NMEA-Datei, die wir bereits am Anfang des Beitrags verwendet haben. Um den Sketch zu testen, starten wir als erstes den SignalK-Server mit der Konfiguration zum Abspielen der NMEA-Datei auf dem Raspberry PI:

pi@raspberrypi:~/signalk-server-node $ ./bin/nmea-from-file

Dann kompilieren wir den Sketch und laden ihn auf den ESP8266. Dabei müssen die #defines in der folgenden Konfiguration sein:

#define DATA_FROM_FILE
//#define DATA_FROM_ARDUINO

Nachdem der Sketch hochgeladen ist, verbindet er sich mit dem WLan-AP des Raspberry Pi und zeigt die eigene IP-Adresse sowie die des Gateway (des Raspberry Pi) an. Anschließend erscheint kurz der Name des SignalK-Servers und anschließend die Werte für Echolot-Tiefe und Geschwindigkeit aus dem NMEA-Replay.

Wenn dieser Schritt funktioniert hat, können wir mit Live-Daten vom Arduino Uno testen. Dafür beenden wir den SignalK-Server auf dem Raspberry PI mit strg+c und starten die Konfiguration mit dem UDP-Eingang vom Arduino Uno. Diese sollte noch gespeichert werden und beim Start der Standard-Konfiguration erscheinen:

pi@raspberrypi:~/signalk-server-node $ ./bin/signalk-server

Jetzt können wir den Arduino Uno starten und sollten im Dashboard des SignalK-Servers sehen, dass auf der Verbindung „Arduino UDP“ Botschaften empfangen werden. Wenn das der Fall ist, stellen wir die Konfiguration im ESP8266-Sketch auf das Arduino-Abonnement um:

//#define DATA_FROM_FILE
#define DATA_FROM_ARDUINO

Anschließend können wir den Sketch kompilieren und hochladen. Der ESP8266 verbindet sich dann wieder mit der gleichen Sequenz auf dem Display mit dem SignalK-Server, zeigt aber anschließend die Werte für Tankfüllstand und Temperatur an. Ihr könnt jetzt wieder mit den Sensoren am Arduino UNO spielen und sehen, wie sich die Werte auf dem Display am ESP8266 verändern.

Ergebnis und nächste Schritte

Mit diesem Schritt haben wir es geschafft, Daten vom Arduino Uno and den SignalK-Server auf dem Raspberry Piy zu senden, sie von dort per Abonemment an einen ESP8266 zu senden und am angeschlossenen Display anzuzeigen. Somit steht der erste Prototyp von einem kompletten System. Natürlich ist das Display noch winzig und die Sensoren eher beispielhaft als wirklich hilfreich – aber das sind Themen, die wir jetzt nach und nach lösen können.