Open Hive GSM and WiFi Firmware: Telemetrie an Hiveeyes Backend anpassen

Wir sollten zusehen, den Telemetrieteil der hybriden AVR/ESP8266 Open Hive GSM and WiFi Firmware von @clemens fit für die Datenübertragung zum Hiveeyes Backend zu machen. @Thias würde das gerne schlüsselfertig nutzen, nicht nur die rudimentäre node-wifi-mqtt Firmware.

Mein Eindruck ist, dass node-gprs-http wesentlich fortgeschrittener ist und offenbar auch für den ESP8266 taugt. Auch auf einem Adafruit Feather HUZZAH?

Jawoll! node-wifi-mqtt war eine Vorstufe davon, das sollten wir entsprechend kennzeichnen. Sorry für die Verwirrung. Lege also einfach mit node-gprs-http los, auch wenn der Name noch nicht ganz passt: Es ist neulich von @clemens so universell gestaltet worden, dass es sowohl auf einem AVR mit GPRSbee modul als auch auf einem ESP8266 mit WiFi betrieben werden kann. Dafür kann es jedoch noch nicht direkt universell HTTP mit unserem Hiveeyes Backend sprechen - ganz zu schweigen von MQTT - bzw. es fehlt noch ein Stück Dokumentation dazu, wie man damit genau per CSV zum Backend funken kann.

Du siehst also: An dieser Stelle haben wir noch ein wenig ein “Moving Target” :-), vielen Dank für Deine Impulse!

P.S.: Vielleicht machst Du also doch erstmal mit node-wifi-mqtt weiter, solange wir an dieser Stelle noch eine Baustelle haben? Ich tausche mich diesbzgl. mit @clemens aus, um zu sehen, dass wir Dir hier evtl. bald eine bessere Firmware auf Basis des node-gprs-http Codes liefern können.

1 Like

Mein aktuelles Dilemma besteht also darin, node-gprs-http nutzen zu wollen, welches die Daten an einen PHP-“Adapter” senden möchte/kann, mein Kotori-Backend aber nur http POST bzw MQTT versteht. Was würde ich denn benötigen um, auch die http GET Variante per PHP auf meinem RasPi fahren zu können?

Zum Verständnis: der PHP-Empfänger nimmt Daten per http GET an und leitet local auf http POST zu Kotori weiter???

Ich mag nur ungern weiter auf die, mit Verlaub, etwas verwaiste node-wifi-mqtt Firmware bauen, wo doch node-gprs-http eine saubere Möglichkeit bietet, die FW an einen individuellen Aufbau zu konfigurieren.

Ich werde wohl warten müssen bis entweder node-gprs-http per MQTT oder http POST senden kann oder der PHP-Adapter bei mir funktioniert.

Richtig, momentan stecken wir leider in diesem Dilemma. @mois und @clemens haben vor ein paar Jahren mit einem PHP Backend angefangen und die Firmwares aus deren Federn sind derzeit leider noch auf diese Kommunikationswege ausgelegt und noch nicht universell genug, entweder per MQTT oder über eine besser strukturierte Variante der HTTP Kommunikation zu funken.

Das Problem ist auch gar nicht unbedingt nur die Anomalie zwischen HTTP GET und POST, sondern auch, dass die Daten von node-gprs-http bisher im CSV Format gesendet werden. Kotori wurde zwar aufgrund dieser Tatsache bereits die Datenerfassung per CSV Format beigebracht, bisher wurde das aber leider im Zusammenspiel mit der Firmware noch nicht erschlossen. Mir sind diese Anomalien auch schon länger ein Dorn im Auge, wir konnten leider innerhalb unserer Gruppe bisher noch nicht die Zeit finden, die an dieser Stelle noch losen Enden konvergieren zu lassen, damit alles zusammenpasst.

Du siehst: Die verschiedenen Entwicklungen kommen aus unterschiedlichen Federn und historischen Zeiten und haben es noch nicht geschafft, unter eine Haube gebracht zu werden. Vielleicht können wir Deine Impulse endlich dazu nutzen, den Sack zuzumachen, wir sind schließlich schon auf der letzten Meile und es fehlt eigentlich nur noch, ein paar Zapfen einiger unserer Legobausteine rund zu schleifen. Vielen Dank nochmals an dieser Stelle für die klare Formulierung des aktuellen Dilemmas :-)!

