Telemetrie

Willkommen zurueck! Ich hoffe, dir hat Teil 1 dieser Serie gefallen und du bist nun gespannt auf mehr.
In diesem Teil greifen wir auf die Telemetriedaten zu, die jede Tello-Drohne sendet. Ein UDP-Listener ist erforderlich, um die eingehenden Pakete von der Drohne zu empfangen. Dieser Listener wird mit Apples Network-Framework implementiert. Die Implementierung des Listeners ist der Kernbestandteil dieses Tutorials.
Die fertige App sieht so aus.

Auf der linken Seite befinden sich die Steuerelemente zum Verbinden mit der Drohne, zum Versetzen in den Befehlsmodus und zum Ausfuehren einiger Flugaktionen wie Start und Landung. Diese Funktionen wurden in Teil 1 behandelt.
Die rechte Seite zeigt den neuen Inhalt dieses Tutorials: die Telemetriedaten, zum Beispiel:
- den Prozentsatz des aktuellen Akkustands
- die Flughoehe in cm
- die Lagewinkel: Pitch, Roll und Yaw
- Geschwindigkeiten entlang der x-, y- und z-Achsen
Der vollstaendige Code fuer dieses Tutorial kann hier heruntergeladen werden.
Das Tutorial ist in folgende Abschnitte gegliedert, entsprechend dem Datenfluss:
- Empfangen,
- Verarbeiten und
- Anzeigen
der Telemetriedaten.
Lass uns die Motoren starten und loslegen.
Empfangen der Telemetriedaten
Die Grundlage fuer den Empfang von Telemetriedaten ist ein UDP-Listener, der mit Apples Network-Framework implementiert wird.
Erstellen des Listeners
Alles basiert auf dem NWListener, der beschrieben wird als
“Ein Objekt, das Sie verwenden, um auf eingehende Netzwerkverbindungen zu lauschen.”
Beginnen wir mit der Instanziierung.
var listener: NWListener?
do {
listener = try NWListener(using:.udp, on: port)
} catch {
print("exception upon creating listener")
}Als Naechstes definieren wir das Verhalten des stateUpdateHandler. Er wird wie folgt beschrieben:
“Ein Handler, der Listener-Statusaktualisierungen empfaengt.”
listener?.stateUpdateHandler = {(newState) in
switch newState {
case.ready:
print("ready")
default:
break
}
}Akzeptieren neuer Verbindungen
Der Listener kann jetzt Statusaktualisierungen verarbeiten. Der interessante Teil beginnt, wenn eine neue Verbindung eintrifft.
Wir muessen einen weiteren Handler implementieren: den newConnectionHandler, den die Dokumentation beschreibt als:
“Ein Handler, der eingehende Verbindungen empfaengt.”
listener?.newConnectionHandler = {(newConnection) in
newConnection.stateUpdateHandler = {newState in
switch newState {
case.ready:
print("ready")
default:
break
}
}
newConnection.start(queue: DispatchQueue(label: "newconn"))
}Empfangen von Nachrichten
Wenn die Verbindung bereit ist, weisen wir einen Handler fuer receiveMessage zu, der “einen einzelnen Empfangs-Completion-Handler fuer eine vollstaendige Nachricht plant, im Gegensatz zu einem Bytebereich.”
connection.receiveMessage { (data, context, isComplete, error) in
// Decode and continue processing data
}Starten des Listeners
Alles ist bereit, um den Listener mit start zu starten. Er wird beschrieben als:
“Registriert sich zum Lauschen und legt die Warteschlange fest, in der alle Listener-Ereignisse zugestellt werden.”
listener?.start(queue:.main)Verarbeiten der Telemetriedaten
Dieser Abschnitt beschreibt die Klasse TelemetryPublisher der Beispielanwendung, die Apples Combine-Framework verwendet.
Von diesem Framework werden zwei Typen verwendet:
Der Published Property Wrapper:
Ein Typ, der eine mit einem Attribut markierte Eigenschaft veroeffentlicht.
Der PassthroughSubject:
Ein Subject, das Elemente an nachgeschaltete Subscriber sendet.
Wenn Data vom UdpListener empfangen wird, wird es an den PassthroughSubject weitergeleitet, der es in einen String dekodiert, um es weiter zu parsen.
Dieser String sieht so aus:
mid:257;x:0;y:0;z:0;mpry:0,0,0;pitch:0;roll:0;yaw:0;vgx:0;vgy:0;vgz:0;templ:76;temph:77;tof:10;h:0;bat:32;baro:531.77;time:0;agx:11.00;agy:-15.00;agz:-1002.00;
Dies wird aufgeteilt und in ein TelemetryData-Objekt geparst, das dann ueber den @Published Property Wrapper veroeffentlicht wird.
Der TelemetryPublisher muss ObservableObject konformieren.
“Ein Objekttyp mit einem Publisher, der sendet, bevor sich das Objekt geaendert hat.”
Dies ist besonders wichtig fuer den naechsten Schritt. Bisher haben wir die Telemetriedaten empfangen, geparst und einen Mechanismus zur Aktualisierung unserer Benutzeroberflaeche bereitgestellt. Jetzt muessen wir diesen Mechanismus in unserer UI verwenden, damit sie sich aktualisiert, wenn sich die Telemetriedaten aendern.
Anzeigen der Telemetriedaten
Unser ObservableObject aus dem vorherigen Schritt muss in der SwiftUI-Umgebung verfuegbar gemacht werden. In der Tutorial-Anwendung ist dies unsere ContentView-Klasse.
Wir werden den TelemetryPublisher als EnvironmentObject bereitstellen.
Ein Property-Wrapper-Typ fuer ein beobachtbares Objekt, das von einer uebergeordneten oder Vorfahren-View bereitgestellt wird.
Im Code ist das so einfach wie:
ContentView().environmentObject(telemtryPublisher)In der ContentView wird der TelemetryPublisher bereitgestellt mit:
@EnvironmentObject var telemetryPublisher: TelemetryPublisherJetzt muessen wir nur noch auf die Telemetriewerte zugreifen und sie in ContentView anzeigen, zum Beispiel:
Text("Battery: \(String(format: "%.2f", telemetryPublisher.data.bat))")Dank der deklarativen Natur von SwiftUI ist das alles, was wir tun muessen. Die ContentView wird automatisch aktualisiert, wenn sich die Telemetriedaten aendern.
Fazit
Wir sind fertig. Nach diesem zweiten Teil koennen wir die Drohne nicht nur steuern, sondern auch wertvolle Telemetriedaten empfangen, die uns ueber den Zustand der Drohne informieren.
Dies schliesst den zweiten Teil dieser Tutorial-Serie ab.
Der vollstaendige Code fuer dieses Tutorial kann hier heruntergeladen werden.