Guten Morgen,
für ein Firmware-Update müssen mehrere KByte Daten auf einen
Mikrocontroller transportiert werden. Als Kommunikationsschnittstelle
wird UDP eingesetzt. Meine jetzige Implementierung funktioniert so, dass
die Übertragung durch ein erstes Segment an Daten gestartet wird. Sobald
der Mikrocontroller ein Acknowledge Frame an den PC sendet wird
daraufhin gleich das nächste Segment versendet. Der Upload Vorgang läuft
nicht immer zuverlässig. Wie könnte man den Update-Vorgang stabiler
realisieren?
user schrieb:> Wie könnte man den Update-Vorgang stabiler realisieren?
Indem man herausfindet, weswegen es nicht stabil (Hardware und/oder
Software) läuft. UDP läuft eigentlich sehr zuverlässig, somit ist es
fraglich, ob ein eigenes Protokoll besser läuft.
user schrieb:> Ja da bin ich auch überfragt ob es an der Hardware oder Software liegt.
Damit gibt es jetzt für Dich 3 Möglichkeiten:
1) Dich in die Hard- und Software soweit einzuarbeiten, dass Du mit
geeigneten Werkzeugen (Oszilloskop, In-Circuit Emulator, etc.) den/die
Fehler findest.
2) Eine gute Glaskugel besorgen.
3) Das Projekt ohne Lösung beenden.
reines UDP kann per Definition keine zuverlässige Kommunikation
garantieren, dafür ist es nicht gemacht. Selbst wenn Du herausfindest,
warum es in deiner Combo Hard/Software nicht zuverlässig tut und das
fixt, wird die Kommunikation in anderen Netzwerken wieder haken.
Für zuverlässige Kommunikation ist TCP gemacht. Wenn Du zuverlässig
kommunizieren willst, aber auf UDP nicht verzichten willst oder kannst,
musst Du die Mechanismen von TCP zur verlässlichen Kommunikation
emulieren (also Paketsequenzierungen und retransmissions). Oder gleich
TCP benutzen.
Bei UDP kann es immer vorkommen das Telegramme nicht beim Empfänger
ankommen. Sei es durch Überlastung von zwischengeschalteten
Netzwerkswitchen, dem Empfänger/Sender (Betriebs)-system oder EMV
Störungen die die Übertragung stören. Das ist per Definition so. Braucht
man sichere Datenübertragung per UDP muss mann sich ein
Handshakeprotokoll ausdenken, bei dem jedes einzelne Telegram vom
Empfänger bestätigt werden muss. Der Empfänger muss dann natürlich auch
damit klar kommen Daten mehrfach zu empfangen, nämlich dann wenn seine
Bestätigung nicht beim Sender der Daten ankommt und dieser deswegen die
Übertragung wiederholt. Beispiele für solche Protokolle wäre das o.g
TFTP oder auch das Connectionless DCERPC. Alternativ könnte man den
gesamten Datenblob auch per Prüfsumme absichern und wenn diese falsch
ist die Übertragung einfach nochmal komplett wiederholen. Wäre in diesem
Fall eventuell einfacher.
UDP ist ein Hinausschreien in den Wald. Bedeutet irgendwas an Sicherheit
muss selbst gemacht werden. Das Protokoll muss zustandsfrei sein. Also
nicht ein Block kommt nach dem Anderen, mit autoincrement, sondern die
Bloecke werden adressiert uebertragen. Die Quitierung, nach einem Block,
inkl. CRC, enthaelt die Blocknummer. Dann ist auch klar welcher Block
nochmals gesendet werden muss. Das Protokoll sieht also irgendwie so aus
:
PC : SendFirmware, Ver# 1234, len=1234, #bloecke=25, CRC
uC : ok, SendFirmware, Ver# 1234, CRC
PC : SendBlock=1, len=40, CRC
uC : ok, SendBlock=1, CRC
PC : SendBlock=2, len=40, CRC
uC : ok, SendBlock=2, CRC
Wie andere schon gesagt haben, ist UDP für sowas nicht gedacht. Vorallem
da du auch noch das System mit dem ack nutzst und Pakete hintereinander
abschickst, bringt dir das nicht mal einen Geschwindigkeitsvorteil
gegenüber anderen Protokollen (bei z.B. Multiplayer games wird aufgrund
der Gewchwindigkeit oft zum Teil auf UDP gesetzt).
Wenn du nach TFTP Lizenz googlest, findest du ein paar Open Source
Implementationen, daher würde ich davon mal ausgehen.
Alternativ mal TCP anschauen.
Ich würde einfach das Programm dahingehend ändern, dass Du auf der
PC-Seite nicht stumpf auf ein ACK vom µC wartest, sondern auch erfasst,
wie lange er auf das ACK wartet. Dann nach z.B. 100 ms abbrechen und den
aktuellen Block wiederholen. Nach ein paar erfolglosen Versuchen noch
das Senden mit Fehlermelsung beenden. Dazu ist es wichtig, die aktuelle
Blocknummer mit zu übertragen. Auf der µC-Seite noch den zuletzt
geschriebenen Block merken und bei Wiederholung einfach ignorieren.
Wegen ein paar kb muss man keinen TCP-Stack oder ein komplettes TFTP
implementieren.
Ich habe nun meine Applikation auf einem Microcontroller um eine TCP
Verbindung erweitert. Nun kann ich per UDP und auch per TCP auf den
Mikrocontroller zugreifen. Wie würde dies nun genauer aussehen, wenn nun
größere Datenmenegen via TCP an den Mikrocontroller versendet werden?
user schrieb:> Kann über TCP sichergestellt werden wenn zum Beispiel 500 Frames a 500> Bytes übertragen werden das diese Daten auch alle ankommen?
Was TCP sicherstellt ist Folgendes:
Voraussetzung: Eine Verbindung ist etabliert (also asymmetrisches Open -
listen/accept() auf Serverseite und connect() auf Clientseite - ist
erfolgreich abgeschlossen).
Du schiebst von der einen Seite x (z.B. 500) bytes mit send() Aufrufen
in die Verbindung. TCP garantiert, dass Du auf der Anderen Seite mit
recv() Aufrufen genau diese x bytes in genau der Reihenfolge
zurückliest, solange die Verbindung nicht in der Zwischenzeit
geschlossen wird. Exoten wie Out-Of-Band Data seien hier mal
vernachlässigt. TCP stellt sicher, dass keine Daten verloren gehen oder
im Datenstrom ihre Positionen wechseln.
Im Nutzdatenstrom von TCP hat zunächst der Begriff des "frames" keine
Bedeutung. In TCP gibt es einen "Stream," dessen innere Struktur TCP
nicht interessiert.
TCP entbindet Dich nicht davon, ein Protokoll auf höherer Ebene zu
spezifizieren und zu implementieren. TCP ist inherent Voll Duplex, d.h.
beide Seiten können unabhängig voneinander und asynchron jederzeit Daten
senden. OB die das tun, hängt davon ab, welches Applikationsprotokoll
über TCP realisiert ist. In vielen Fällen ist das darüberliegende
Protokoll Halbduplex, d.h. die beiden Partner erwarten eine gewisse
Ordnung der Daten und wechseln sich in einem PingPong Spiel mit
Datensenden und -bestätigen ab. Das muss aber nicht sein.
TCP garantiert keine Maximallatenz zwischen Senden und Empfangen
derselben Daten.
Programmtechnisch ist das Arbeiten mit TCP relativ einfach. Du brauchst
im Kern nur send() (~write) und recv() (~read) mit einem socket
(~Verbindungskennung, die nach Verbindungsaufbau geliefert wird) als
Parameter. Dafür gibt es unendlich viele Beispiele im Netz.
Es gibt aber zur Realisierung von stabilen Kommunikationen ein paar
Fallstricke zu beachten. Z.B. machen nach wie vor erstaunlich viele
Leute den Fehler, die Returnwerte von send() und recv() zu ignorieren
und damit ihre Kommunikation zum Stolpern zu bringen. Ausserdem sollte
jedes High Level Protokoll mit Timeouts abgesichert sein, um z.B. zu
verhindern, dass ein Bug im Protokoll des peers die einene Seite der
Kommunikation einfriert. Auch sollte das Protokoll über TCP einigen
Anforderungen genügen (z.B. bei variabel langen Paketen die Paketlänge
im header enthalten). Im Kapitel 7 meines Buches widme ich mich den
Details relativ ausführlich.
TCP ist nicht so einfach, bei vollständig implementiertem IP Layer ist
es aber schon recht sicher. UDP kann ja da so ziemlich nichts. Ich würde
trotzdem UDP nehmen und Redunanz einbauen, damit möglichst wenig schief
geht. Manche Router machen z.B. bei USP komische Sachen!
Wichtig ist in jedem Fall genaue Prüfungen der empfangenen SW bevor
irgendwas geflascht wird. Auch golden image handling wird benltigt.
Und wie gesagt, einiges an Redunanz auf dem Zuführkanal, weil sonst zu
oft was schief geht. Wir haben gerade so ein nettes Problem. Es ist kaum
was rein oder rauszubekommen von und ins Gerät.
> Welche Hardware, welche Software?
Beim ENC28j60 ist im Netz viel über Hardware-Probleme gerätselt worden,
die mit zusätzlichen Kondenstoren gelöst werden sollten. Ich hatte diese
HW-Probleme auch. Mit Verwendung geänderter Software waren sie weg.
Paul schrieb:> UDP läuft eigentlich sehr zuverlässig, ...
UDP garantiert für überhaupt nichts. Der Empfänger eines Blockes kann
nur entscheiden, ob es den Block richtig empfangen hat, aber es ist bei
UDP völlig unklar, ob alle Blöcke beim Empfänger ankommen. Wenn es auf
sichere Übertragung ankommt, muss eine Protokollschicht oberhalb von UDP
dafür sorgen.
Da kann man mit relativ wenig Aufwand sich selber etwas ausdenken, ohne
gleich ein komplexes Protokoll komplett neu zu erfinden - aber UDP
alleine reicht für die Aufgabe nicht.
user schrieb:> Ok vielen Dank für eure Beiträge.>> Gibt es das TFTP Protokoll quasi als open source Software ?
TFTP ist in RFC-1350 definiert (mit ein paar optionalen Erweiterungen
RFC-2347, 2348, 2349), es gibt etliche freie Implementierungen für
Server und Client.
TFTP ist so simpel gestrickt daß man in 2 Stunden mit nichts als der
nackten socket API selber einen vollständigen Server zusammengeklöppelt
hat, das hab ich am Donnerstag Nachmittag für mein Update-Tool gemacht,
die paar Dutzend Zeilen Code schnell selber hinzuschreiben ging unter
dem Strich schneller als lang herumzugoogeln um einen der vielen
existierenden Server auszusuchen und dann so mit meinem eigenen Programm
zu verheiraten daß es aussieht als wäre alles aus einem Guss.
Im Prinzip wird einfach ein Block an Daten als ein UDP Paket geschickt,
im 4 Byte langen Header stehen 2 Byte Sequenznummer und 2 Byte
Datenlänge, jedes Paket muss vom Empfänger mit einem ACK-Paket quittiert
werden, der Sender wartet nach jedem Block auf das ACK bevor er den
nächsten sendet. Es gibt einen Timeout, dann wird das letzte Paket mit
der letzten Sequenznummer einfach nochmal gesendet, so oft bis endlich
ein ACK kommt. Das Ende wird markiert indem ein Block gesendet wird der
weniger Daten als die maximale Blocklänge enthält. Das ist schon alles.
Bernd K. schrieb:> ... der Sender wartet nach jedem Block auf das ACK bevor er den> nächsten sendet.
Bei einer längeren Datenübertragungskette macht das die Sache unnötig
langsam. Der Sender könnte den nächsten Block durchaus schon vor
Eintreffen des ACK senden und muss nur buchführen, welche Blöcke schon
bestätigt sind. Dann kann immer noch ein Timeout greifen, ohne bei jedem
Block die Warterei einzubauen.