Ich sehe gerade folgende Möglichkeiten:

Variante 1

Vielleicht kannst Du Dich an der Vorgehensweise von @mois orientieren und die Daten per PHP durchleiten?

Siehe auch:
Bienenwaage: Prototyp im Testlauf (Bauanleitung) – mois-Blog (der Ur PHP Code) sowie von neulich:
Daten per HTTP und PHP ans Backend auf swarm.hiveeyes.org übertragen

Obacht:

  • Der PHP Code von @mois ist nicht identisch zu dem von @clemens, obwohl damals die ersten Versionen gemeinsam von beiden entwickelt wurden.
  • Die node-gprs-http Firmware ist jedoch von @clemens. Vielleicht steuerst Du noch Deinen PHP Code bei, @clemens?
  • Von @mois haben wir noch keine Firmware im Repository, vielleicht willst Du auch die Gelegenheit ergreifen, diese beizusteuern?

Variante 2

Wir erschließen das rudimentäre Zusammenspiel zwischen node-gprs-http Firmware mit der CSV Datenerfassungsschnittstelle nun selbst. Dazu müsste man aber mindestens a) von HTTP GET auf HTTP POST umstellen, b) die URL entsprechend anpassen und c) einmalig das Header Announcement manuell zum Backend schicken. Das ist alles an und für sich kein Drama, vielleicht finde ich diesbzgl. kurz Zeit, dann könnte ich das vorbereiten. Allerdings ohne Gewähr, weil ich keine Hardware hier habe. Du müsstest dazu also ein guter Sparringspartner sein, vielleicht können wir uns kurz telefonisch abstimmen?

Variante 3

Erstmal mit node-wifi-mqtt weitermachen… Da ich aber Deine Ambitionen erkenne, mit dem zu arbeiten, was dem reifsten Softwarestand entspricht, ist das vermutlich nicht das, was Du Dir wünschst.

Da wäre ich mit dabei, weil mein Ding eben gerade wieder gar nicht funktioniert, ohne dass ich überhaupt etwas geändert habe. Auf die SD am Arduino schreibt er einwandfrei, also liegts nicht an der Firmware.

Nachtrag 17.3.2017: Habe jetzt für alle meine Files ein Repository auf GitHub eingerichtet:

Hier findet sich der aktuelle Quelltext für meine Yun Firmware:

Die node-wifi-mqtt-Firmware sollte eigentlich nie veröffentlicht werden. ;-) zumindest von meiner Seite. Das war nur mal schnell zusammengestöpselt um die IoT-Plattform von Adafruit (IO-Adafruit) per MQTT mal kurz zu testen. ;-) Auch die dort verwendete MQTT-lib ist eigentlich nicht unser Favorit.

Der node-gprs-http läuft auch auf eine Feather, pins muss man dann ggf. noch anpassen.

Meine Frage / Idee ist jetzt, ob du tatsächlich über die PHP-Schiene gehen musst oder nicht besser den Kotori gleich direkt per Feather ansprichst, für CSV ist der ja schon vorbereitet, wir müssen nur von GET auf POST umstellen.

Lass uns mal schauen, was man da machen könnte, als Vorlage vielleicht das:

HTTPClient http;
http.begin("http://jsonplaceholder.typicode.com/posts");
http.addHeader("Content-Type", "application/x-www-form-urlencoded");
http.POST("title=foo&body=bar&userId=1");
http.writeToStream(&Serial);
http.end();

Habe ich von hier https://github.com/esp8266/Arduino/issues/1390#issuecomment-170343346

Bei unserem sketch arduino/node-gprs-http.ino at master · hiveeyes/arduino · GitHub geht der relevante code ab Zeile 1046 los: HTTPClient http; und http.begin(uploadUrl); können bleiben, uploadUrl müsste man noch weiter oben im code anpassen.

Statt int httpCode = http.GET(); nehmen wir mal

http.addHeader("Content-Type", "application/x-www-form-urlencoded");
http.POST(dataset);

So in etwa könnte es gehen.

Das wäre spitze! Ich würde gerne, wie wir schon besprochen hatten, vor allem die Routinen zum Zusammenbauen des Payloads verbessern. Derzeit stecken diese in den Zeilen 952-999 und sind exklusiv dafür da, die Daten zu CSV zu serialiseren. Dies müsste man dann redundant für x-www-form-urlencoded kodieren, was den Quelltext noch umfangreicher und starrer machen würde. Der Wartungsaufwand wäre noch höher und gleichzeitig die Flexibilität geringer.

