Albergue para EasyHive

@Ron, Daria und Lukas von EasyHive bauen den neuen Datenlogger auf Basis eines Cortex-M0, machen Telemetrie per UDP über LTE Cat M1 ¹ und das Backend auf Basis von express – eine gute Gelegenheit sie mit dem Neuzugang Terkin for JavaScript zu unterstützen und andersherum ebenfalls danke für die Gelegenheit.

Die Designstudie: Stockübersicht & Bienenwetter (BeeKloppte) von @wtf freut sich bestimmt über weitere Teilnehmer. Herzlich willkommen!

Codeschnipsel zur Telemetriedatenübertragung

Die Telemetriedaten werden hier von Backend zu Backend übertragen, mindestens der Parameter “node” sollte also mit dem jeweiligen, über das UDP-Telemetriedatenpaket vermittelten Identifizierer belegt werden.

var terkin = require('./terkin');

var telemetry_node = new terkin.TelemetryNode(
    "https://swarm.hiveeyes.org/api",
    {
        "realm": "easyhive",
        "network": "testdrive",
        "gateway": "jena",
        "node": "cortex-m0-testbench",
    }
);

var data = {"temperature": 42.84, "humidity": 83.01};
telemetry_node.transmit(data);

Bei Fragen: fragen!

Viele Grüße und Freude beim Datenschaufeln aus Berlin nach Jena.


¹

Display

Wenn man beispielhaft Telemetriedaten erzeugt und überträgt per

echo '{"temperature": 42.42, "humidity": 84.84}' | mosquitto_pub -h swarm.hiveeyes.org -t easyhive/testdrive/foo/bar/data.json -l

sollten sie direkt z.B. im Grafana Instant Dashboard “easyhive-testdrive” zu sehen sein.

Die drop-in Konfiguration zu Kotori für einen selbst betriebenen Daten-Historian rund um InfluxDB und Grafana wäre für Euch die folgende.

root@elbanco:~# cat /etc/kotori/apps-available/easyhive.ini
; ------------------------------------------
; Name:     Easyhive
; Date:     June 2019 - ongoing
; About:    A monitoring platform collecting sensor 
;           data for the beekeeper community.
; Channel:  Transport: HTTP over TCP; Format: x-www-form-urlencoded
; Storage:  InfluxDB
; See also: https://easyhive.org/
;           https://getkotori.org/docs/applications/hiveeyes.html
; ------------------------------------------


; -----------------------------
; Data acquisition through MQTT
; -----------------------------
[easyhive]
enable      = true
type        = application
realm       = easyhive
mqtt_topics = easyhive/#
application = kotori.daq.application.mqttkit:mqttkit_application

# How often to log metrics
metrics_logger_interval = 60

# Enable automatic dashboard refresh interval taming
dashboard_refresh_taming = true


; --------------------------------------------------------------------
; Data acquisition through HTTP
; https://getkotori.org/docs/handbook/acquisition/protocol/http.html
; https://getkotori.org/docs/applications/forwarders/http-to-mqtt.html
; --------------------------------------------------------------------
[easyhive.http-acquisition]
enable          = true

type            = application
application     = kotori.io.protocol.forwarder:boot

realm           = easyhive
source          = http:/api/easyhive/{address:.*}/{slot:(data|event)} [POST]
target          = mqtt:/easyhive/{address}/{slot}.json


; ----------------------------------------------------------------------
; Data export
; https://getkotori.org/docs/handbook/export/
; https://getkotori.org/docs/applications/forwarders/http-to-influx.html
; ----------------------------------------------------------------------
[easyhive.data-export]
; See also:
enable          = true

type            = application
application     = kotori.io.protocol.forwarder:boot

realm           = easyhive
source          = http:/api/{realm:easyhive}/{network:.*}/{gateway:.*}/{node:.*}/{slot:(data|event)}.{suffix} [GET]
target          = influxdb:/{database}?measurement={measurement}
transform       = kotori.daq.intercom.strategies:WanBusStrategy.topology_to_storage,
                  kotori.io.protocol.influx:QueryTransformer.transform

Dass oben ist backend-to-backend code, um Daten z.B. auf easyhive und zusätzlich auf hiveeyes zu haben. Falls die easyhive-Waage – wie jetzt zum easyhive-Server – weiterhin UDP sendet, allerdings mit anderen endpoint, könnte das aktuell Kotori annehmen?

1 Like

Hi Clemens,

Kotori hat in vergangenen Projekten [1] zwei UDP Empfangsschnittstellen für Telemetriedaten realisiert. Der entsprechende Code hält sich allerdings in “vendor”-spezifischen Bereichen auf [2][3], ist also nicht generisch verwendbar.

Man könnte den Code jedoch generalisieren, dazu habe ich gerade [4] angelegt. Die Routine zur gerätespezifischen Dekodierung der Datenpakete müsste jedoch weiterhin “speziell” auf das von EasyHive gesendete Format zugeschnitten werden, klar.

