lets_plaiy:lorawan:start
Differences
This shows you the differences between two versions of the page.
| Next revision | Previous revision | ||
| lets_plaiy:lorawan:start [2026/04/27 16:52] – created jan.sonntag | lets_plaiy:lorawan:start [2026/04/28 12:20] (current) – jan.sonntag | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | ====== | + | ====== |
| In diesem Workshop geben wir eine kurze Einführung in LoRaWAN und die Verwendung zusammen mit einem Seeedstudio Wio E5 und einem Raspberry Pi. | In diesem Workshop geben wir eine kurze Einführung in LoRaWAN und die Verwendung zusammen mit einem Seeedstudio Wio E5 und einem Raspberry Pi. | ||
| Line 5: | Line 5: | ||
| ===== Was ist LoRaWAN? ===== | ===== Was ist LoRaWAN? ===== | ||
| + | {{pdfjs 75%, | ||
| ===== The Things Network ===== | ===== The Things Network ===== | ||
| + | The Things Network (TTN) versteht sich als globales, kollaboratives Ökosystem, das die [[https:// | ||
| + | |||
| + | Hier geht es zur Registrierung: | ||
| + | Registeriere dich, und lege deine erste Application an, hier fügen wir dann später unseren Wio-E5 ein. | ||
| ===== Raspberry Pi und E5 verbinden ===== | ===== Raspberry Pi und E5 verbinden ===== | ||
| + | Verbinde den Raspberry Pi und den E5 über die Pins des Raspberry Pi´s wie folgt: | ||
| + | * 3.3V -> Rotes Kabel | ||
| + | * Ground -> Schwarzes Kabel | ||
| + | * UART0 Tx (GPIO 14) -> Weißes Kabel | ||
| + | * UART0 Rx (GPIO 15) -> Gelbes Kabel | ||
| - | ===== Erste manuelle Tests ===== | + | [Suche gerne nach einem sogenannten Pinout im Internet zu dem von dir verwendeten Pi damit du die Kabel richtig anschließen kannst] |
| + | |||
| + | Die Kabelfarben stimmen mit den offiziellen Grove Kabel Farben überein. | ||
| + | 3.3V und Ground sind für die Spannungsversorgung zuständig. Tx und Rx sind Teil des von dem Wio-E5 Modul verwendeten Serial Protokolls. Tx steht für Transmit und Rx für Receive. Hier ist Obacht geboten! Dies ist dann keine 1:1 Verbindung sondern diese müssen Überkreuz angeschlossen werden. Rx vom Modul geht zum Tx vom Raspberry Pi und so auch das Tx vom Modul zum Rx vom Pi. | ||
| + | |||
| + | Um die serielle Schnittstelle auf dem Raspberry Pi zu aktivieren, nutzt man den Befehl '' | ||
| + | |||
| + | Das Terminal werden wir auch im weiteren Teil des Workshops verwenden. | ||
| + | |||
| + | ===== Was sind AT-Befehle und wie funktionieren sie? ===== | ||
| + | |||
| + | AT-Befehle (abgeleitet von **AT**tention) sind ein standardisierter Befehlssatz zur Steuerung von Modems und Funkmodulen über eine serielle Schnittstelle (UART). In diesem Workshop dienen sie als Brücke zwischen dem Raspberry Pi und dem LoRa-E5 Modul, um komplexe LoRaWAN-Prozesse durch einfache Textbefehle zu steuern. | ||
| + | |||
| + | ==== Funktionsweise im Projekt ==== | ||
| + | Die Kommunikation erfolgt nach einem einfachen Frage-Antwort-Prinzip: | ||
| + | |||
| + | * **Syntax:** Jeder Befehl beginnt mit dem Präfix '' | ||
| + | * **Testen:** Ein einfaches '' | ||
| + | * **Konfiguration: | ||
| + | * **Aktion:** Mit Befehlen wie '' | ||
| + | |||
| + | {{pdfjs 75%, | ||
| ===== Tests mit Python (Beispiel Skript) ===== | ===== Tests mit Python (Beispiel Skript) ===== | ||
| + | Für eine komfortable Steuerung des LoRa-Moduls steht das Skript '' | ||
| + | |||
| + | ==== Vorbereitung ==== | ||
| + | Bevor das Skript gestartet werden kann, muss die benötigte Python-Bibliothek installiert werden: | ||
| + | < | ||
| + | pip install pyserial | ||
| + | </ | ||
| + | |||
| + | Hier ist außerdem das Skript. Referenziere es gerne um bestimmte Funktionen zu verstehen: | ||
| + | <file python LoRaWorkshop.py> | ||
| + | import serial | ||
| + | import time | ||
| + | |||
| + | PORT = "/ | ||
| + | BAUD = 9600 | ||
| + | |||
| + | def send_command(ser, | ||
| + | """ | ||
| + | ser.write(f" | ||
| + | start_time = time.time() | ||
| + | response = "" | ||
| + | |||
| + | while (time.time() - start_time) < timeout: | ||
| + | if ser.in_waiting > 0: | ||
| + | line = ser.readline().decode(errors=' | ||
| + | if line: | ||
| + | print(f" | ||
| + | response += line | ||
| + | if wait_for in line: | ||
| + | return True | ||
| + | return False | ||
| + | |||
| + | def main_menu(): | ||
| + | try: | ||
| + | ser = serial.Serial(PORT, | ||
| + | print(" | ||
| + | |||
| + | while True: | ||
| + | print(" | ||
| + | print(" | ||
| + | print(" | ||
| + | print(" | ||
| + | print(" | ||
| + | print(" | ||
| + | print(" | ||
| + | print(" | ||
| + | print(" | ||
| + | print(" | ||
| + | # print(" | ||
| + | |||
| + | choice = input(" | ||
| + | |||
| + | if choice == ' | ||
| + | send_command(ser, | ||
| + | |||
| + | elif choice == ' | ||
| + | send_command(ser, | ||
| + | |||
| + | elif choice == ' | ||
| + | print(" | ||
| + | keywords = [" | ||
| + | ser.write(b" | ||
| + | start_time = time.time() | ||
| + | joined = False | ||
| + | |||
| + | while (time.time() - start_time) < 30: | ||
| + | if ser.in_waiting > 0: | ||
| + | line = ser.readline().decode(errors=' | ||
| + | if line: | ||
| + | print(f" | ||
| + | line_lower = line.lower() | ||
| + | if any(key in line_lower for key in keywords): | ||
| + | joined = True | ||
| + | break | ||
| + | if " | ||
| + | break | ||
| + | |||
| + | if joined: | ||
| + | print(" | ||
| + | else: | ||
| + | print(" | ||
| + | |||
| + | elif choice == ' | ||
| + | msg = input(" | ||
| + | send_command(ser, | ||
| + | |||
| + | elif choice == ' | ||
| + | hex_payload = input(" | ||
| + | hex_payload = hex_payload.replace(" | ||
| + | send_command(ser, | ||
| + | |||
| + | elif choice == ' | ||
| + | send_command(ser, | ||
| + | |||
| + | elif choice == ' | ||
| + | send_command(ser, | ||
| + | | ||
| + | elif choice == ' | ||
| + | msg = input(" | ||
| + | print(" | ||
| + | try: | ||
| + | while True: | ||
| + | print(f" | ||
| + | send_command(ser, | ||
| + | print(" | ||
| + | time.sleep(45) | ||
| + | # Es gibt Gesetzliche Vorgaben (5min) für den Abstand von Signalen | ||
| + | # Wollen ja nicht die Bundesnetzagentur im nacken haben | ||
| + | except KeyboardInterrupt: | ||
| + | print(" | ||
| + | |||
| + | elif choice == ' | ||
| + | msg = input(" | ||
| + | send_command(ser, | ||
| + | |||
| + | elif choice == ' | ||
| + | print(" | ||
| + | ser.close() | ||
| + | break | ||
| + | else: | ||
| + | print(" | ||
| + | | ||
| + | time.sleep(1) | ||
| + | |||
| + | except serial.SerialException as e: | ||
| + | print(f" | ||
| + | |||
| + | if __name__ == " | ||
| + | main_menu() | ||
| + | </ | ||
| + | |||
| + | ==== Skript starten ==== | ||
| + | Navigiere im Terminal in den Ordner, in dem die Datei liegt, und führe sie aus: | ||
| + | < | ||
| + | python LoRaWorkshop.py | ||
| + | </ | ||
| + | |||
| + | ==== Funktionsübersicht ==== | ||
| + | Nach dem Start erscheint ein Auswahlmenü mit folgenden Optionen: | ||
| + | |||
| + | ^ Auswahl ^ Funktion ^ Beschreibung ^ | ||
| + | | **1** | Test (AT) | Prüft, ob die RX/ | ||
| + | | **2** | IDs auslesen | Zeigt DevEui, DevAddr und AppEui für die TTN-Registrierung an. | | ||
| + | | **3** | Mit TTN verbinden | Startet den Join-Prozess (AT+JOIN). Ein Gateway muss erreichbar sein. | | ||
| + | | **4** | Nachricht senden (Text) | Übermittelt eine Nachricht im Klartext. | | ||
| + | | **5** | Nachricht senden (Hex) | Übermittelt Daten als Hexadezimal-Zeichen (ASCII-Code). | | ||
| + | | **6** | Reset | Startet das LoRa-Modul neu, falls es nicht reagiert. | | ||
| + | | **7** | Loop-Modus | Sendet automatisch alle 45 Sekunden eine Nachricht. | | ||
| + | | **f** | Verbindung erzwingen | Verlässt bestehende Sitzungen und erzwingt einen neuen Join (Force). | | ||
| + | | **q** | Beenden | Schließt das Skript (auch per STRG+C möglich). | | ||
| + | |||
| + | > **Hinweis: | ||
| + | |||
| + | ==== Payload Decoder ==== | ||
| + | Um die Daten, welche in TTN über LoRaWAN ankommen, zu decodieren kann ein sogenannter Payload Decoder hinterlegt werden. Da die Daten in diesem Fall über die ASCII Tabelle (Gerne mal googeln) codiert wurden in Hexadezimal, | ||
| + | <file javascript decoder.js> | ||
| + | function decodeUplink(input) { | ||
| + | var bytes = input.bytes; | ||
| + | | ||
| + | // Konvertierung der Bytes in einen String (ASCII) | ||
| + | var text = ""; | ||
| + | for (var i = 0; i < bytes.length; | ||
| + | text += String.fromCharCode(bytes[i]); | ||
| + | } | ||
| + | |||
| + | return { | ||
| + | data: { | ||
| + | type: " | ||
| + | message: text, | ||
| + | length: bytes.length | ||
| + | } | ||
| + | }; | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | ===== Credits ===== | ||
| + | Die Entwicklung des Python Scripts sowie die Anleitung für die manuellen Tests wurden durch den Studenten Tim Oswald im Rahmen seiner Bachelorarbeit durchgeführt. Hier der Link zu seinem erstellten Cheatsheet: {{ : | ||
lets_plaiy/lorawan/start.1777301572.txt.gz · Last modified: 2026/04/27 16:52 by jan.sonntag