An dieser Stelle würde ich die Daten bevorzugt im JSON Format übermitteln, wir können aber auch zusehen, das x-www-form-urlencoded Format zu unterstützen. Dafür würde ich gerne unsere TerkinData C++ Bibliothek verwenden, diese kann beliebige gesammelte Sensordaten universell wahlweise nach JSON oder CSV serialisieren.

Grob könnte das dann folgendermaßen aussehen (der Code wurde ein wenig vereinfacht, um die grundsätzliche Idee besser erkennen zu können):

JSON

// Daten sammeln
Measurement *measurement = new Measurement();
measurement->data["dht.0.temp"]  = 42.42f;
measurement->data["dht.0.hum"]   = 84.84f;
measurement->data["rssi"]        = 72;
measurement->data["voltage"]     = 3.843f;

// Daten zu JSON serialisieren
std::string payload = datamgr->json_data(*measurement);

// Daten übermitteln
HTTPClient http;
http.begin("http://swarm.hiveeyes.org/api-notls/hiveeyes/testdrive/area-42/node-1/data");
http.addHeader("Content-Type", "application/json");
http.POST(payload.c_str());
http.writeToStream(&Serial);
http.end();

CSV

Wenn man CSV benutzen will, wäre das ganz ähnlich:

// Daten zu CSV serialisieren
std::string payload = datamgr->csv_data(*measurement);

// Daten übermitteln
HTTPClient http;
http.begin("http://swarm.hiveeyes.org/api-notls/hiveeyes/testdrive/area-42/node-1/data");
http.addHeader("Content-Type", "text/csv");
http.POST(payload.c_str());
http.writeToStream(&Serial);
http.end();

… nachdem man einmalig (z.B. beim Systemstart) die CSV Feldnamen bekanntgegeben hat:

datamgr->field_names  = new DataHeader({"temperature", "humidity", "rssi", "voltage"});
// ...
std::string data_header = datamgr->csv_header();
// ...
http.POST(data_header.c_str());

Das ist bei der Übertragung per CSV essentiell, da die Werte ja immer in der gleichen Reihenfolge übermittelt werden müssen, wie es die Liste der Feldnamen vorgibt. Auch wenn mal ein Sensor ausfällt (und die Ausleseroutine resilient demgegenüber ist), sollte das die korrekte Serialisierung nicht beeinträchtigen:

-- Test single reading (incomplete: "weight" and "temperature-inside" missing)
header: ## weight,temperature-outside,humidity-outside,temperature-inside,voltage
data:   ,42.420,84.840,,3.843

Die Bibliothek kann das bereits, siehe TerkinData C++ Examples.

Nun kann man auch in das Format “x-www-form-urlencoded” serialisieren:

x-www-form-urlencoded

// Daten zu x-www-form-urlencoded serialisieren
std::string payload = datamgr->urlencode_data(*measurement);

// Daten übermitteln
HTTPClient http;
http.begin("http://swarm.hiveeyes.org/api-notls/hiveeyes/testdrive/area-42/node-1/data");
http.addHeader("Content-Type", "application/x-www-form-urlencoded");
http.POST(payload.c_str());
http.writeToStream(&Serial);
http.end();

Beispiel:

-- Test single reading (incomplete)
data:   temperature-outside=42%2E42&humidity-outside=84%2E84&voltage=3%2E84

Siehe auch TerkinData C++ urlencode example.

HTTP POST Requests mit HTTPie

Wenn man sich HTTPie installiert, kann man sich den Anfragen in verschiedenen Übertragungsformaten schon etwas im Trockendock nähern. Es gibt verschiedene Installationsvarianten für HTTPie.


Vorbereitung

Der HTTP Endpunkt zur Übertragung von Telemetriedaten:

export TELEMETRY_URL=http://swarm.hiveeyes.org/api-notls/hiveeyes/testdrive/area-42/node-1/data