Also: »Generische UDP Empfangsschnittstelle, konfigurierbar mit gerätespezifischem Dekodierer«, wäre das Ziel an der Stelle.

Viele Grüße,
Andreas.


  1. Gallery — Kotori 0.26.12 documentation ↩︎

  2. kotori/udp.py at 0.26.12 · daq-tools/kotori · GitHub ↩︎

  3. kotori/application.py at 0.26.12 · daq-tools/kotori · GitHub ↩︎

  4. Receiving telemetry data via UDP · Issue #88 · daq-tools/kotori · GitHub ↩︎

1 Like

Der relevante Code zum Versand einer Telemetrienachricht ist vermutlich jener hier.

Leider konnte ich nur an dieser Stelle eine Referenz auf die Funktion sendMessageThroughUDP(param) entdecken, wo leider auch nur eine Initialisierungsnachricht versendet wird.

Finden wir anderswo Informationen darüber, wie eine UDP Nachricht aussieht?

In Rons repo gibt es noch neueren Code:

Vielleicht kann auch @Ron helfen?

1 Like

Danke. Das hier sind die relevanten Stellen.

Datenformat

Spuren

Im “hello” Paket ist kein Gewicht und keine Temperatur enthalten, nur Spannung, Signalstärke und Geräteidentifizierer.

In den folgenden “vollen” Telemetriepaketen sind alle Werte enthalten.

Die Nachricht wird an den UDP Port 16666 gesendet.

Analyse

Es scheint ein zeichenkettenorientiertes Format zu sein, kodiert per CSV.

Schema

// [WEIGHT1], [WEIGHT2], [TEMP],  [VOLT],  [SIGNALQUALITY], [BOARDID], [PAKETNR/EPOCH]            
// [FLOAT],   [FLOAT],   [FLOAT], [FLOAT], [INT8_T],        [INTEGER], [LONG]

Serialisierung

String msg = \
    String(weight1) + "," + String(weight2) + "," + String(temp) + "," + \
    String(volt) + "," + String(csq) + "," + String(BoardID) + "," + String(epoch);

Verschlüsselung

Hier gibt es, wie von @Ron bei EasyHive_M0 SAMD21 nbiot Boards günstig abzugeben beschrieben, noch die Verschlüsselung mit XXTEA auf dem Übertragungsweg, per xxtea.c.

Für XXTEA finden sich hier zwei Python Implementierungen, das könnte also klappen.

1 Like

Hallo! Ihr habt ja schon eine Menge herausgeholt an Infos.
Der Code ist zwar älter, aber die Daten-Message hat sich grundsätzlich nicht verändert.

In der “hello” Nachricht sind noch Infos zum Netzbetreiber und angefunkte Antennenstation hinzugekommen:

// Get provider.
nbiot.getProvider(&provider);

// Check signal quality.
nbiot.getRSSIAndBER(&SIGNAL, &BER);

// Read Battery voltage
float VOLT = getBatteryVoltage();

// Build message.
// [FLOAT], [FLOAT], [FLOAT], [FLOAT], [INT8_T], [INTEGER], [LONG]
// [WEIGHT1],[WEIGHT2],[TEMP],[VOLT],[SIGNALQUALITY],[BOARDID],[PAKETNR/EPOCH]
**String startmsg = "Hello!," + String(provider) + "," + String(cellId) + "," + String(VOLT) + "," + String(SIGNAL) + "," + String(BoardID);**

// Send message and check answer.
if (sendMessageThroughUDP(startmsg.c_str())) {
  DEBUG_STREAM.println("FINE!");
}

Die Information, wohin das ganze geschickt wird ist in der EasyHive.cpp abgespeichert:

struct Serverdata{
  boolean isSet;
  char ip[16];
  uint16_t port;
} 
server = {false, "255.255.255.255", 9999};

Wir haben auch ein kleines NodeJS UDP-Skript zum verschicken von Testdaten, vielleicht hilft das auch etwas weiter, siehe test_send_udp.

test_send_udp
/**
 *
 * Demonstration program for submitting telemetry data to the EasyHive backend.
 *
 * - https://easyhive.org/
 * - https://github.com/easyhive
 *
 **/

var dgram = require('dgram');
var xxtea = require('xxtea-node');
const https = require('https');
const url = require('url');

var key = "yourKEY";


var PORT = 9999;
var HOST = '255.255.255.255';

var d = new Date();
var seconds = Math.round(d.getTime() / 1000);

// standard message
// [FLOAT], [FLOAT], [FLOAT], [FLOAT], [INT8_T], [INTEGER], [INTEGER]
// [WEIGHT1],[WEIGHT2],[TEMP],[VOLT],[SIGNALQUALITY],[BOARDID],[PAKETNR]

var message = '1000.00,2000.00,20.1,4.1,-88,11,' + seconds 

