Für das Projekt "Arduino weltweit steuern" wird ein Server benötigt, der als UDP Paket Relay funktioniert. Das Script wurde in Python programmiert und ist somit ohne Einschränkungen in der Regel mit allen Betriebssystemen kompatibel. Auf dieser Seite gibt es die Dokumentation zur Serversoftware.

Die Voraussetzungen

Damit das UDP Relay gestartet werden kann, wird die Installation von Python benötigt. Sollte das Relay öffentlich erreichbar sein, wird ein öffentlicher Server benötigt. Alternativ kann dieser Server auch Zuhause mit entsprechenden Portforwarding und einem dynamischen IP-Updater (DynDNS, NO-IP...) auf einem Raspberry betrieben werden. Das Script wurde unter Python3 getestet.

Wie das Relay funktioniert

Das Relay wird je nach Betriebssystem meistens über einen entsprechenden Befehl gestartet. Dadurch wird ein Socket auf den Port 9999 gestartet. Die Software wechselt anschließend in eine Schleife und wartet auf ein "Keep-Alive" vom Controller. Sobald sich dieser meldet und sich als "Arduino" ausgibt, ist dem Server der offene Port und die öffentliche IP-Adresse vom Controller bekannt. Ab sofort werden alle gültigen Befehle an diese IP-Adresse weitergeleitet. Sollte sich der Controller noch nicht beim Server gemeldet haben und es kommen trotzdem bereits Steuerbefehle vom Client, so sendet der Server eine entsprechende Fehlermeldung an den Client zurück. Wichtig ist, dass sich nach dem Programmstart zuerst immer der Controller beim Server melden muss, damit Steuerbefehle vom Client über den Server zum Controller gesendet werden können.

Hat sich der Controller beim Server angemeldet, so kann ab sofort über den Client jederzeit ein Steuerbefehl empfangen und weitergeleitet werden. Da aus dem Internet auch sehr viel "Müll" am Server ankommt, müssen die UDP Pakete entsprechend gefiltert werden. Unter "Müll" versteht man in diesem Fall UDP-Pakete, die von einem anderen System an den Server gesendet werden, aber eigentlich nicht dafür bestimmt sind (z. B. VoIP Anfragen usw.). Um eine Weiterleitung von falschen UDP-Paketen an den Controller zu verhindern, prüft die Software alle ankommenden Steuerbefehle. Sobald ein Befehl "CMD:" enthält, wird er an den Controller weitergeleitet. Umgekehrt erkennt der Server ein Datenpaket vom Controller, da sich dieser mit seinen Namen (Arduino) meldet. Kommt vom Controller ein Paket, wird es an die Adresse vom zuletzt verbundenen Client gesendet. Es können daher Steuerbefehle von beliebig vielen Clients gesendet werden, aber nur der Client, der zuletzt etwas zum Server gesendet hat, bekommt die Antwort.

Download Python Server (ZIP)


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import socket

server_address = '0.0.0.0'  # Listen on all
server_port = 9999  # Listen on Port 9999
controller_name = "Arduino"  # Name or ID of your Controller

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
controller_address = ""
controller_reg = False
client_adr = ""
client_reg = False
RECV_BUFFER = ""  # Buffer for received data
server = (server_address, server_port)
sock.bind(server)  # Start Socket on Server and bind address
print("Listening on " + server_address + ":" + str(server_port))