Die URL besteht aus folgenden Komponenten:

  • Basis URL: http://swarm.hiveeyes.org/api-notls
    Wir verwenden hier den unverschlüsselten Endpunkt, weil das GPRSbee Modul noch kein SSL beherrscht [stimmt das noch?] und man den kleinen AVR MCUs die Kryptographie auch nicht unbedingt zumuten muss [auf dem ESP8266 könnte SSL besser erschlossen sein!?], der Stromverbrauch würde vermutlich ebenfalls steigen.

  • Kanaladresse: realm / network / gateway / node.
    Der realm ist bei uns immer “hiveeyes”, während die Parameter “network” (Imker), “gateway” (Standort) und “node” (Beute) frei wählbare Identifizierer sind, siehe Hiveeyes Data acquisition » Addressing.
    Gegebenenfalls kann man den network identifier “testdrive” beispielsweise in “testdrive-chimovto” abändern, um auf einen anderen Datenkanal für Testzwecke zu wechseln. Sobald man schließlich produktiv Daten übertragen will, holt man sich am besten eine exklusive UUID per Online UUID Generator.

  • URL Suffix: Für die Datenakquise immer “data”.

Dieses kurze Stück Python Code wäre geeignet, eine entsprechende URL zu erzeugen und soll die Zuordnung der Komponenten nochmals verdeutlichen:

# HTTP telemetry url
telemetry_url = u'{api_url}/{realm}/{network}/{gateway}/{node}/data'.format(
    api_url = 'http://swarm.hiveeyes.org/api-notls',
    realm   = 'hiveeyes',
    network = 'c0b2ba47-cad8-4cb3-94b5-abf72d971b73',     # Imker
    gateway = 'area-42',                                  # Standort
    node    = 'node-1'                                    # Bienenstock
)

Messdaten übertragen

Sensorwerte können in den Formaten JSON, x-www-form-urlencoded und CSV übertragen werden.

JSON

# HTTP POST request as application/json
http POST $TELEMETRY_URL temperature:=42.42 humidity:=84.84

x-www-form-urlencoded

# HTTP POST request as application/x-www-form-urlencoded
http --form POST $TELEMETRY_URL temperature:=42.42 humidity:=84.84

Siehe auch Hiveeyes Data acquisition » HTTP.


CSV

Beim CSV Format muss man einmalig die Liste der Datenfelder ankündigen, bevor Daten übertragen werden können.

# HTTP POST request as text/csv

# 1. Announce field names
echo '## temperature, humidity' | http POST $TELEMETRY_URL Content-Type:text/csv

# 2. Transmit sensor readings
echo '42.42,84.84' | http POST $TELEMETRY_URL Content-Type:text/csv

Siehe auch Hiveeyes Data acquisition » HTTP CSV.

+1

die CSV-Variante ist ja schnell machbar, muss ausser die URL und GET vs. POST noch was geändert werden?

Mir wäre der direkte Weg zu Kotori lieber als der Umweg über PHP. Falls ihr Code zum Testen habt, kann ich den am Wochenende gerne in meinen Testaufbau übersetzen.

Die aktualisierten telemetry improvements im “openhive-stl” branch kompilieren zwar (gegen AVR sowie Espressif), dass alles auf Anhieb funktioniert, ist jedoch unwahrscheinlich.

Es geht hier um die Generalisierung der Messdatensammlung und -serialisierung (CSV vs. x-www-form-urlencoded vs. JSON) sowie die Modularisierung des Telemetrietransports (AVR/GPRSbee vs. ESP8266, HTTP vs. MQTT). Dafür werden die Bibliotheken TerkinData C++ sowie TerkinTelemetry C++ verwendet. Vorerst bleibt der Fokus noch auf CSV-over-HTTP, da das @clemens’ Hausformat ist.

Wer will, kann das gerne ausprobieren, verwendet aber bitte nicht zu viel Zeit beim Debugging - das ist bislang wirklich nur eine kompilierfähige Skizze.


@clemens: Neben der Umstellung der Datensammel-, Serialisierungs- und Telemetrieroutinen wurden dabei folgende Dinge realisiert:

Telemetrie/-Adressinformationen homogenisiert

Dabei wurde auch Dein Wunsch eines Authentication Tokens berücksichtigt: Zeile 141 ff.:

  // HTTP API Base URL
  #define TELEMETRY_API_URL      "http://swarm.hiveeyes.org/api-notls/"

  // Authentication token
  #define TELEMETRY_TOKEN        "88673770c6d64097e3"

  // Data acquisition channel address
  #define TELEMETRY_REALM        "hiveeyes"
  #define TELEMETRY_USER         "testdrive"
  #define TELEMETRY_SITE         "area-42"
  #define TELEMETRY_NODE         "node-1"