// Hello Message
// Hello!,[Provider],[CELLID],[VOLT],[SIGNALQUALITY],[BOARDID],[]
// var message = 'Hello!,26201,27717381,4.1,-88,11'

var answercrypted = xxtea.encryptToString(message,key);

var client = dgram.createSocket('udp4');
client.send(answercrypted, PORT, HOST, function(err, bytes) {
  if (err) throw err;
  console.log('UDP message sent to ' + HOST +':'+ PORT);
  client.close();
});
1 Like

Noch eine wichtige Sache ist die Antwort, die an die Waage zurückgeschickt werden muss.
Falls keine Antwort vom Server kommt sendet die Waage die Daten später noch einmal nach (man könnte das auch deaktivieren, aber dann könnten Daten verloren gehen)

Es muss also auch eine direkte Rückantwort vom Server auf die Nachricht von der Waage kommen. Im einfachsten Fall “OK!”, aber es können auch Config-Befehle geschickt werden.

Wenn die Waage sich beim Server meldet hat sie eine eindeutige IP+Port Adresse,über die sie in dem Moment dann erreichbar ist. Die muss für die Antwort verwendet werden. Ich muss noch mit Albi und Daria reden wegen der Veröffentlichung der Code-Datein auf dem Server, aber da habt ihr dann auch ein Beispiel sowie die möglichen config-Befehle (das Kalibrieren der Waagen läuft z.B. auch online)

liebe Grüße!

1 Like

das ist nur der Port vom Ublox- bzw Quectel-FunkModul

Backend-Backend Weiterleitung mit Terkin

Ich hab jetzt den Terkin-Code mal in unseren Backend probehalber eingespielt. Die Datenübermittlung an das Grafana Testdrive läuft bisher mit Testdaten. Ich hab eine unsere Waagen aus dem Lager mal auf den Testport geschickt. Die sollte bald regelmäßig Daten schicken.

Spannend wäre jetzt noch einen “richtigen” Account zu erhalten.
Und noch spannender wäre natürlich eine Verbindung via mqtt oder udp wie oben beschrieben zum Laufen zu bekommen.

3 Likes

Hi Ron,

schön dass Ihr weiterhin am Thema dran seid, das freut mich sehr.

Wunderbarst. [1]

Klar, bekommt Ihr gerne. Die Frage ist, ob “per-Device”, oder irgendwie “catch-all”. Wahrscheinlich sollten wir dazu einfach mal telefonieren?

Absolut.

Viele Grüße,
Andreas.


  1. Welchen Code genau? ↩︎

Ich denke der code hier im thread, erstes posting.

Dazu kurz der Hintergrund, warum wir den Faden gerade jetzt wieder aufnehmen wollen: Das bei der documenta 2022 gezeigte Projekt BeeDAO (schöner Essay dazu unter BeeDaO und die Demokratie der Bienen) soll etwas kleiner ab Dezember 2023 im Zeppelin Museum Friedrichshafen gezeigt werden, Sonderausstellung Kryptomania. Die Verheißungen der Blockchain. Die Daten der Imker aus Kassel sollen dabei auch mit lokalen Daten von Bienen / Imkern am Bodensee ergänzt werden. Dafür wollen wir ein paar easyhive-Waagen nutzen. Sprich die Daten, um die es aktuell geht, sollen hier als hive-30, hive-31 usw. landen: https://swarm.hiveeyes.org/grafana/d/cx_KOvH7k/documenta-stockubersicht-and-bienenwetter.

Das ist eh nur ein workaround weil wir das direkte Senden der Daten von der easyhive-Waage direkt zu hiveeyes so schnell nicht programmiert und vor allem gut getestet bekommen.

1 Like

Ah, logisch ;]. Danke.

Klappt denn nun alles gut? terkin-javascript nutzt ja HTTP, daher müsste der gewünschte Kanal für Euch einfach adressierbar sein, ohne viel zutun. Sagt ansonsten gern Bescheid, wenn Ihr dazu noch Unterstützung braucht.

Wir, @ron und ich, haben jetzt ein paar Testdaten von backend zu backend geschickt, was schon gut funktioniert. Dabei haben wir bisher keinen timestamp mit geschickt. Die easyhive Waage kann aber auch Daten zwischenspeichern und “nochmal” schicken, wenn z.B. die Konnektivität einmal nicht funktioniert. Dafür müssen wir aber den timestamp mitschicken.

Kotori kann auch einen timestamp annehmen und den dann statt system time verwenden, Variable ist time und die eigentliche Zeit kann in unterschiedlichen Formaten übergeben werden, s. Timestamp formats - Kotori DAQ

@Ron, was kommt denn von der Waage, ist das Format kompatibel mit etwas aus der Liste von Kotori / @Andreas?

Human readable timestamp in ISO 8601 format, UTC

wäre mein Favorit, das ist das gleiche wie bei der Kotori-Rohdaten-Ausgabe.