also warum dann nicht
String buffer=Serial.ReadStringUntil('\n');
und dann den String teilen und umwandeln?
also warum dann nicht
String buffer=Serial.ReadStringUntil('\n');
und dann den String teilen und umwandeln?
Geändert von HaWe (15.10.2017 um 08:23 Uhr)
Ich bevorzuge eigentlich das Lesen einzelner Zeichen, die ich dann selbst in Array oder string hinzufüge.
+ Man kann da einen Timeout einbauen
+ Falls die Gegenseite mal das \n vergisst, kann man aufhören wenn es zuviel wird (die Teensy Implementierung von readStringUntil hat im Gegensatz zu Arduino eine einstellbare Maximallänge)
+ Man kann auch Zeilenenden wie \r\n beachten
+ Steuerzeichen kann man ignorieren, z.B. Falls da jemand auf der Gegenseite von Hand tippt
+ Spezialprotokolle wie das obige mit STX kann man gut behandeln, einfach erst mit dem Lesen anfangen, wenn man ein Startzeichen gesehen hat
- - - Aktualisiert - - -
Ok, immerhin einen Timeout haben sie bei Arduino
beim Teensy sieht die Funktion so ausCode:String Stream::readStringUntil(char terminator) { String ret; int c = timedRead(); while (c >= 0 && c != terminator) { ret += (char)c; c = timedRead(); } return ret; }
Das ist ist ja fast wie bei dem Bug in der Arduino map Funktion ...Code:String Stream::readStringUntil(char terminator, size_t max) { String str; size_t length = 0; while (length < max) { int c = timedRead(); if (c < 0) { setReadError(); break; // timeout } if (c == 0 || c == terminator) break; str += (char)c; length++; } return str; }
man kann vieles tun, auch ein eigenes TCP programmieren, aber hier heißt es doch: KISS, und trotzdem halbwegs sicher!
Problem bei Serial/UART sind Datenübertragungsfehler, und kaum ist mal 1 Trennzeichen falsch übertragen/gelesen, sind die ganzen Daten async.
Also ein definiertes Ende, was auch dann den neuen Start synced!
Also was ist einfacher als
String buffer=Serial.ReadStringUntil('\n');
und dann den String teilen und umwandeln?
Geändert von HaWe (15.10.2017 um 08:22 Uhr)
Das Problem sind eigentlich nicht falsche Endzeichen. Eher Gegenseiten, z.B. ein Raspi, die mal unbedacht einen zu langen Text, z.B. eine Fehlermeldung schicken. Auf einem kleinen Arduino geht dann schnell nichts mehr, weil gelesen wird bis der Speicher voll ist.
ja, oder einfach zu schnell sendet, so dass der Empfänger nicht nachkommt. Ich selber schicke daher immer mindestes 1 ack zurück, bevor neue Daten geschickt werden (handshake).
Aber wenn man die Gegenseite per delays etwas verlangsamt, geht es auch so.
Übrigens:
Transmission Fehler passieren häufig, das merkt man, wenn man checksums einführt. Je nach Bedingungen (UART speed, msg Länge, Kabel oder drahtlos, timeouts, Störfelder) durchaus jede 50.- jede 1000. msg fehlerhaft. Und wenn dabei 1 Trennzeichen verlorengeht und man ohne feste Paket-Grenzen arbeitet, ist spätestens ab dann Schluss mit lustig. Mit Paket-Trennzeichen aber synced es sich anschließend automatisch wieder ganz von selbst.
Daher sehe ich immer noch mein Modell ganz vorn:
String buffer=Serial.readStringUntil('\n');
und dann den String teilen und umwandeln
Geändert von HaWe (15.10.2017 um 08:22 Uhr) Grund: typo ReadStringUntil
Dafür gibt es seit ewigen Zeiten Hand-Shake-Protokolle.
Das verbreitetste Software-Protokoll ist XON/XOFF, dies verstehen die meisten Geräte mit RS-232 Schnittstelle und es funktioniert mit einer 3-Draht-Verbindung.
Wenn eine komplette RS-232-Schnittstelle vorhanden ist, kann man auch Hardware-Handshake verwenden.
Die meisten Betriebssysteme lesen die Zeichen per Interrupt in einen Ringbuffer ein. Einige Bytes bevor der Ringbuffer überläuft sendet man ein XOFF. Dadurch darf der Sender noch ein paar weitere Zeichen senden ohne, dass diese verloren gehen. Ist dann wieder genügend Platz im Ringbuffer sendet man ein XON. Die Applikation liest dann seine Daten aus dem Ringbuffer.
Beim Senden mach man es auch so. Die Applikation schreibt in einen Buffer. gesendet wird dann per Interrupt. Der Vorteil des Buffers liegt darin, dass die Applikation direkt weiter arbeiten kann nachdem die Daten in den Buffer geschrieben wurden, zumindest so lange noch Platz im Buffer ist.
Beim direkten Schreiben auf die Hardware, muss die Applikation oft warten, bis das letzte Zeichen gesendet ist. Dies verlansamt dann die Rechenzeit für die Anwendung ganz erheblich, wenn niedrige Baudraten verwendet werden.
Typische Programme berechnen zuerst eine Menge (CPU-Zeit) um dann einige Zeichen (Textzeile) auszugeben. Durch die Verwendung von Interrupts bekommt man eine einfache Art von Multitasking hin. Senden, Empfangen und Berechnen sind dann eigene Tasks, welche parallel ablaufen können.
MfG Peter(TOO)
Manchmal frage ich mich, wieso meine Generation Geräte ohne Simulation entwickeln konnte?
Ich hatte weiter oben vorgeschlagen, die Zeichen im Interrupt zu empfangen und im Interrupthandler den String zu füllen. Da Arduino nicht meine Baustelle ist, hab ich mal nach "arduino UART receive interrupt" gegoogelt. Dabei ist dann als erster Hit dieses herausgekommen:
Mangels eines Arduinos kann ich das nicht testen, es sieht für mich aber ok aus. serialEvent() scheint mir zwar kein wirklicher Interrupthandler zu sein, blockiert aber die Mainloop nicht und hat daher einen ähnlichen Effekt. In der Mainloop kann man, wenn stringComplete true ist, ganz in Ruhe sscanf() oder was auch immer passt einsetzen. Und in serialEvent() kann man sicherstellen, daß der Inputstring nicht durch eine zu lange Zeile überläuft und einen Fehler melden.Code:/* Serial Event example When new serial data arrives, this sketch adds it to a String. When a newline is received, the loop prints the string and clears it. A good test for this is to try it with a GPS receiver that sends out NMEA 0183 sentences. NOTE: The serialEvent() feature is not available on the Leonardo, Micro, or other ATmega32U4 based boards. created 9 May 2011 by Tom Igoe This example code is in the public domain. http://www.arduino.cc/en/Tutorial/SerialEvent */ String inputString = ""; // a String to hold incoming data boolean stringComplete = false; // whether the string is complete void setup() { // initialize serial: Serial.begin(9600); // reserve 200 bytes for the inputString: inputString.reserve(200); } void loop() { // print the string when a newline arrives: if (stringComplete) { Serial.println(inputString); // clear the string: inputString = ""; stringComplete = false; } } /* SerialEvent occurs whenever a new data comes in the hardware serial RX. This routine is run between each time loop() runs, so using delay inside loop can delay response. Multiple bytes of data may be available. */ void serialEvent() { while (Serial.available()) { // get the new byte: char inChar = (char)Serial.read(); // add it to the inputString: inputString += inChar; // if the incoming character is a newline, set a flag so the main loop can // do something about it: if (inChar == '\n') { stringComplete = true; } } }
MfG Klebwax
Strom fließt auch durch krumme Drähte !
Lesezeichen