Guten Abend, habe ein Problem mit dem Verständniss zu Serial.parseInt! Ich sende eine Zahl in Ascii (z.B. -1000 mit einem \n am Schluss) zu meinem uC und wandel diese in einen long. Dazu nutze ich den Seriellen Port. Chip ist ein ATMega8. Abfangen tu ich es mit: if (Serial.available() > 0){ long myInt = Serial.parseInt(SKIP_ALL, '\n'); ... } Mein Problem: Sobald ich Serial.parseInt verwende, scheint es als würde der uC sich eine Gedenksekunde gönnen und wird blockiert. Benötigt Serial.parseInt tatsächlich so viel Leistung oder fährt es in einen TimeOut? Die Wandlung funktioniert nämlich wie ich es möchte. Nur diese ewige Pause möchte ich vermeiden, da eine andere Aufgabe dann stehen bleibt. Wenn ich hingegen: long myInt = -1000; schreibe, mehkt man keine Verzögerung (und kann auch keine mit dem Sozi messen)!
Könnte es daran liegen, dass das Endzeichen nicht erkannt wird und statt dessen ein Timeout ausgelöst wird? Oder dass das Endzeichen erkannt wird, aber noch ein zusätzliches Zeichen in der Pipe steckt, welches dann den Timeout auslöst? https://www.arduino.cc/reference/en/language/functions/communication/serial/parseint/ => Serial.setTimeout()
Ansgar schrieb: > Ich sende eine Zahl in Ascii (z.B. -1000 mit einem \n am Schluss) zu > meinem uC und wandel diese in einen long. Dazu nutze ich den Seriellen > Port. > if (Serial.available() > 0){ Sobald das erste Zeichen (›-‹) empfangen wurde, wird die Abfrage wahr und > long myInt = Serial.parseInt(SKIP_ALL, '\n'); wird aufgerufen. > Sobald ich Serial.parseInt verwende, scheint es als würde der uC sich > eine Gedenksekunde gönnen und wird blockiert. Benötigt Serial.parseInt Das wartet nun solange bis alle notwendigen Zeichen empfangen wurden. Hängt von deiner Schnittstelle ab, bei 9600 ungefähr 1ms pro Zeichen.
>Das wartet nun solange bis alle notwendigen Zeichen empfangen wurden. >Hängt von deiner Schnittstelle ab, bei 9600 ungefähr 1ms pro Zeichen. Das Problem ist der Ausdruck "alle notwendigen Zeichen". Ich habe es gerade mal im Wokwi-Simulator ausprobiert: https://wokwi.com/projects/334148643034497620 Beim ersten Zeichen wird parseInt() aufgerufen. Wenn man hingereinander 1,2,3 eintippt (ohne CR,LF) geht es etwa eine Sekunde (TimeOut) und die Zahl wird angezeigt.
chris_ schrieb: > Beim ersten Zeichen wird parseInt() aufgerufen. Wenn man hingereinander > 1,2,3 eintippt (ohne CR,LF) geht es etwa eine Sekunde (TimeOut) und die > Zahl wird angezeigt. Wenn's in einen Timeout läuft, dann wurden eben nicht alle notwendigen Zeichen empfangen. Vernünftigerweise sammelt man ohne Verzögerung erst einmal eine Eingabezeile und extrahiert dann die Informationen.
Anstelle von Serial.available() kannst mit Serial.peek() antesten, ob das letzte empfangene Zeichen ein Zeilenumbruch war. Erst dann rufst du Serial.parseInt() auf. Dieses Vorgehen hat aber zwei Knackpunkte: a) Wenn der Empfangspuffer nicht groß genug, die ganze Zahl aufzunehmen, kommt Schrott heraus. Ich denke der Puffer von Arduino ist normalerweise groß genug. b) Wenn du nach dem Zeilenumbruch noch etwas anders empfangen hast, dann ist das letzte Zeichen eben kein Zeilenumbruch. Dadurch "verpasst" du unter Umständen eine komplette Zeile. Um es besser zu machen habe ich folgende Prozedur geschrieben:
1 | /**
|
2 | * Append characters from a stream to the buffer until the terminator has been found.
|
3 | * In case of buffer overflow, read until the terminator but skip all characters that
|
4 | * do not fit into the buffer.
|
5 | * @param source The source stream.
|
6 | * @param buffer The target buffer, must be terminated with '\0'.
|
7 | * @param bufSize Size of the buffer.
|
8 | * @param terminator The last character that shall be read from the stream, e.g. '\n'.
|
9 | * @return True if the terminator has been reached, false if the stream did not contain the terminator.
|
10 | */
|
11 | bool appendUntil(Stream& source, char* buffer, int bufSize, char terminator) |
12 | {
|
13 | int data=source.read(); |
14 | if (data>=0) |
15 | {
|
16 | int len=strlen(buffer); |
17 | do
|
18 | {
|
19 | if (len<bufSize-1) |
20 | {
|
21 | buffer[len++]=data; |
22 | }
|
23 | if (data==terminator) |
24 | {
|
25 | buffer[len]=0; |
26 | return true; |
27 | }
|
28 | data=source.read(); |
29 | }
|
30 | while (data>=0); |
31 | buffer[len]=0; |
32 | }
|
33 | return false; |
34 | }
|
Diese kannst du innerhalb der Hauptschleife so benutzen:
1 | char buffer[200]; |
2 | |
3 | void loop() |
4 | {
|
5 | if (appendUntil(Serial, buffer, sizeof(buffer), '\n')) |
6 | {
|
7 | // If the line starts with "GET", do something
|
8 | if (strncmp(buffer,"GET ",4)==0) |
9 | {
|
10 | ...
|
11 | }
|
12 | else
|
13 | {
|
14 | // Prepare to read the next line
|
15 | buffer[0]=0; |
16 | }
|
17 | }
|
18 | }
|
Letztendlich läuft das auf eine doppelte Pufferung hinaus.
>>https://wokwi.com/projects/334148643034497620 >Wenn's in einen Timeout läuft, dann wurden eben nicht alle notwendigen >Zeichen empfangen. Das ist erst mal eine "Schlafschulweisheit". Die Probleme liegen hier tiefer. Wenn du den obigen Simulatorlink ausprobierst, kannst du sehen, dass beim Drücken der Return-Taste eine unnötige '0' als geparster Integer im Ausgabefenster auftaucht.
chris_ schrieb: >>>https://wokwi.com/projects/334148643034497620 >>Wenn's in einen Timeout läuft, dann wurden eben nicht alle notwendigen >>Zeichen empfangen. > > Das ist erst mal eine "Schlafschulweisheit". Die Probleme liegen hier > tiefer. > Wenn du den obigen Simulatorlink ausprobierst, kannst du sehen, dass > beim Drücken der Return-Taste eine unnötige '0' als geparster Integer im > Ausgabefenster auftaucht. Es wird gemunkelt das lesen hilft:
1 | parseInt() returns the first valid (long) integer number from the serial buffer. Characters that are not integers (or the minus sign) are skipped. |
2 | |
3 | In particular: |
4 | |
5 | Initial characters that are not digits or a minus sign, are skipped; |
6 | Parsing stops when no characters have been read for a configurable time-out value, or a non-digit is read; |
7 | If no valid digits were read when the time-out (see Stream.setTimeout()) occurs, 0 is returned; |
cr ist \r und nicht \n. Du gibst cr lf über das Terminal ein.
>cr ist \r und nicht \n. >Du gibst cr lf über das Terminal ein. Das wiederum ist der Grund vieler Fehler bei der Datenübertragung zwischen Unix- und Windowssystemen. Die Frage ist, ob Ansgar hier ein Unix- oder Windows System benutzt. Im Eingangspost schreibt er nur \n ohne \r. Beim Simulator im Browser vermute ich, dass er unabhängig vom Betriebssystem CR/LF beim Drücken der Returntaste erzeugt. >They are used to mark a line break in a text file. As you indicated, >Windows uses two characters the CR LF sequence; Unix only uses LF and the >old MacOS ( pre-OSX MacIntosh) used CR. aus https://stackoverflow.com/questions/1552749/difference-between-cr-lf-lf-and-cr-line-break-types
Stefan ⛄ F. (stefanus) >Anstelle von Serial.available() kannst mit Serial.peek() antesten, ob >das letzte empfangene Zeichen ein Zeilenumbruch war. Erst dann rufst du >Serial.parseInt() auf. Vermutlich ist deine Lösung die sauberste. Schade, dass man es so selber implementieren muss. ParseInt scheint für viele Anwendungen ungeeignet und bedürfte dahingehend einer besseren Implemtierung mit der Erkennung eines "Terminator"-Zeichens.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.