GPRSbee Konfigurationsparameter homogenisiert

Siehe Zeile 157 ff.:

  // Specify your APN here, specific for your network operator
  // https://en.wikipedia.org/wiki/Access_Point_Name
  #define GPRSBEE_AP_NAME     "internet.eplus.de"
  #define GPRSBEE_AP_AUTH     false
  #define GPRSBEE_AP_USER     "testuser"
  #define GPRSBEE_AP_PASS     "12345"

  // pwrkeyPin, vbatPin, statusPin, we use -1 for vbatPin because we have no switched battery here
  #define GPRSBEE_PIN_VCC       12
  #define GPRSBEE_PIN_VBAT      -1
  #define GPRSBEE_PIN_STATUS    13

Routine wifi_ensure_connection() eingeführt

Sie stellt die WiFi connectivity beim ESP8266 sicher: Zeile 1311 ff.

Neue Präprozessor Makrodefinitionen

Da die Information, welche Telemetrievariante (GPRSbee vs. ESP8266 WiFi) verwendet werden soll, auch schon zur Kompilierzeit für die TerkinTelemetry C++ Bibliothek wichtig ist, wurden zwei generische DEFINE Variablen namens TELEMETRY_DEVICE_GPRSBEE sowie TELEMETRY_DEVICE_ESP8266 eingeführt. Sie reflektieren 1:1 die von Dir eingeführten Variablen isGSM bzw. isWifi und versuchen nur, das “naming things” ein wenig zu verbessern, so dass sie auch für die generische TerkinTelemetry Bibliothek besser passen. isGSM bzw. isWifi hätten für diesen Zweck ohnehin nicht verwendet werden können, weil sie im Scope für die Bibliotheken keine Gültigkeit besitzen:

How can I pass preprocessor variables to the compilation of the library? Standard #define in a sketch file wouldn’t work, since their live will be limited to my sketch object file(s), not the library’s.
– via: Arduino configure script for conditional compilation of libraries

Derzeit werden sie über ein Konfigurations-Makefile Configuration.mk gesetzt, wenn man auf der Kommandozeile arbeitet. Hier klappt das perfekt.

P.S.: Für die Anleitung Build for ESP8266 benötigt man die neueste Version von makeEspArduino, diese honoriert die Makefile Variable “BUILD_EXTRA_FLAGS”. Im genannten Branch steht diese Version bereits als git submodule zur Verfügung.


Wie man solche -D Makrodefinitionen in der Arduino IDE setzt, müssen wir noch herausfinden, ich habe dazu ein paar Dinge gesammelt:

  • Evtl. ginge es über die “compiler.cpp.extra_flags=” Direktive in einer “platform.local.txt”, beispielsweise per:

echo “compiler.cpp.extra_flags=-DTELEMETRY_DEVICE_GPRSBEE=true” > ./hardware/arduino/avr/platform.local.txt


- `arduino-builder` sieht das Kommandozeilenargument "`-prefs`" vor, siehe [Passing flags to preprocessor on arduino-builder](https://forum.arduino.cc/index.php?topic=426408.0).

Hier gibt es weitere Dinge in den GitHub issues, die das Thema streifen:

- [Ability to specify additional compiler flags](https://github.com/arduino/Arduino/issues/421)
- [Library configuration file management](https://github.com/arduino/Arduino/pull/1808)
- [Include global UserSettings.h file](https://github.com/arduino/arduino-builder/issues/15)
- [Adding support for sketch-specific build properties](https://github.com/arduino/arduino-builder/pull/29)

Wünschenswert wäre, wenn die _Arduino IDE_ ab Werk eine entsprechende Option bieten würde, ähnlich wie es beim [Arduino for Visual Studio zu sehen ist](http://www.visualmicro.com/page/User-Guide.aspx?doc=Project-Defines.html) oder in Eclipse via "Project » Properties", "Settings » AVR Compiler » Miscellaneous » Other Flags" funktionieren würde.

Vielleicht fühlt sich jemand dazu berufen, diesem Thema weiter nachzugehen? Das würde uns sehr helfen!
1 Like