Zum Inhalt springen

TTN Node Integration per Webhook

    1. Möglichkeiten zur Datenverarbeitung in TTN

    Wenn ein Node erfolgreich in The Things Network (TTN) integriert ist, stellt sich die Frage, wie die gesammelten Daten weiterverarbeitet werden sollen. TTN bietet verschiedene Optionen zur Datenübertragung und -verarbeitung, um unterschiedliche Anforderungen und Anwendungsfälle abzudecken. Im Folgenden werden die möglichen Integrationsmethoden vorgestellt und die Vorteile der TTN Node Integration per Webhook näher beleuchtet.
    Lies dir auch gerne meinen Beitrag zum Erstellen eines SensorNodes durch.

    1.1 Übersicht über Datenverarbeitungsoptionen

    TTN bietet eine breite Palette von Schnittstellen, um Daten aus Ihrem LoRaWAN-Netzwerk zu exportieren und in andere Systeme zu integrieren. Hier sind die wichtigsten Optionen:

    • MQTT (Message Queuing Telemetry Transport): MQTT ist ein leichtgewichtiges Nachrichtenprotokoll, das besonders für IoT-Anwendungen geeignet ist. Mit MQTT können Daten in nahezu Echtzeit abgerufen und an andere Systeme oder Anwendungen übermittelt werden.
    • HTTP Webhooks: Webhooks sind eine einfache Möglichkeit, Daten automatisch an einen definierten Webserver weiterzuleiten. Sie sind ideal, wenn Daten in Echtzeit verarbeitet oder auf einem eigenen Server gespeichert werden sollen.
    • Integration mit Drittanbieter-Plattformen: TTN bietet vordefinierte Integrationen für Plattformen wie AWS IoT, Azure IoT Hub und Google Cloud. Diese Optionen eignen sich besonders, wenn Sie umfangreichere Cloud-basierte Datenverarbeitung und Analyse benötigen.
    • Benutzerdefinierte Skripte und Anwendungen: Für Benutzer, die maximale Kontrolle über ihre Daten benötigen, kann TTN auch mit eigenen Skripten und Anwendungen integriert werden, um individuelle Workflows zu unterstützen.

    Jede dieser Optionen hat spezifische Vor- und Nachteile, abhängig von der Komplexität der Anwendung, der benötigten Verarbeitungsgeschwindigkeit und der Infrastruktur, die bereits vorhanden ist.


    1.2 Vorteile der TTN Node Integration per Webhook

    Webhooks stellen eine der flexibelsten und einfachsten Methoden zur Weiterleitung von Daten aus TTN dar. Hier sind einige der wichtigsten Vorteile:

    • Einfache Konfiguration: Webhooks erfordern lediglich eine Ziel-URL, an die die Daten gesendet werden. Diese URL kann leicht in der TTN-Konsole eingerichtet werden.
    • Echtzeit-Datenübertragung: Daten werden direkt nach dem Empfang durch TTN an die angegebene URL weitergeleitet, wodurch eine nahezu verzögerungsfreie Verarbeitung möglich ist.
    • Hohe Flexibilität: Webhooks können mit nahezu jedem Webserver oder cloudbasierten Service verwendet werden, solange dieser HTTP-Anfragen akzeptieren kann. Das macht sie zu einer universellen Lösung.
    • Individuelle Anpassung: Durch die Verwendung von Webhooks kannst du die Verarbeitung und Speicherung der Daten komplett selbst kontrollieren. Beispielsweise können JSON-Daten decodiert, in Datenbanken gespeichert oder in andere Formate umgewandelt werden.

    Mit Webhooks bietet TTN eine mächtige und gleichzeitig benutzerfreundliche Methode, um IoT-Daten effizient weiterzuverarbeiten. In den folgenden Abschnitten des Beitrags werde ich dir zeigen, wie du Webhooks einrichten und die empfangenen Daten auf Ihrem eigenen Server speichern und verarbeiten kannst.


    2. Einrichtung eines Webhooks in der TTN-Konsole

    Um einen Webhook in der TTN-Konsole einzurichten, folge diesen einfachen Schritten:

    1. Logge dich in die TTN-Konsole ein: Melde dich mit deinem Benutzerkonto an und wähle die entsprechende Anwendung aus, die deinen Node enthält.
    2. Navigiere zu Integrationen: Unter dem Tab „Integrationen“ findest du die Option, einen neuen Webhook hinzuzufügen. Klicke auf „Add Webhook“.
    Webhook auf TTN hinzufügen

    3. Wähle einen Webhook-Typ: TTN bietet mehrere vordefinierte Webhook-Vorlagen. Wähle für dieses Beispiel eine benutzerdefinierte Integration.

    Webhook Template auf TTN wählen

    4. Gebe in markierten Feldern geforderte Daten ein: Trage die URL deines Webservers ein, an den die Daten gesendet werden sollen. Dies ist die wichtigste Einstellung, da sie bestimmt, wohin die Nachrichten geleitet werden.
    Konfiguriere weitere Parameter: Du kannst optionale Einstellungen wie Header oder Authentifizierungsdaten definieren, falls Ihr Server diese benötigt.

    5. Speichere die Einstellungen: Nach dem Speichern ist der Webhook aktiv und empfängt Daten von deinem Node.

    Mit diesen Schritten ist der Webhook einsatzbereit. Daten von deinem Node werden nun automatisch an Ihren Server weitergeleitet, wo du sie weiterverarbeiten kannst. Im nächsten Abschnitt erfährst du, wie du die empfangenen JSON-Daten decodierst und speicherst.


    3. Daten auf dem eigenen Server speichern

    Die Speicherung der empfangenen Daten auf dem eigenen Server ist ein essenzieller Schritt, um sie für weitere Analysen, Visualisierungen oder Automatisierungen zu nutzen. Dabei spielt die Decodierung der JSON-Daten eine zentrale Rolle.

    3.1 Decodierung und Aufbereitung der Daten

    Die von TTN empfangenen Daten werden in der Regel als JSON-Payload übermittelt. Diese Daten enthalten wichtige Informationen wie Sensormesswerte, Zeitstempel und Metadaten. Um diese Daten effizient weiter zu verarbeiten, müssen diese erst einmal decodiert und geprüft werden.

    Im Auszug des vollständigen PHP-Skriptes habe ich 2 Stellen markiert und das Ergebnis in weiteren Tabs eingefügt. Wichtig zu verstehen ist die Art, wie TTN das JSON liefert und wie du das JSON dekodiert und dann in ein Array packst. Damit hast du die Voraussetzungen geschaffen, die Daten im PHP-Skript weiter zu verarbeiten bzw. in eine Datenbank zu schreiben.

    // JSON-Daten einlesen
    $json = file_get_contents("php://input");
    
    // optional - Debugging: Empfangene Rohdaten speichern
    file_put_contents("debug_raw_json.txt", $json);
    
    // JSON-Daten dekodieren
    $data = json_decode($json, true);
    
    if (json_last_error() !== JSON_ERROR_NONE) {
        http_response_code(400); // Bad Request
        echo "Invalid JSON format";
        exit;
    }
    {
        "end_device_ids": {
            "device_id": "01-sendor-xxxxx",
            "application_ids": {
                "application_id": "sensors-xxxxx01"
            },
            "dev_eui": "xxxxxxxxxxxxxxxxx",
            "join_eui": "xxxxxxxxxxxxxxx ",
            "dev_addr": "260BEB64"
        },
        "correlation_ids": [
            "gs:uplink:01JD5GAS9JRE25XXGSYW2KFN7Z"
        ],
        "received_at": "2024-11-20T19:21:05.279326266Z",
        "uplink_message": {
            "session_key_id": "XXXXXXXXX\/PjfPtkJmCNa5w==",
            "f_port": 1,
            "f_cnt": 47,
            "frm_payload": "CAsC",
            "decoded_payload": {
                "humidity": 51.2,
                "temperature": 20.58
            },
            "rx_metadata": [
                {
                    "gateway_ids": {
                        "gateway_id": "xxxxxxxx-gw-01",
                        "eui": "4xxxxxxxxxxx"
                    },
                    "time": "2024-11-20T19:21:05.008706Z",
                    "timestamp": 331733460,
                    "rssi": -74,
                    "channel_rssi": -74,
                    "snr": 8.75,
                    "location": {
                        "latitude": xx.6853802326073008xx,
                        "longitude": xx.337478060088400xx,
                        "altitude": 54,
                        "source": "SOURCE_REGISTRY"
                    },
                    "uplink_token": "ChsKGQoNcG5nc2hnbi1ndy0wMRIIQTd=",
                    "channel_index": 4,
                    "received_at": "2024-11-20T19:21:05.050186747Z"
                }
            ],
            "settings": {
                "data_rate": {
                    "lora": {
                        "bandwidth": 125000,
                        "spreading_factor": 7,
                        "coding_rate": "4\/5"
                    }
                },
                "frequency": "867300000",
                "timestamp": 331733460,
                "time": "2024-11-20T19:21:05.008706Z"
            },
            "received_at": "2024-11-20T19:866334268357604742050170898437521:05.075092061Z",
            "consumed_airtime": "0.051456s",
            "network_ids": {
                "net_id": "000013",
                "ns_id": "EC656E0000000181",
                "tenant_id": "ttn",
                "cluster_id": "eu1",
                "cluster_address": "eu1.cloud.thethings.network"
            }
        }
    }
    
    Array
    (
        [end_device_ids] => Array
            (
                [device_id] => 01-sendor-xxxxx
                [application_ids] => Array
                    (
                        [application_id] => sensors-xxxxx-01
                    )
    
                [dev_eui] => xxxxxxxxxxxxxx
                [join_eui] => xxxxxxxxxxxxxx
                [dev_addr] => 260BF8E3
            )
    
        [correlation_ids] => Array
            (
                [0] => gs:uplink:01JD5HPQN34B3CADTZ12Z7MNBD
            )
    
        [received_at] => 2024-11-20T19:45:05.394568305Z
        [uplink_message] => Array
            (
                [session_key_id] => AZNLGn1/pakiejgMU1sy0w==
                [f_port] => 1
                [f_cnt] => 1
                [frm_payload] => CC0C
                [decoded_payload] => Array
                    (
                        [humidity] => 51.2
                        [temperature] => 20.93
                    )
    
                [rx_metadata] => Array
                    (
                        [0] => Array
                            (
                                [gateway_ids] => Array
                                    (
                                        [gateway_id] => xxxxxxxgw-01
                                        [eui] => xxxxxxxxxxxxxxx
                                    )
    
                                [time] => 2024-11-20T19:45:05.112839Z
                                [timestamp] => 1771849956
                                [rssi] => -74
                                [channel_rssi] => -74
                                [snr] => 8.25
                                [location] => Array
                                    (
                                        [latitude] => xx.6853802326
                                        [longitude] => xx.3374780601
                                        [altitude] => 54
                                        [source] => SOURCE_REGISTRY
                                    )
    
                                [uplink_token] => ChsKGQoNcG5nc2hnbi1ndy0wMRIIQTdOSzBGSjYQ5JHxzAYaCwjB+Pi5BhDj1qRZIKD18tPIsAE=
                                [channel_index] => 1
                                [received_at] => 2024-11-20T19:45:05.165056092Z
                            )
    
                    )
    
                [settings] => Array
                    (
                        [data_rate] => Array
                            (
                                [lora] => Array
                                    (
                                        [bandwidth] => 125000
                                        [spreading_factor] => 7
                                        [coding_rate] => 4/5
                                    )
    
                            )
    
                        [frequency] => 868300000
                        [timestamp] => 1771849956
                        [time] => 2024-11-20T19:45:05.112839Z
                    )
    
                [received_at] => 2024-11-20T19:45:05.188530359Z
                [consumed_airtime] => 0.051456s
                [network_ids] => Array
                    (
                        [net_id] => 000013
                        [ns_id] => EC656E0000000181
                        [tenant_id] => ttn
                        [cluster_id] => eu1
                        [cluster_address] => eu1.cloud.thethings.network
                    )
    
            )
    
    )
    

    4. Speicherung in einer Datenbank

    Nachdem die Daten decodiert und validiert wurden, können sie in einer Datenbank gespeichert werden. Eine relationale Datenbank wie MySQL oder PostgreSQL ist hierfür besonders geeignet. Die Speicherung bietet folgende Vorteile:

    • Strukturierte Ablage: Die Daten können in Tabellen organisiert werden, was eine einfache Abfrage und Analyse ermöglicht.
    • Langfristige Archivierung: Sensordaten können über einen längeren Zeitraum aufbewahrt und für historische Analysen genutzt werden.
    • Basis für Visualisierungen: Mit den gespeicherten Daten lassen sich Diagramme, Dashboards oder Berichte erstellen.
    <?php
    // Verbindung zur Datenbank herstellen
    require_once('connection.php'); // Verbindung über deine connection.php mit LoginDaten
    
    // Definieren des API-Schlüssels
    $api_key = 'dein-selbst-erstellter-key';
    
    // Überprüfen des API-Keys im Header
    $incoming_key = $_SERVER['HTTP_X_API_KEY'] ?? '';
    if ($incoming_key !== $api_key) {
        http_response_code(401); // Unauthorized
        echo "Unauthorized request";
        exit;
    }
    
    // JSON-Daten einlesen
    $json = file_get_contents("php://input");
    
    // Debugging: Empfangene Rohdaten speichern
    file_put_contents("debug_raw_json.txt", $json);
    
    // JSON-Daten dekodieren
    $data = json_decode($json, true);
    
    if (json_last_error() !== JSON_ERROR_NONE) {
        http_response_code(400); // Bad Request
        echo "Invalid JSON format";
        exit;
    }
    
    // Werte aus dem JSON extrahieren
    try {
        // Daten aus dem JSON extrahieren (direkt von der obersten Ebene)
        $received_at = $data['received_at'] ?? null;
        $uplink_message = $data['uplink_message'] ?? [];
        $decoded_payload = $uplink_message['decoded_payload'] ?? [];
        $rssi = $uplink_message['rx_metadata'][0]['rssi'] ?? null;
        $snr = $uplink_message['rx_metadata'][0]['snr'] ?? null;
        $consumed_airtime = $uplink_message['consumed_airtime'] ?? null;
        $temperature = $decoded_payload['field1'] ?? null;
        $humidity = $decoded_payload['field2'] ?? null;
        $gw = $uplink_message['rx_metadata'][0]['gateway_ids']['gateway_id'] ?? null; // Extrahiert die Gateway-ID
    
        // Überprüfung, ob notwendige Werte vorhanden sind
        if (empty($received_at) || $temperature === null || $humidity === null || empty($gw)) {
            throw new Exception("Missing required data");
        }
    
        // received_at in Datum und Zeit aufteilen und Zeitzone konvertieren
        $date = $time = null;
        if ($received_at) {
            $datetime = new DateTime($received_at, new DateTimeZone('UTC')); // UTC-Zeit aus TTN
            $datetime->setTimezone(new DateTimeZone('Europe/Berlin')); // Lokale Zeitzone (MEZ/CEST)
            $date = $datetime->format('Y-m-d'); // Datum im Format 'YYYY-MM-DD'
            $time = $datetime->format('H:i:s'); // Zeit im Format 'HH:MM:SS'
        }
    
        // Daten in die Datenbank einfügen
        $stmt = $conn->prepare("
            INSERT INTO `01-xxxxxxxxx` //deine Datenbank
            (`date`, `time`, `rssi`, `snr`, `consumed_airtime`, `gateway`, `temperature`, `humidity`)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?)
        ");
        $stmt->bind_param(
            "ssddssdd",  // Typen: 's' für String, 'd' für Double/Float
            $date,
            $time,
            $rssi,
            $snr,
            $consumed_airtime,
            $gw,
            $temperature,
            $humidity
        );
    
        if ($stmt->execute()) {
            http_response_code(200); // Success
            echo "Data successfully saved";
        } else {
            throw new Exception("Failed to save data: " . $stmt->error);
        }
        $stmt->close();
    } catch (Exception $e) {
        http_response_code(500); // Server Error
        echo "Error processing data: " . $e->getMessage();
    }
    
    // Verbindung schließen
    $conn->close();

    Erläuterungen zum Code:

    In der Konfiguration deines Webhooks hast du den X-API-Key eingegeben.
    Hier wir dieser geprüft und der Code wird nur abgearbeitet, wenn der Schlüssel übereinstimmt.

    // Definieren des API-Schlüssels
    $api_key = 'dein-selbst-erstellter-key';
    
    // Überprüfen des API-Keys im Header
    $incoming_key = $_SERVER['HTTP_X_API_KEY'] ?? '';
    if ($incoming_key !== $api_key) {
        http_response_code(401); // Unauthorized
        echo "Unauthorized request";
        exit;
    }

    Anschließend werden die Daten aus den erstellten Arrays in Variablen übernommen und entsprechend aufbereitet. Dies gilt besonders für die Erestellung von Datums- und Zeitangaben.

    Danach werden die erforderlichen Daten in die Datenbank geschrieben. Und für die Rückmeldung an das TTN noch der Response-Code ermittelt. 200 für ok und 500 für Fehler. Anschließend wird die Verbindung zur Datenbank geschlossen. Lies dir auch bitte meinen Beitrag zum Speichern der Daten in eine Datenbank durch.


    Durch die Kombination aus Decodierung und Speicherung schaffst du eine solide Grundlage für die Weiterverarbeitung deiner IoT-Daten. Im nächsten Beitrag gebe ich einen Ausblick darauf, wie du die Daten mit Drittanbieter-Plattformen wie ThingSpeak weiter nutzen kannst. Damit entfällt für dich auch die Voraussetzung einen eigenen Server haben zu müssen.

    Damit sind wir wieder an Ende eines neuen DIYTechAdventures angelangt. Ich hoffe, alles soweit verständlich aufbereitet und rüber gebracht zu haben. Schreibe mir bitte in den Kommentaren, wie du mit dieser Anleitung klar gekommen bist, bzw. was noch besser gemacht werden kann.


    0 Kommentare
    Inline Feedbacks
    View all comments