while True:
    print("---")
    payload, client_address = sock.recvfrom(1024)
    print("OK     <-Received: " + str(payload) + " From: " + str(client_address))
    RECV_BUFFER = str(payload)  # Save received data into buffer
    data = RECV_BUFFER.split(";")  # Create array split by ; ( eg. ;Arduino;Parameter;... )
    datalen = len(data)  # Get length of elements in data array
    if datalen == 4:
        device = str(data[1])
        device_cmd = str(data[2])
        if device == controller_name:  # If your controller (eg. Arduino) has connected
            controller_reg = True
            controller_address = client_address
            print("OK     ->Send Response to Controller at " + str(controller_address))
            if client_reg and device_cmd != "Keep-Alive":
                sent = sock.sendto(bytes("MSG-DEV: " + device_cmd, 'utf-8'), client_adr)
                print("OK     ->Controller confirmed command")
        sent = sock.sendto(b"MSG-SRV: Received!", controller_address)  # Send "M:MSG Received! to connected Controller
    else:
        if controller_address != "" and controller_reg == True:
            if datalen == 1:
                if "CMD:" in str(data[0]):
                    print("OK     ->Send Command to Controller (" + data[0] + ")")
                    sent = sock.sendto(bytes(data[0], 'utf-8'), controller_address)
                    sent = sock.sendto(bytes("MSG-SRV: CMD forwarded to " + controller_name, 'utf-8'), client_address)
                    client_reg = True
                    client_adr = client_address
                else:
                    sent = sock.sendto(b"Warning: Message received, but not forwarded to client!", client_address)
                    client_reg = True
                    client_adr = client_address
            else:
                print("OK     <-Received unknown Control Command")
        else:
            print("Error: Controller is not connected")
            sent = sock.sendto(b"Error: Controller is not connected", client_address)

Code Beschreibung

In diesem Absatz sind die einzelnen Befehlszeilen beschrieben:

1: Import die Socket Bibliothek von Python
3: Aktiviert den Server auf allen Netzwerken
4: Definiert den Port, auf dem UDP Datenpakete empfangen werden
5: Der Name, mit dem sich der Mikrocontroller als dieser Identifiziert (Arduino)
7,13,14: Startet den Socket Server
8-12: Reservierung von Variablen, die vom Code automatisch beschrieben werden
15: Ausgabe -> Server wurde gestartet
17-52: Dieser Teil des Programmes läuft durchgehend in einer Schleife
19: Wenn der Server ein UDP-Paket empfängt, werden daraus die Nutzdaten und Informationen zu IP und Port übergeben 1024 = maximale Paketgröße
20: Ausgabe -> Inhalt des Datenpaketes und Absender IP, Port
21: Empfangene Nutzdaten (Steuerbefehle) werden in einen Buffer geladen
22: Der empfange Befehlssatz wird durch ";" aufgetrennt und in ein Array (data) zerlegt
23: Die Anzahl der Elemente im Array (data) werden ermittelt. | Bsp: ;Controllername;Steuerbefehl; = 4 Elemente (0;1;2;3)
24: Wenn das Array "data" vier Elemente enthält, wird der Code bis einschließlich Zeile 34 ausgeführt. (Trifft nur zu, wenn der Controller ein Paket sendet)
25,26: Deklariert Controllername und Controllerbefehl aus dem Array (data)
27: Prüft, ob das Datenpaket vom Arduino kommt (Vergleich mit Zeile 5)
28, 29: Identifiziert den Controller und speichert seine Adresse. An diese Adresse werden alle vom Client empfangenen Steuerbefehle gesendet
32: Leitet die Rückantwort vom Arduino nach einem Steuerbefehl an den Client weiter
34: Bestätigt dem Controller den Erhalt vom empfangenen Datenpaket
35: Führt die folgenden Befehle nur aus, wenn sich der Arduino zuvor bereits verbunden hat
36: Führt die folgenden Befehle nur aus, wenn das Array nur ein Element enthält (Ist nur der Fall, wenn ein Befehl vom Client kommt)
37: Führt die folgenden Befehle nur aus, wenn im Datenpaket "CMD:" vorkommt (Bsp: CMD:LED=1 -> wird verarbeitet, LED=1 -> wird abgelehnt)
40: Sendet empfangenen Steuerbefehl vom Client an den Controller weiter
41: Informiert den Client über die Weiterleitung vom Datenpaket zum Controller
42,43: Identifiziert den Client und speichert seine Adresse. An diese Adresse werden alle vom Controller empfangenen Datenpakete weitergeleitet
44-47: Fehlerfall: Wenn der Steuerbefehl vom Client nicht "CMD:" enthält
50-52: Fehlerfall: Wenn ein Steuerbefehl an den Server gesendet wird, aber der Controller noch nicht verbunden ist

Oft gelesen

Anzeige:

Social Media

Newsletter