Grüße, meine Lieben!
Irgendwann in ihrem Leben hört jede hartnäckige DIY-Box auf, den Kantian Arduino als „Dinge an sich“ zu verpassen, die
sie einfach nicht können! : Das Blinken der LED, das Übertragen von Daten von den Sensoren über das Kabel zum PC macht sicherlich Spaß, aber der heilige Gral ist in der Mobilität, in der Befreiung von den „Kupferbindungen“, in der wahren Freiheit unter den Wellen des universellen Äthers.
Hier eröffnet sich uns die harte Realität von instabilen Kommunikationskanälen, Übertragungsfehlern und nicht zugestellten Nachrichten.
Gott verbietet es, auf diesem Gebiet Originalität zu behaupten: Die Menschheit hat lange Zeit eine ganze Reihe von Protokollen für alle Gelegenheiten verwendet.
Aber unser Ziel ist es zu lernen, und da ich ein begeisterter Unterstützer der Aufklärung im Kampf bin, werden wir unser eigenes Protokoll „Fahrrad“ erfinden.
Heute schlage ich vor, ein Protokoll zu entwickeln, das die garantierte Zustellung, Integrität und Abfolge von Nachrichten zwischen zwei Teilnehmern (Punkt-zu-Punkt, Punkt-zu-Punkt) gewährleistet, weiß, wie und wendet den
Nagle- Algorithmus und das
Protokoll-Pipelining an , was auch immer dies bedeutet. Gleichzeitig sollte es einen minimalen
Overhead haben und sich sogar in den beengten Arduino UNO pressen.

Ich bitte alle, die sich an Bord dafür interessieren, die Luken zu schließen, die Königssteine zu öffnen, die Ballasttanks zu füllen. Wir haben einen Ausflug in die Vergangenheit, Ziel: Jahr 1974!
Für die Ungeduldigen (ich selbst bin so!)Hier ist das Github-Repository, in dem die Implementierungen sind:
Nach der guten alten Tradition beschäftigen sich mindestens zwei anerkannte Experten auf diesem Gebiet mit der Beschreibung von kryptografischen Algorithmen und Protokollen. Wenn jemand sie nicht kennt, machen Sie sich mit ihnen bekannt:
Und
Zunächst beschreiben wir eine einfache Aufgabe
Alice und Bob sitzen in benachbarten Schützengräben und können ihren Kopf nicht heben, um sich zu sehen. Sie können nur mit Stimme sprechen, daneben pfeifen Kugeln und Granaten, die ihre Schreie übertönen, und außerdem müssen Sie schreien, wenn einer von ihnen spricht, damit Sie überhaupt nichts hören.
Die Situation wird durch die Tatsache erschwert, dass sie von Feinden belauscht werden - und Sie müssen eine Codesprache verwenden, die aus irgendeinem Grund aus langen Folgen von Zahlen besteht.
Da sowohl Alice als auch Bob Menschen sind, müssen sie regelmäßig zum Essen oder zur Toilette gehen, und sie sind so ungeduldig, dass sie im ungeduldigsten Moment ungeduldig sein können!
Wie und warum eine Verbindung herstellen?
Wie können wir in einer so bedrückenden Situation, in der alles zum Scheitern verurteilt zu sein scheint, eine zuverlässige Datenübertragung veranlassen?
Die erste Lösung, die in den Sinn kommen kann, besteht darin,
Stoppwort- Codephrasen zu verwenden, um die Übertragung zu starten und zu beenden.
Sagen wir mal, wenn Alice eine Nachricht senden möchte, muss sie "Beginne die Übertragung!" Rufen und warten, bis Bob mit "Beginne den Empfang!" Antwortet.
Wenn Alice nicht auf Bobs Antwort wartet, wiederholt sie einfach ihre Aufforderung, die Übertragung zu starten. Natürlich sollten Sie dies nicht zu oft tun, sonst hören Sie, wie wir wissen, nur Bobs Antwort nicht.
Großartig. Aber was passiert, wenn Alice als Antwort vom nächsten Graben hört: "Beginne die Übertragung!"?
Es stellt sich heraus, dass Bob sich auch dazu entschlossen hat, jetzt einige wichtige Informationen zu übertragen. Alice hat einen milden Charakter und sie hätte denken können: "Okay, ich werde warten, meine Nachricht ist im Prinzip nicht dringend, lass Bob sie zuerst weitergeben." Sie denkt darüber nach und antwortet: "Beginnen Sie mit dem Empfang!".
Da
in Kriegszeiten der Sinuswert vier erreichen kann, ist die Schallgeschwindigkeit endlich und es dauert einige Zeit, um zu verstehen, was Alice und Bob gehört haben, und selbst Bob, als Gentleman, kann sich entscheiden, der Dame nachzugeben, zuckt er mit den Schultern und schreit: "Ich fange an zu empfangen!"
Um die Empörung zu veranschaulichen, werden wir Zeitdiagramme verwenden. Die Zeit vergeht auf sie.
Der Fall, in dem Alice und Bob sich nicht rechtzeitig einig waren:

Der Fall, als die Nachricht verloren ging:

Das ist ein Fiasko. Alles wird zu verwirrend und wird durch die Tatsache verschlimmert, dass der Adressat einen der Sätze entweder hören oder nicht hören kann, und in jedem Fall weiß der Gesprächspartner nicht, ob seine Nachricht vom Adressaten gehört wurde.
Nun erwarten sowohl Alice als auch Bob ein Willkommen. Es wäre logisch zu erkennen, dass ein Konflikt aufgetreten ist und jemand die Übertragung fortsetzen muss. Aber was ist, wenn alles auf eine neue Art und Weise wieder passiert? Und hier sind wir wieder, wo wir angefangen haben.
Wenn Sie der Meinung sind, dass die Situation äußerst selten ist, denken Sie daran, wann Sie das letzte Mal über Sprache mit jemandem gesprochen haben, wenn Ihr Teilnehmer oder Sie (oder beide) eine langsame Internetverbindung haben. "Hallo hallo hallo, du verschwindest." "Sie können hallo hallo nicht hören."
Während sich die Lage in Schützengräben erwärmt, fordern die Kommandeure die Übermittlung von Berichten.
Es ist Zeit
, sich den primären Quellen zuzuwenden: Um Marx zu studieren, wird Engels vor mehr als 40 Jahren zurückkehren und sehen, wie solche Probleme von
DEC- Ingenieuren bei der Entwicklung des
DDCMP- Protokolls gelöst wurden.
Laut den Entwicklern von DDCMP müssen Alice und Bob Emotionen ablehnen und wie
endliche Zustandsmaschinen werden .
Dies bedeutet, dass unsere Alice und Bob von nun an nur noch wenige feste Zustände haben. Übergänge zwischen diesen Zuständen können streng nach bestimmten Regeln erfolgen, wenn bestimmte Ereignisse eintreten.
Zuerst listen wir einfach die Zustände auf:
- Angehalten
- ERSTER START
- ANERKANNTER START
- LAUFEN
Wie Sie sehen, gibt es nur vier davon. Und jetzt, egal was passiert, weiß jeder der Abonnenten mit Sicherheit, dass sich sein Gegenüber in nur einem dieser Zustände befindet. Mit Blick auf die Zukunft werde ich sagen, dass fast immer ein Teilnehmer weiß, in welchem Zustand sich der zweite Teilnehmer befindet,
aber dies ist nicht korrekt .
Betrachten wir die Zustände im Detail getrennt
HALTED ist der einfachste Zustand, niemand geht irgendwohin, jeder bleibt an seinem Platz, nichts wird gesendet und nicht empfangen, irgendwelche äußeren Reize werden ignoriert. Alle bis auf einen - der Wille der höheren Behörden. Im ursprünglichen DDCMP-Protokoll kann der Übergang vom
Status HALTED nur auf
Anforderung des Benutzers in den
Status INITIAL START versetzt werden. Alice oder Bob erhalten den Befehl, eine Verbindung herzustellen.
Was passiert, wenn Alice oder Bob eine solche Bestellung erhalten?
Sie stellen sofort fest, dass sich der Zustand von
HALTED zu
INITIAL START geändert hat. Dieser Übergang beinhaltet, wie jeder andere, eine genau definierte Abfolge von Aktionen. In diesem Fall müssen Sie „DO IT!“ Rufen und die Uhr auf die Uhr stellen. Das ist alles.
Also schrie Alice, was von ihr verlangt wurde, und drückte einen Knopf auf der Stoppuhr. Um zu verstehen, was von Bob zu erwarten ist, werden wir herausfinden, was mit Alice passieren kann, wenn sie sich im
INITIAL START- Zustand befindet.
- Von dem Moment an, als Alice bemerkte, dass die Zeit vergangen ist, sagen wir 10 Sekunden, und sie hat keine Reaktion von Bob gehört (beachte, ich sage nicht, dass Bob ihr nichts geschrien hat - das ist nicht bekannt, aber nur, dass Alice nichts weiß Alice ist eine weise und rationale Frau und verlässt sich ausschließlich auf Fakten. Wir nennen dieses Ereignis ein Timeout - das Wartezeitintervall wurde überschritten. In diesem Fall fordert uns das Protokoll auf, Folgendes zu wiederholen: Rufen Sie „DO IT ONCE!“ Und wiederholen Sie die Zeit. Noch nicht dick.
- Wenn Alice hörte, dass Bob dasselbe schrie - "DO ONE TIME!", Dann geht Alice
nicht selektiv in den Zustand
ACKNOWLEDGED START über , über den sie sofort "DO TWO!" Rufen und die Uhr erneut einstellen sollte.
- Wenn Alice von Bob "DO TWO!" Hört, geht sie sofort in den Zustand
RUNNING (!) Und ruft "ACCEPTED NOOOOOL!". Wenn ihre Stoppuhr gestartet wurde, schaltet sie sie vorsichtshalber aus.
Es ist sehr wichtig, keine unnötigen Bewegungen auszuführen, die vom aktuellen Status nicht vorgesehen sind. Was auch immer Bob weint, egal wie fluchend oder bettelnd, Alice reagiert nur wie vereinbart.
Solche Dinge werden bequem in einer Tabelle dargestellt.
Beginnen wir also mit den bereits beschriebenen Zuständen
HALTED und
INITIAL START , und dann werden wir die Tabelle weiter auffüllen.
Ich lasse bewusst einige Punkte aus der ursprünglichen Beschreibung von DDCMP weg - wir brauchen sie nicht, wir wollen DDCMP nicht nur wiederholen, sondern auf
der gleichen Basis
nur ein weiteres neues Protokoll aufbauen.
Aber zurück zur Beschreibung von Zuständen und Übergängen. Der nächste Status ist
BESTÄTIGTER START .
In diesem Zustand ist alles, was Alice oder Bob Sorgen machen kann:
- Nach Ablauf der Wartezeit, in diesem Fall müssen Sie im selben Zustand bleiben, schreien Sie "DO TWO!" und starten Sie den Timer erneut
- Das hörbare "DO TWO!" wird in den Status "
RUNNING " übersetzt, während Sie "ACCEPTED NOOOOL!" rufen. Halten Sie den Timer an.
- das gehörte "DO IT!" geht in den gleichen Zustand, Sie müssen "DO TWO!" rufen und den Timer starten;
- "NOOOL ACCEPTED!" gehört - Übergang in den Zustand
RUNNING , Timer anhalten.
Wir haben all das in einen Tisch gelegt.
Mit einem Handschlag ist fast alles fertig - es bleibt nur ein
RUNNING- Status zu berücksichtigen, da einer der Teilnehmer bereits darauf zugreifen kann, und der zweite - sofort zur Toilette eilen und bei seiner Rückkehr alles vergessen und versuchen, eine neue Verbindung herzustellen.
Aus der Sicht des Handshake-Verfahrens (wir beschäftigen uns noch nicht mit der Datenübertragung, für die alles gestartet wurde - dies ist eine separate Geschichte) im Zustand
RUNNING sind wir an zwei Ereignissen interessiert:
- Wenn sie zu uns "DO IT ONCE!" rufen - ist alles sehr schlecht, es ist eine vollständige Desynchronisation, alles muss neu gestartet werden. Das ursprüngliche Protokoll
weist Sie an, einfach in den
Status HALTED zu wechseln . Aber das wird uns in keiner Weise helfen - wenn dies aus irgendeinem Grund auf einem autonomen Arduino passiert ist, der einige Daten von einigen Sensoren überträgt, dann ist dies für uns ein völliger Fehler. Wie wir wissen, können Sie von
HALTED nur auf Anordnung der Behörden zu
INITIAL START gehen.
Aus diesem
Grund ändern wir hier das Protokoll: Der Empfang des
Befehls "DO ONCE!"
Im Status "
HALTED " sollte wie eine behördliche Anweisung funktionieren - d. H.
Schalten Sie in den Status INITIAL START , rufen Sie „DO IT ONCE!“ und starten Sie den Timer. Darüber hinaus ist es in einigen Fällen zweckmäßig, einen Befehl zum Aufbau der Kommunikation zu erteilen, nachdem Sie sich mit Strom versorgt haben.
Daher wird im ungünstigsten Fall die Verbindung einfach zurückgesetzt.
- das zweite Ereignis, auf das im Zustand
RUNNING reagiert werden muss - wenn aus einem benachbarten Graben „DO TWO!“ zu hören ist. Das ist schon interessanter. In diesem Fall müssen Sie „ER AKZEPTIERT!“ Rufen. Mit ER ist die Anzahl der Nachrichten gemeint, die in der aktuellen Kommunikationssitzung erfolgreich empfangen wurden. Dies ist ein neues Konzept. Im Folgenden werden wir alles genauer betrachten, aber vorerst werden wir alles, was wir gelernt haben, auf den aktuellen Moment in eine Tabelle bringen:
Wenn Alice und Bob sich strikt an das Protokoll halten, haben sie nur keine Möglichkeit
, in etwas
Unverständliches zu geraten , außer wie sie eine Verbindung herstellen, gemeinsam in den Status
RUNNING wechseln oder im schlimmsten Fall versuchen, sie herzustellen, bevor auf den Sieg
geklickt wird .
Ein aggressiver Leser kann versuchen, alle Optionen zu sortieren und zu dem Schluss kommen, dass sich die Reihe von Zuständen und Übergängen als geschlossen und streng bestimmt herausstellt. Wir (mit Hilfe der Köpfe der DEC-Ingenieure) haben Alice und Bob nun mit einer Reihe von Regeln verbunden, die einfach durch Befolgen der Regeln eine Verbindung herstellen, wenn dies unter den gegenwärtigen Bedingungen im Prinzip möglich ist.
Wie übertrage ich jetzt Daten?
Okay, das war ein schönes Training. Candy-Bouquet-Periode in der Beziehung zweier Netzwerkknoten. Denken Sie daran, dass wir ein Unternehmen gegründet haben: Wir müssen Daten mit garantierter Zustellung und Priorität übertragen! Mit Disaster Recovery. Soweit Hardware-Ressourcen dies zulassen (Alice und Bob können sich als schwache 8-Bit-Controller mit 2 Kilobyte RAM herausstellen!).
Die DEC-Ingenieure lehren uns, dass die Nachrichten, die wir nummerieren müssen, gezählt werden müssen, wie viel wir gesendet haben, wie viele wir empfangen haben und wie viele der gesendeten Nachrichten den Empfänger erreicht haben.
Es ist Zeit für einen Exkurs!Gib es zu. Als ich die Namen der Variablen in der Beschreibung des DDCMP-Protokolls sah, entschied ich, dass es kein Zufall war: Amerikaner ziehen sehr gerne schöne Abkürzungen an ihren Ohren an.
Für unsere Bequemlichkeit gibt es sogar mehrere Ressourcen, bei denen Interessierte das Schöne berühren können.
Mein Favorit ist dieser -
dumme oder übermäßig erzwungene astronomische Abkürzungsseite (oder DOOFAAS)Was sind diese Erfindungen wert!
Hier ist ein Beispiel:
WASP - Wideband Analog SPectrometer (Aber nicht das, was Sie gedacht haben!)
SAURON - Spektroskopische
Flächeneinheit zur Erforschung optischer Nebel
CISCO - Cooled Infrared Spectrograph und Camera for OHS (Das ist es also!)
Und hier nur Feuer:
SQUIRT (oh ja, Artikel 18+!) - Satettile QUick Research Testbed
SHIT (Weder mehr noch weniger!) - Super Huge Interferometric Telescope mit der Aufschrift „Look for yourself“, zu der der Link zur Zusammenfassung an den gleichnamigen Artikel angehängt ist.
Daher werden die Variablen, die die Anzahl der empfangenen, gesendeten und zugestellten Pakete auf dem Knoten in der ursprünglichen Beschreibung des Protokolls angeben, als
RNA bezeichnet .
Ah, warum haben sie das Protokoll nicht so genannt - RNA! Eine Art RNA-Netzwerk. DECnet-Protokolle hatten jede Chance, zu Internetprotokollen zu werden, wenn die Geschichte anders ausgefallen wäre.
Aber zurück zu unseren Schützengräben
Der ursprüngliche Protokollstandard definiert, dass alle Zähler 8-Bit und Inkrement Modulo 256 sind. Dies bedeutet, dass maximal 256 Nachrichten gesendet werden können, für die noch keine Bestätigung eingegangen ist.
Und wenn keine Bestätigung eingeht, müssen sie möglicherweise erneut übertragen werden, und wenn dies erforderlich ist, müssen sie bis zur Bestätigung gespeichert werden. Immerhin haben wir die Lieferung garantiert!
Die körperlichen Parameter unserer Alice und Bob diktieren uns unterschiedliche Bedingungen. In 8-Bit-Arduino kann diese Datenmenge einfach nicht gespeichert werden, und wir müssen Kompromisse eingehen. Und ich spreche nicht von der Tatsache, dass in der Norm die Länge von Paketen (Nachrichten) in Bytes auf eine 16-Bit-Zahl begrenzt ist, d.h. 64 Kilobyte sind ein unzulässiger Luxus!
Damit ist die Verbindung hergestellt. Was weiter?
In dem Moment, in dem Alice oder Bob
in den Zustand
RUNNING wechseln , werden die Zähler zurückgesetzt.
Wie ich bereits erwähnte, beinhaltet das ursprüngliche Protokoll die Nummerierung von Nachrichten modulo 256, aber wir müssen diese Nummer reduzieren, um der geringen Speicherkapazität in Arduino-ähnlichen Dingen zu entsprechen.
Um sofort alle Inkremente von Zählern einschränken zu können, werden wir eine bestimmte Konstante UMCP_PACKETS_NUMBER einführen, und nun werden alle Inkremente in diesem Modul auftreten.
Wenn Sie UMCP_PACKETS_NUMBER = 8 nehmen und die maximale Paketgröße UMCP_PACKET_DATA_SIZE ist - die Teile der zu einem Zeitpunkt übertragenen Daten sind auf 64 Bytes begrenzt, dann passt alles in das Arduino UNO und bleibt ein bisschen für die Benutzeranforderungen.
Es ist wichtig zu bedenken, dass diese beiden Parameter für beide Parteien gleich sein müssen.
Wenn Alice und Bob nun erfolgreich eine Verbindung hergestellt haben und einer von ihnen Daten übertragen muss, müssen die Daten offensichtlich zunächst in Teile mit einer Größe von maximal 64 Byte aufgeteilt werden, und zweitens muss jedes Paket auch einen Status enthalten Zwei Absenderzähler: Die Anzahl der empfangenen und gesendeten Nachrichten (R und N).
Sehen Sie, wie einfach es jetzt ist, die sogenannten zu organisieren Pipelining und wie einfach es ist, mit Fehlersituationen umzugehen!
Wenn Alice gleich nach dem Verbindungsaufbau 3 Pakete hintereinander sendet, wird für alle der Zähler R auf 0 gesetzt (sie hat noch keine Pakete erhalten), und der Zähler N wird mit jedem neuen Paket um eins erhöht.
Wenn Bob alle erfolgreich akzeptiert, reicht es aus, nur für das letzte Paket eine Bestätigung zu senden, um den Empfang aller drei Pakete zu bestätigen. Wenn er lediglich den Status seiner Zähler R = 3 und N = 0 zurücksendet, wird Alice sofort verstehen, dass alle gesendet wurden Ihre Nachrichten erreichten den Adressaten.
Es war ein idealer Fall, in dem keine höhere Gewalt auftrat. Schauen wir uns nun an, was möglicherweise schief gehen könnte und wie Sie damit umgehen können.
Wenn Bob aus irgendeinem Grund das erste Paket überspringt und eines der nächsten annimmt, macht er sofort darauf aufmerksam, dass der Zähler N (die Anzahl der von Alice gesendeten Pakete) den Zähler R auf Bobs Seite deutlich überschreitet und Bob leicht erkennt, dass er das erste Paket verpasst hat . In diesem Fall muss er nur den flachsten Captain Evidence spielen und Alice den Status seines Zählers der empfangenen Pakete mitteilen (R = 0). Alice versteht gleichzeitig, dass sie N = 3 ist und Bob R = 0 hat, das heißt, dass es notwendig ist, Pakete auf eine neue Art und Weise zu übertragen, beginnend mit der ersten.
Wenn Sie sich dieses Schema genau ansehen, können Sie feststellen, dass bei jeder Übertragung des Status seiner Zähler durch einen Abonnenten sofort das Ergebnis der Übertragung von Datenpaketen angezeigt wird und die Differenz zwischen dem auf der einen Seite gesendeten und dem auf der anderen Seite empfangenen Zähler angibt, wie viele Pakete verloren gegangen sind und von welcher Nummer es angefangen hat.
, A R , «» .
, , ().
RNA SACK SPEP. , (Send ACKnowledgement), — (Send REPly to a message).
, DDCMP — SNAK (Send Negative AcKnowledgement). - . , , , — .
, .
- . - . Und das ist die Wahrheit. .
.
, — , . , , L, (, — R).
, . , .
DDCMP, , SELECT — ( ) «» .
, , .
: SELECT. : , , — .
ACK REP . . «» , «», . , , , . ( ?).
, , — . — SELECT.
.
.
, !
Message Framing — .
, .
1. , R N . Arduino 8 . , , 4-.
:
= (RL & 0x0F) | (NL << 4);
Und wir werden den Status der Zähler wie folgt lesen:
NR = (c >> 4) & 0x0F; RR = c & 0x0F;
c - entsprechendes Byte aus der Nachricht
2. Wir erinnern uns auch, dass jede Nachricht den Status des SELECT-Flags enthalten muss. Die verschiedenen Arten von Nachrichten selbst sind:
Das heißt, nur 6 verschiedene Arten von Nachrichten. Alle Nachrichten mit Ausnahme von DTA geben das SELECT-Flag "frei" - sie benötigen eine sofortige Antwort vom entfernten Teilnehmer, und ohne das Flag kann er es nicht senden. Die DTA-Nachricht gibt kein Flag zurück, um Pipelining zu ermöglichen.
Im Allgemeinen haben wir genug 3 Bits für den Nachrichtentyp, aber um nicht mit den Bits zu verwechseln, weisen wir dem Typ ein ganzes Byte zu - im Falle einer Revision haben wir eine gewisse Handlungsfreiheit.
Wenn die Nachricht Daten enthält, müssen wir deren Menge und Prüfsumme übertragen. Da die maximale Paketgröße 64 Byte beträgt, nehmen wir auch ein Byte für die Prüfsumme und für die Länge - plötzlich müssen Sie die Paketgröße erhöhen.
3. Wir benötigen auch eine Signatur des Nachrichtenanfangs und eine separate Prüfsumme für den Header.
In diesem Sinne sieht der Header (auch als Kontrollmeldungen bezeichnet) folgendermaßen aus:
Und der Datenblock sieht so aus:
Das ist alles Dies ist eine vollständige Beschreibung des Protokolls, das wir von DDCMP erhalten haben.
Jetzt können Sie die Implementierung durchführen.
Wie ist es aufgebaut und wie benutzt man es?
Zunächst ein wenig über die Struktur des Repositorys.
Wie eingangs erwähnt, liegt der Projektcode auf dem Github:
uMCPInoUm zu sehen, wie alles funktioniert, können Sie eine
Testanwendung auf einem PC ausführen.
Führen Sie im Archiv "uMCPIno_Test.exe" aus, wählen Sie den gewünschten COM-Port aus und testen Sie die Funktionsweise.
Sie können ein Paar virtueller COM-Anschlüsse überprüfen (ich mache dies normalerweise).
Warum können Sie zwei Kopien der Anwendung ausführen. Vergessen Sie nur nicht, in einer Kopie "AUSGEWÄHLT NACH STANDARD" zu aktivieren - dies ist der Master, und in der anderen - deaktivieren Sie ihn. Übrigens, wenn Sie interessiert sind, können Sie sehen, was passiert, wenn Sie diese Regel nicht einhalten =)
Mit der Option EXTRAS können Sie alle Gedankenbewegungen im Gehirn des Protokolls anzeigen. Alle Änderungen des Zustands der SELECT-Flags, Ereignisse von Timern, Änderungen des Zustands des Knotens sowie die Werte der Variablen R und N in den gesendeten und empfangenen Nachrichten werden angezeigt.
Ich verbinde meinen Arduino UNO über einen UART <-> USB-Konverter mit meinem Laptop. Mit den Stiftleisten können Sie jederzeit einen Zeilenumbruch simulieren:

Wenn Sie die Anwendung jetzt auf dem Laptop ausführen, stellt das Arduina nach dem Drücken der Taste "CONNECT" eine Verbindung her:

Und so reagiert das System auf den Versuch, durch eine "zerrissene" Leitung zu senden:

So binden Sie uMCPIno in Ihre PC-Anwendung ein:
- Das Repository verfügt über eine uMCPIno-Bibliothek. Verbinden Sie es mit den Referenzen Ihres Projekts
- Es enthält die Klasse uMCPInoPort. Wir erklären seine Instanz:
uMCPInoPort port; port = new uMCPInoPort("COM1", UCNLDrivers.BaudRate.baudRate9600, true, 8100, 2000, 64, 8);
Parameter in der Reihenfolge: Portname, dann Portgeschwindigkeit, Standard-SELECT-Status, Intervall für SELECT, Timeout-Intervall, Paketgröße und die maximale Anzahl nicht bestätigter Nachrichten.
- Veranstaltungen abonnieren:
wenn sich das SELECT - port.Select Flag ändert:
OnSelectChangedEventHandler
wenn sich der Zustand ändert - port.State:
OnStateChangedEventHandler
Der Remote-Host bestätigt den Erhalt des Codes:
OnDataBlockAcknowledgedEventHandler
wann kommt das datenpaket an
OnDataBlockReceivedEventHandler
- Öffnen Sie vor der Arbeit den Anschluss
port.Open();
- Um Daten zu senden, rufen wir die Methode auf:
port.Send(byte[] data);
- Nach Abschluss schließen Sie den Hafen:
port.Close();
Senden Sie einfach zwei Bytes!
Fahren wir nun mit der Implementierung für Arduino fort. Zwei Beispiele befinden sich im Ordner
github.com/AlekUnderwater/uMCPIno/tree/master/ArduinoDas erste ist nur ein Konverter von und nach uMCP. Die erste serielle Schnittstelle dient zur Kommunikation mit dem Host und die serielle Schnittstelle 1 (sofern sie sich auf Ihrer Platine befindet) oder die Software-serielle Schnittstelle an den Anschlüssen 2 und 3 zur Kommunikation mit einem anderen uMCPIno-Knoten. Hier können Sie Bluetooth oder ein Funkmodul anschließen.
Die zweite ist eine Projektvorlage mit Unterstützung für das uMCPIno-Protokoll
Beide Projekte haben Einstellungen, in denen Sie klettern können und sollten. Hier sind sie:
Der Standardstatus des SELECT-Flags. Wenn es auf (true) gesetzt ist, wird es vom Timer auf true gesetzt, auch wenn der Remote-Knoten das Flag nicht zurückgibt.
#define CFG_SELECT_DEFAULT_STATE (false)
Um die Dauer dieses Timers einzustellen, gibt es die folgende Einstellung: Intervall zum Zurückgeben des SELECT-Flags in Millisekunden
#define CFG_SELECT_DEFAULT_INTERVAL_MS (4000)
Das Intervall für das Warten auf eine Antwort in Millisekunden sollte etwas kürzer als das Intervall für das Zurücksetzen des SELECT-Flags sein.
#define CFG_TIMEOUT_INTERVAL_MS (3000)
Die tatsächliche Baudrate der Leitung. Dieser Parameter wird benötigt, um zu bestimmen, wann die Übertragung beendet wird.
#define CFG_LINE_BAUDRATE_BPS (9600)
Datenakkumulationsintervall für den Nagle-Algorithmus. Nehmen Sie es unverschämt gleich 100 Millisekunden. Während dieser Zeit warten wir auf eine Reihe von Paketen. Wenn diese nicht eingegeben wurden, senden wir sie so, wie sie sind. Die Aufgabe des Nagle-Algorithmus besteht darin, das Netzwerk von einem Haufen kleiner Pakete mit einer Größe von einem bis mehreren Bytes zu befreien.
#define CFG_NAGLE_DELAY_MS (100)
Mit diesen Einstellungen werden die Portgeschwindigkeiten für die Kommunikation mit dem Steuerungssystem (Host) und der Leitung festgelegt. Verwechseln Sie die Portgeschwindigkeit nicht mit einer Leitung mit einer physischen Übertragungsrate.
#define CFG_HOST_CONNECTION_BAUDRATE_BPS (9600)
Wenn diese Einstellung aktiviert ist, fordert sich das Protokoll selbst an, eine Verbindung herzustellen, wenn die Steuerung mit Strom versorgt wird.
#define CFG_IS_AUTOSTART_ON_POWERON (true)
Dies ist die Größe des Puffers in Byte für eingehende Datenpakete.
#define CFG_IL_RING_SIZE (255)
Als nächstes wollen wir sehen, wie die Hauptskizzenschleife aussieht:
void loop() { uMCP_ITimers_Process(); DC_Input_Process(); DC_Output_Process();
Nun wollen wir sehen, wie das Protokoll funktioniert. Die Hauptlogik ist in der Funktion uMCP_Protocol_Perform () enthalten. Hier ist ihr Code:
void uMCP_Protocol_Perform() { if (state == uMCP_STATE_RUNNING) {
Ein Paketparser, der in einer Funktion lebt
On_NewByte_From_Line
ebenfalls nach dem Prinzip einer endlichen Zustandsmaschine angeordnet und arbeitet "byteweise". Dies geschieht, um Speicherplatz zu sparen.
Der Rest der Implementierung ist nicht von besonderem Interesse. Wir analysieren besser, wie der Benutzer mit dem Protokoll interagiert. In diesem Beispiel gibt es vier "Kontaktstellen".
Die erste Funktion ist das Senden von Daten auf der uMCPIno-Leitung:
bool uMCPIno_SendData(byte* dataToSend, byte dataSize);
Hier ist alles einfach - Sie haben einen Byte-Puffer dataToSend, dessen Größe dataSize ist. Die Funktion gibt true zurück, wenn das Senden möglich ist (es ist Platz zum Hinzufügen von Daten vorhanden), andernfalls false.
Um nicht umsonst zu fahren, können Sie mit der Funktion sofort prüfen, ob ausreichend Platz vorhanden ist:
bool uMCP_IsCanSend(byte dataSize);
Um eingehende Datenpakete zu analysieren, müssen Sie Ihren Code zum Funktionskörper hinzufügen
void USER_uMCPIno_DataPacketReceived();
Eingehende Daten werden in den il_ring-Ringpuffer geschrieben. Das Lesen kann wie folgt organisiert werden:
while (il_Cnt > 0) { c = il_ring[il_rPos]; il_rPos = (il_rPos + 1) % CFG_IL_RING_SIZE; il_Cnt--;
Für gehobene Genüsse gibt es eine Funktion
void USER_uMCP_OnTxBufferEmptry();
Welches aufgerufen wird, wenn alle Daten erfolgreich gesendet wurden. Es ist auch möglich und notwendig, eine Art Code einzufügen.
Warum ist das alles und wo?
Ich habe mich hauptsächlich aus Spaß damit beschäftigt. Außerdem brauchte ich ein einfaches und vor allem "leichtes" Protokoll, um Daten über unsere
uWAVE-Sonarmodems zu senden. Da sie mit einer Geschwindigkeit von nur 80 bps und einer maximalen Kommunikationsreichweite von 1000 m / s und einer Schallgeschwindigkeit von etwa 1500 m / s Daten über Wasser übertragen, ist die Übertragung mit spürbaren Verzögerungen verbunden, und es gibt nur einen Sonarkanal (wenn nicht den meisten) !) der lautesten, langsamsten und instabilsten.
Vor allem aus diesem Grund musste ich den Mechanismus der negativen Bestätigung (NAK) aufgeben - wenn es möglich ist, nicht zu übertragen -, ist es besser, nicht zu 100% zu übertragen.
In der Realität hat sich das Protokoll als nützlich
erwiesen, wenn Daten über einen Funkkanal mit
DORJI- Modulen und dem Arduino bekannten
NS-012 übertragen wurden.
Was weiter?
Wenn es Zeit gibt, plane ich, die Möglichkeit der Adressierung (die übrigens in DDCMP war) hinzuzufügen. Da die Hauptaufgabe dieses Protokolls nun darin besteht, alle Arten von Tests unserer Sonarmodems und anderer Sensornetzwerke zu vereinfachen, gibt es (im wahrsten Sinne des Wortes!) Fallstricke. Ich kann nur sagen, dass das Problem nicht durch einfaches Hinzufügen der Felder "Absender" und "Ziel" gelöst werden kann.
Vielleicht kommt es zu
Geographic Routing und all dem Jazz.
PS
Für konstruktive Kritik, Wünsche und Anregungen bin ich traditionell sehr dankbar. Es ist immer wichtig zu verstehen, ob Sie etwas Nützliches für Menschen tun oder Zeit verschwenden.
Vielleicht habe ich bei dem Versuch, den Übergang dieses Longreads zum Roman "Krieg und Frieden" zu vermeiden, einige Details übersehen - zögern Sie nicht zu fragen.
PPS
Vielen Dank, dass ich mich für meinen Analphabetismus schäme und auf Fehler hinweise (grammatikalisch und logisch):
Das Projekt war ursprünglich Open Source, aber jetzt ist der Artikel auch Open Source.