Ich habe folgenden Code auf meinen Arduino gespielt:
void setup()
{
Serial.begin(9600);
}
void loop()
{
if (Serial.available() > 0)
{
pwm_1 = Serial.readStringUntil(";").toFloat();
pwm_2= Serial.readString().toFloat();
Serial.println(pwm_1 );
Serial.println(pwm_2);
}
}
Wenn ich beispielsweise 10;20 im Terminal sende bekomme ich als Ausgabe
10.00
0.00
Eigentlich hätte ich erwartet:
10.00
20.00
readStringUntil(";") sollte den String bis zum ";" lesen und der
restliche String aus dem Buffer sollte in pwm_2 gespeichert werden. Wo
ist mein Denkfehler?
https://www.arduino.cc/reference/en/language/functions/communication/serial/readstringuntil/
Der restliche String ist noch nicht im Buffer. readString() scheint nicht zu warten.
Für mich liest sich: Returns: The entire String read from the serial buffer, up to the terminator character Dass er den ganzen Buffer ausliest, aber nur bis zum Trennzeichen. Der Rest wird also verworfen und ist auch nicht mehr im Buffer. Deshalb erhältst du als zweite Ausgabe "0.00". Edit: Volkers Aussage macht auch Sinn. Im Notfall einfach in den Sourcecode schauen.
Gast
#6845527
Vielleicht probierst du mal 10;20; zu senden….
Gast
#6845529
Gerald M. schrieb: > Der > Rest wird also verworfen und ist auch nicht mehr im Buffer. Doch bleibt er. Außer der Terminator, der verfällt. Gerne wird der Timeout vergessen, welcher einem dann Streiche spielt.
EAF schrieb: > Gerald M. schrieb: >> Der >> Rest wird also verworfen und ist auch nicht mehr im Buffer. > Doch bleibt er. > Außer der Terminator, der verfällt. > > Gerne wird der Timeout vergessen, welcher einem dann Streiche spielt. Daran habe ich auch schon gedacht und setTimeout() von 0 bis 2 Sekunden durchprobiert, allerdings erfolglos
Gast
#6845556
Gefährlich ist, dass der String beliebig groß werden kann. Bei einem ESP8266 hat readStringUntil() mal mehrere Kilobytes am Stück empfangen, das hatte zu einem Speicherüberlauf geführt. Alternativer Vorschlag:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
33 | |
34 | |
Du musst die Funktion wiederholt aufrufen. Da du immer abwechselnd auf ein Semikolon versus Zeilenumbruch wartest, würde ich dies in einen Zustandsautomaten verpacken:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
33 | |
34 | |
35 | |
36 | |
37 | |
38 | |
39 | |
40 | |
Einen Timeout kann man dort recht einfach unterbringen:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
33 | |
34 | |
35 | |
36 | |
37 | |
38 | |
39 | |
40 | |
41 | |
42 | |
43 | |
44 | |
45 | |
46 | |
47 | |
48 | |
49 | |
50 | |
51 | |
52 | |
53 | |
Um zu verhindern, dass der Einagbe String zu groß wird gibt es die setTimeout() Funktion der Serial Klasse. Deine Variante sieht ganz gut und logisch aus, ist allerdings ziemlich viel Code für eine doch recht überschaubare Aufgabe. Laut anderen Foren sollte meine anfänglich dargestellte Variante eigentlich ihren Job erfüllen, tut es aber aus irgend einem Grund nicht.
Gast
#6845578
Max M. schrieb: > ist allerdings ziemlich viel Code für eine doch recht > überschaubare Aufgabe Wenn deine Aufgabe wirklich so einfach ist, dann stimme ich dir zu. Bei meinen Anwendungen laufen allerdings meisten mehreres Threads parallel, was die Verwendung von blockierenden Funktionen wie readStringUntil() verbietet. Deswegen gehe ich diese Aufgabe wie gezeigt an. > Um zu verhindern, dass der Einagbe String zu groß wird > gibt es die setTimeout() Funktion der Serial Klasse. Diese Funktion begrenzt nicht die Länge der Strings. Stelle dir vor du empfängst nur ganz viel Müll bis zufällig mal ein Zeilenumbruch dabei ist. Meine Methode ist dagegen abgesichert. Wenn das bei dir nicht passieren kann, dann hast du es natürlich einfacher.
Stefan ⛄ F. schrieb: > Max M. schrieb: >> ist allerdings ziemlich viel Code für eine doch recht >> überschaubare Aufgabe > > Wenn deine Aufgabe wirklich so einfach ist, dann stimme ich dir zu. > Nicht ganz so einfach aber auch nicht zu kompliziert > Bei meinen Anwendungen laufen allerdings meisten mehreres Threads > parallel, was die Verwendung von blockierenden Funktionen wie > readStringUntil() verbietet. Deswegen gehe ich diese Aufgabe wie gezeigt > an. > >> Um zu verhindern, dass der Einagbe String zu groß wird >> gibt es die setTimeout() Funktion der Serial Klasse. > > Diese Funktion begrenzt nicht die Länge der Strings. Stelle dir vor du > empfängst nur ganz viel Müll bis zufällig mal ein Zeilenumbruch dabei > ist. Meine Methode ist dagegen abgesichert. Wenn das bei dir nicht > passieren kann, dann hast du es natürlich einfacher. Ich dachte dadurch wird die Zeit begrenzt, in der der String gefüllt wird. Also sollte doch z.b. 50 ms als Timeout den String auf eine Länge von 50 begrenzen, wenn man von 1ms pro char ausgeht z.B.
Gast
#6845587
Max M. schrieb: > tut es aber aus > irgend einem Grund nicht. Eben. Parserbau ist doch nicht so einfach...... Hier ganz naiv, aber mit Netz und doppeltem Boden, allerdings ohne Timeout.
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
33 | |
Gast
#6845590
Max M. schrieb: > Ich dachte dadurch wird die Zeit begrenzt, in der der String gefüllt > wird. Also sollte doch z.b. 50 ms als Timeout den String auf eine Länge > von 50 begrenzen, wenn man von 1ms pro char ausgeht z.B. Ach so, ja das klingt plausibel. Ich habe zuletzt immer mit WLAN gebastelt, da hat man "etwas" höhere Übertragungsraten.
Gast
#6845610
Max M. schrieb: > Um zu verhindern, dass der Einagbe String zu groß wird gibt es die > setTimeout() Funktion der Serial Klasse. Und was macht dein Arduino, wenn mal ein etwas langsamerer Mensch an der Tastatur am PC sitzt und die Zeit vom ersten Zeichen bis zum letzten Zeichen größer ist als dein Timeout? Nein, auch hier grinst mich wieder mal der Mangel an systematischem Denken an. Eingaben über eine asynchrone serielle Schnittstelle sind erstens Text (wo man sich auch mal vertippen kann und man hätte es gern, da per Backspace-Taste eine Korrektur anzubringen) und zweitens zeitlich unbestimmt. Da mit einem Timeout als Endekenner zu arbeiten, ist eine denkbar schlechte Idee. Andererseits scheint das ereignisgesteuerte Programmieren in Arduino-Kreisen eher unerwünscht zu sein (oder nicht verstanden zu sein) und man programmiert dort nach PAP, also durchgehend blockierend. Ich könnte jetzt eine Lösung des Problems posten, aber die würde das halbe Arduino-Zeugs für die Mülltonne machen und sie würde ganz offensichtlich die Arduino-Leute vor ihre jeweiligen Köpfe stoßen. Aber eines ist immer klar: Eine Zeitbegrenzung ist nicht das richtige Mittel, um eine Platzbegrenzung zu programmieren. W.S.
Gast
#6845655
W.S. schrieb: > Andererseits scheint das ereignisgesteuerte > Programmieren in Arduino-Kreisen eher unerwünscht zu sein (oder nicht > verstanden zu sein) und man programmiert dort nach PAP, also durchgehend > blockierend. Alles klar... Keine Fragen mehr.... Tipp: Darum habe ich eben auch wohl ein "nicht blockierendes" Beispiel gezeigt. Das passt wohl nicht durch deinen Wahrnehmungsfilter. Oder du hast so wenig Ahnung von Arduino, dass dir das gar nicht auffallen kann, aber dann so mit Vorurteilen und Pauschalisierungen um sich werfen, ist auch irgendwie ****, oder?
Gast
#6845734
EAF schrieb: > Darum habe ich eben auch wohl ein "nicht blockierendes" Beispiel > gezeigt. Du bist nicht der TO, jedenfalls dem Namen nach. Offenbar ist dir nicht wirklich alles so klar, wie du schriebest. Abgesehen davon ist dein Beispiel eher ein Ausdrucken des Puffers auf der Seriellen und nicht das, was dem TO eigentlich vorgeschwebt hatte. W.S.
Gast
#6845858
W.S. schrieb: > ... viel bla bla.... Du sprachest "Ereignisorientiert" (oder so)! Das ist es! Du sagtest "Ereignis orientiert, nix Arduino". Och... Ereignis im Arduino Core verankert. Also: Du nix Ahnung von Arduino. W.S. schrieb: > und nicht das, was dem TO eigentlich vorgeschwebt hatte. Ach nee.. kleine Anpassungen und es kommt der Schwebung viel näher... Ich machs dir mal vor... vielleicht lernst du ja auch sogar was davon...
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
33 | |
34 | |
35 | |
36 | |
37 | |
38 | |
39 | |
40 | |
41 | |
42 | |
43 | |
44 | |
45 | |
46 | |
Ich lese in der Loop immer nur ein Zeichen in einen Puffer, sobald es empfangen wurde. Ist es \n oder \r, wird eine \0 an den Puffer angehängt und dem Parser übergeben, der es dann ausführt. Timeouts sind generell evil und werden so auch nicht gebraucht, da die Loop ja nicht blockiert. Parsen kann man am flexibelsten mit sscanf() und spart sich damit haufenweise Spezialfunktionen, wie .toFloat() usw. sscanf() gibt außerdem die Anzahl der gelesenen Argumente zurück, d.h. man kann ein fehlendes Argument von dem Wert 0 unterscheiden.
Gast
#6846228
Peter D. schrieb: > Timeouts sind generell evil und werden so auch nicht gebraucht, da die > Loop ja nicht blockiert. Bei asynchroner Kommunikation sind Timeouts eine einfache Methode, die beiden Kommunikationspartner nach einer Übertragungsstörung wider zu synchronisieren. Dateiübertragungen per Moden und TCP/IP machen das so.
Gast
#6846234
EAF schrieb: > Ich machs dir mal vor... vielleicht lernst du ja auch sogar was davon... Schön kompakt. Wenn er das auf 3 Werte erweitern will braucht er dennoch einen Zustandsautomaten (oder noch ein anderes Trennzeichen). Ich würde die Eingabe Zeilenweise empfangen und dann am Semikolon splitten. Der TO wird sich darüber sicher noch Gedanken machen, wenn das Programm wächst.
Gast
#6846247
Peter D. schrieb: > Ich lese in der Loop immer nur ein Zeichen in einen Puffer, sobald es > empfangen wurde. Ist es \n oder \r, wird eine \0 an den Puffer angehängt > und dem Parser übergeben, der es dann ausführt. Also, in anderen Worten: du baust in einem Puffer eine Eingabezeile auf (oder man nennt es Kommandozeile). Genau SO ist es richtig, denn dabei fällt auch zugleich mit ab, daß man den im Puffer vorhandenen Platz überprüfen kann, damit Überläufe vermeidet und obendrein eine simple Editiermöglichkeit hat, indem man Backspace auswertet. Genau so mache ich das auch. Und wenn dann die Zeile fertig ist, dann kann man auch bequem auswerten. Und das alles ohne irgendwelche Timeouts. Diese sind eher Mode, wenn 2 Maschinen miteinander kommunizieren. Wenn aber an einem Ende der Mensch sitzt, dann nicht. Nur noch ein kleines Detail: man kann auch nach dem Einstapeln eines jeden Zeichens auf den nächsten Platz eine 0 vorsorglich schreiben. Kann_ aber nicht _Muss, denn eigentlich ist es egal, wann man den String mit einer 0 abschließt. Es erleichtert bloß bei etwaigen Editierzeichen die Behandlung ein wenig. W.S.
Gast
#6846259
Damit haben wir die Erklärung, warum meine append_until() Funktion so aussieht wie sie aussieht. Genau für das von W.S. beschriebene Szenario war sie vorgesehen und hat sich auch bewährt.
Stefan ⛄ F. schrieb: > Bei asynchroner Kommunikation sind Timeouts eine einfache Methode, die > beiden Kommunikationspartner nach einer Übertragungsstörung wider zu > synchronisieren. Dateiübertragungen per Moden und TCP/IP machen das so. Ja, das fällt mir auch ständig unangenehm auf. Netzwerkzugriffe bleiben gerne mal 30s hängen (Explorer wird grau). Die IT sagt, sie könne den Fehler nicht finden. Synchronisation ohne Timeout ginge ganz einfach, indem man nach längeren Pausen erstmal das Endezeichen überträgt. Der Parser sieht dann entweder ein leeres Paket oder Müll und kann sofort das nächste gültige Paket empfangen.
Gast
#6846416
Peter D. schrieb: > Ich lese.. In der C Welt hantiert man mit Zeigern, ist oftmals gezwungen dazu. Hier ist aber C++ und da ist das nicht wirklich immer nötig. Pointer, eine gruselige Fehlerquelle, schön wenn es einen Weg drum rum gibt. Die Folgen, wenn sich gestandene Profis, beim pointern verhaspelt haben, haben schon viel Leid über die Angegriffenen gebracht. Es ist ein Gebiet, wo viele Fallen lauern, und einen der Compiler kaum warnt, warnen kann. Natürlich schadet es nicht, wenn auch ein C++ Anfänger irgendwann den richtigen Umgang damit lernt. Peter D. schrieb: > parsen kann man am flexibelsten mit sscanf() und spart sich damit > haufenweise Spezialfunktionen, wie .toFloat() usw. "Spezialfunktionen" und "haufenweise", sind Wertungen, resultierend aus deinem Empfinden und Erfahrungen. Wenn man die Funktionen nicht kennt, also nicht häufig damit arbeitet, fallen die natürlich auf. .toFloat ist gar nicht so aufwändig, siehe: https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/WString.cpp#L741 Im Gegenteil, hier ist vom UNO, also von einem AVR, die Rede. Da gibts dein float fähiges sscanf() erstmal überhaupt nicht. Und wenn man es einbaut(was natürlich geht), frisst es schon einiges an Ressourcen/Flash ---- Zeileneditor... Die Anforderung steht nicht im Eingangsposting. Wurde nicht vom TO hier eingeschoben. Und ist auch zumindest beim Seriellen Monitor der Arduino IDE auch nicht nötig, da dieser per Default sowieso nur Zeilenweise sendet, seinen eigenen Zeileneditor hat. Aus der konkreten Richtung kommt also kein Backspace. Wenn das ein Maschine zu Maschine Protokoll wird, ist eine Backspace Abhandlung auch nicht nötig.
Gast
#6846436
EAF schrieb: > Die Folgen, wenn sich gestandene Profis, beim pointern verhaspelt haben, > haben schon viel Leid über die Angegriffenen gebracht. Ich kann mich an reichlich viele Fälle erinnern, wo Java Entwickler sich mit Referenzen ebenso verhaspelt haben. Um den Fehler zu verstehen musste ich diesen Leuten erstmal erklären, was ein Pointer auf CPU Ebene ist. Will sagen: Jeder Programmierer sollte wissen, was ein Pointer ist und welche Konsequenzen ihre Nutzung hat. Das nützt sogar bei Java, welches gar keine Pointer kennt.
Gast
#6846454
Java ist anders. Bufferoverflows muss es da nicht geben. Und gegen Memoryleaks gibts den GC Mechanismus. Das C++ hat da die SmartPointer (leider nicht beim AVR) Klar, schadet es nicht, Pointer zu kennen. Und dennoch muss man sie nicht nutzen, wenn es Risiko ärmere Möglichkeiten gibt. Sicherlich kann man auch in Java Bockmist bauen. Wie wohl in allen Sprachen, oder mit allen Sprachmitteln.
Gast
#6846465
EAF schrieb: > Sicherlich kann man auch in Java Bockmist bauen. > Wie wohl in allen Sprachen, oder mit allen Sprachmitteln. Darauf wollte ich hinaus. Referenzen als Alternative zu Pointern bewahren niemanden automatisch davor, Fehler zu machen.
EAF schrieb: > In der C Welt hantiert man mit Zeigern, ist oftmals gezwungen dazu. Ich bevorzuge für Arrayzugriffe die Indexschreibweise. Dem Compiler ist das egal, ob Index oder Pointer, er erzeugt (fast) den gleichen Code. EAF schrieb: > Die Folgen, wenn sich gestandene Profis, beim pointern verhaspelt haben, > haben schon viel Leid über die Angegriffenen gebracht. > Es ist ein Gebiet, wo viele Fallen lauern, und einen der Compiler kaum > warnt, warnen kann. Ja, sscanf() muß man die Adresse der Variablen übergeben. Wenn man es mal vergißt, sollte das schnell auffallen. EAF schrieb: > .toFloat ist gar nicht so aufwändig, siehe: > https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/WString.cpp#L741 Hab ich mir schon gedacht, daß es ein verstecktes atof() ist. Der große Nachteil ist aber, daß man den Wert 0 nicht von einem Fehler unterscheiden kann. Deshalb benutze ich es nicht mehr. EAF schrieb: > Und wenn man es einbaut(was natürlich geht), frisst es schon einiges an > Ressourcen/Flash Beim AVR sind das einmalig ~7kB. Beim Nano mit 32kB fällt das also nicht sehr ins Gewicht. EAF schrieb: > Wenn das ein Maschine zu Maschine Protokoll wird, ist eine Backspace > Abhandlung auch nicht nötig. Es kostet aber nur wenige Byte Code. Und manchmal hat man nur ein einfaches Terminal angeschlossen. Hier mal meine Empfangsloop:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
Gast
#6846516
EAF schrieb: > Das passt wohl nicht durch deinen Wahrnehmungsfilter. Oder du hast so > wenig Ahnung von Arduino, dass dir das gar nicht auffallen kann, aber > dann so mit Vorurteilen und Pauschalisierungen um sich werfen, ist auch > irgendwie ****, oder? W.S. ließt mit Absicht falsch um dann solche netten Dinge zu schreiben. Das hat bei ihm System.
Gast
#6846546
Peter D. schrieb: > Beim AVR sind das einmalig ~7kB. Beim Nano mit 32kB fällt das also nicht > sehr ins Gewicht. Da kann man geteilter Meinung sein. Immerhin ist das mehr als ein 1/4 des Flashes. Das zuletzt von mir gezeigte Gesamtprogramm ist kleiner, 6082 Bytes Peter D. schrieb: > case '\b': > if (idx) > idx--; Würde mit String, bei mir eingesetzt so aussehen: case '\b': buffer.remove(buffer.length()-1); break; Also auch nicht wirklich viel komplizierter. Peter D. schrieb: > Ich bevorzuge für Arrayzugriffe die Indexschreibweise. Dem Compiler ist > das egal, ob Index oder Pointer, er erzeugt (fast) den gleichen Code. Eben! Die beiden Verfahren sind äquivalent, so ist es in C und C++ festgeschrieben. Es wird der gleiche Code erzeugt(zumindest habe ich nie was anderes gesehen). > cbuf[idx] Ob cbuf[idx], idx[cbuf] oder *(cbuf+idx), die unterscheiden sich nur in der Schreibweise, die Ausdrücke sind gleichwertig. Damit auch keinerlei Unterstützung durch den Kompiler, wenn man Mist baut, z.B. bei Bereichsüberschreitungen zur Laufzeit. Peter D. schrieb: > if (idx < sizeof(cbuf) - 1) > idx++; > /// Schnipp > if (idx) > idx--; Hier sieht man deutlich, dass du an allen Stellen, wo idx manipuliert wird, händisch *Schutzmaßnahmen* gegen Über- und Unterläufe einbauen MUSST. Das erfordert eine starke Disziplin und Aufmerksamkeit. Sind es doch gerade die Flüchtigkeitsfehler, welche einem dort die Streiche spielen können. Da zu den logischen Fehlern gehörend, da vom Kompiler nicht angemäckert, sind das recht schwer zu findende Fehler. Da muss man IMMER sehr aufmerksam sein, wenn man mit nackten C strings oder auch anderen Arrays arbeitet. Sicherlich ist die String Klasse nicht die Lösung aller Probleme. Aber immerhin sind die Bereichsüberschreitungen vollständig weggekapselt. Der Pointer, welcher (versehndlich) in die Wiese oder in den Wald zeigt, ist damit aus dem Rennen. Das Faktum kann man schätzen lernen, oder ausblenden, bleibt jedem selber überlassen. Ja, ich verwende in dem Code auch eine Bereichsgrenzen Prüfung. > if(buffer.length() >= bufferSize) buffer = ""; Sie dient aber nicht dazu Bereichsüberschreitungen, und damit das versehentliche überschreiben anderer Variablen zu unterbinden, sondern das allokieren von weiterem Speicher zu verhindern.
Gast
#6846574
Peter D. schrieb: > Hier mal meine Empfangsloop: Man sieht, daß dir die Zeilenenden per LF nahestehen. Das ist im Prinzip ein Problem zwischen zwei Welten: die einen nehmen CR als Zeilenende, die anderen nehmen LF. Nur in der DOS/Windows Welt wird brav beides as CRLF benutzt. Das ist früher als Platzverschwendung angesehen worden, ist aber heutzutage vom Platzbedarf her unbedeutend geworden. Eigentlich hätte man das Verwenden von Drucker-Steuerzeichen für das Markieren von Zeilenenden vermeiden sollen und sich stattdessen einen der Separatoren von ASCII (FS..US) aussuchen sollen. Hat man aber damals nicht. Also müssen wir damit irgendwie leben. Und da ich vornehmlich in der Windowswelt bin, teste ich als Zeilenende auf CR und werfe LF der Einfachheit halber weg. Sonst kriegt man nämlich ne leere Zeile, weil an 2 Stellen (bei CR und bei LF) ein Zeilenende erkannt wird. Man könnte jetzt versuchen, dafür eine Logik zu entwickeln, die auf alle 4 möglichen Situationen richtig reagiert: CR oder LF oder CRLF oder LFCR, aber das war mir bislang zu aufwendig für den bescheidenen Nutzen. W.S.
EAF schrieb: >> Beim AVR sind das einmalig ~7kB. Beim Nano mit 32kB fällt >> das also nicht sehr ins Gewicht. > Da kann man geteilter Meinung sein. > Immerhin ist das mehr als ein 1/4 des Flashes. > Das zuletzt von mir gezeigte Gesamtprogramm ist kleiner, 6082 Bytes Wenn der Chip 32K hat, dann bekommst du dafür auch kein Geld zurück - hängt von der Anwendung ab. Meine Chips sind deutlich dicker, was ich da an Overhead ertragen muss geht auf keine Kuhhaut... W.S. schrieb: > Man könnte jetzt versuchen, dafür eine Logik zu entwickeln, die auf alle > 4 möglichen Situationen richtig reagiert: CR oder LF oder CRLF oder > LFCR, aber das war mir bislang zu aufwendig für den bescheidenen Nutzen. Alternativ kann man auch einfach definieren, wie das Zeilenende in diesem Protokoll auszusehen hat. Wenn da "CR" steht, dann ist das so gesetzt und auch für die meisten Windows-Terminalemulatoren akzeptabel (oder einstellbar).
Gast
#6846605
Stefan ⛄ F. schrieb: > Referenzen als Alternative zu Pointern > bewahren niemanden automatisch davor, Fehler zu machen. Es unterbindet nicht alle Fehler, welche man machen könnte. Aber doch schon einige Fehler der gemeinen Sorte. C++: Ein Zeiger auf ein Array sagt einem nicht wie groß das Array ist. Er zeigt nur auf das erste Element. Eine Referenz dagegen schon. Irrtümer sind bei der Index Verarbeitung mit for(;;) nicht ausgeschlossen. Bei dem "Range Based For", dagegen schon, da ist der Index (oder vergleichbares) gekapselt. Ich sage es mal so: Wenn es Möglichkeiten/Sprachmittel gibt, welche weniger Problempotential haben, dann sollte man sie auch einsetzen, wo immer es sinnvoll ist.
EAF schrieb: > Aber immerhin sind die Bereichsüberschreitungen vollständig > weggekapselt. Was macht denn die Kapselung, wenn eine Über- oder Unterschreitung versucht wird? Die Reaktion muß ja irgendwo festgelegt werden. In meinem Beispiel werden die Anweisungen nicht ausgeführt. Ich hab mit C++ immer das Problem, daß ich schnell die Übersicht verliere, weil die Aktionen an völlig anderen Stellen stehen, nur nicht an der Stelle des Aufrufs.
Gast
#6846702
Peter D. schrieb: > Was macht denn die Kapselung, wenn eine Über- oder Unterschreitung > versucht wird? > Die Reaktion muß ja irgendwo festgelegt werden. In meinem Beispiel > werden die Anweisungen nicht ausgeführt. Dafür ist die Konkrete Klasse/Kapselung zuständig. Im falle der Arduino Strings würde das so aussehen:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
Wenn buffer ein C String wäre, würde buffer[42] einen "Zufallswert" liefern, eben das was zufällig an der Stelle im Speicher steht. Peter D. schrieb: > Ich hab mit C++ immer das Problem, daß ich schnell die Übersicht > verliere, weil die Aktionen an völlig anderen Stellen stehen, nur nicht > an der Stelle des Aufrufs. Habe ich volles Verständnis für! Prozedurale Programmierer sind gewohnt Programmlaufpläne zu malen, und auch so zu denken. Ich nenne das die Kontrollfluss orientierte Denkweise. Es wird in jedem Programm nach dem Kontrollfluss gesucht, um es verstehen zu können. Da ist nix falsches dran. Funktioniert nur nicht mehr in der OOP. Oder nur noch in den Methoden selber. In der OOP setzt man die einzelnen Objekte/Kapseln in Beziehung zueinander. Die Objekte schicken sich gegenseitig Nachrichten. Es werden also keine Programmlaufpläne gemalt, sondern UML Diagramme. Und auch so gedacht. Das führt dazu, dass die Beziehungen klar sind, aber der Programmfluss weit aufgefächert stattfindet. Der Prozedurale Programmierer setzt also seinen Fokus auf den Programmfluss, anstatt auf die Beziehungen zwischen den Komponenten. Es ist eine "andere" Denke. Die Umgewöhnung ist nicht leicht. Vereinfacht gesagt: Wer einmal ein richtiger Prozedurale Programmierer geworden ist, ist für die OOP verdorben. Stimmt natürlich nicht immer, aber das prozedurale Paradigma, steht da wirklich quer im Raum und verstellt gerne den Blick, ist eine derbe Hemmung
EAF schrieb: > Ein Zeiger auf ein Array sagt einem nicht wie groß das Array ist. > Er zeigt nur auf das erste Element. > Eine Referenz dagegen schon. Hä? Eine Referenz (in C++) ist ziemlich genau das gleiche wie ein Zeiger, nur dass es (durch den Compiler garantiert) kein Nullzeiger ist. (Gibt schöne Bugs, wenn es dann doch mal einer ist.) Eine Referenz auf einen Vektor enthält dessen Größe, aber eben nur, weil der Vektor das selbst weiß. Das hat mit der Referenz nichts zu tun, geht mit einem Zeiger auf einen Vektor auch. > Wenn es Möglichkeiten/Sprachmittel gibt, welche weniger Problempotential > haben, dann sollte man sie auch einsetzen, wo immer es sinnvoll ist. Dem stimme ich zu. Es hilft aber, wenn man das umschiffte Problempotential auch dann kennt, wenn es durch die verwendeten Sprachmittel nicht auftritt. (Gerüchten zufolge ist genau das der Unterschied zwischen Autodidakt und "hat den Kram in den letzten 25 Jahren mal studiert".) Ansonsten landet man ganz schnell in religiösen Fanatismus in Form seltsamer Codestrukturen.
EAF schrieb: > Wer einmal ein richtiger Prozedurale Programmierer > geworden ist, ist für die OOP verdorben. Einen prozedural strukturierten Code kann man zumindest von oben nach unten lesen und hoffentlich nachvollziehen, was da passiert. Mit massiv OOP-strukturiertem Code geht das nicht. Bei 50 gleichzeitig operierenden Objekten, die sich gegenseitig ständig Events und Messages schicken, während sie ihre eigenen State Machines aktualisieren, wird das ohne zusätzliche Dokumentation nichts. Trocken (d.h. ohne das laufende System) erst recht nicht. Dazu kommt noch, dass OOP andere Probleme mit sich bringt als prozeduraler Code. Nicht besser oder schlechter, aber anders.
Gast
#6847197
S. R. schrieb: > Hä? Eine Referenz (in C++) ist ziemlich genau das gleiche wie ein > Zeiger, nur dass es (durch den Compiler garantiert) kein Nullzeiger ist. > (Gibt schöne Bugs, wenn es dann doch mal einer ist.) Nicht wirklich, oder nicht nur! Ein Zeiger zeigt auf die erste Zelle des Arrays. So kann man auch innerhalb einer Funktion die Größe der Zelle ermitteln, wenn es nicht gerade ein void Zeiger ist. Übergibt man dagegen die eine Referenz auf das Array, kann man auch die Anzahl Zellen, innerhalb der Funktion ermitteln.
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
Ausgabe: > zellengr: 2 > anzzellen: 56 Das mach bitte mit einem Zeiger auf das Array.
Gast
#6847203
Oder so, falls man keinen Wert auf das Array selber legt, sondern nur die Größe ermitteln möchte:
1 | |
2 | |
3 | |
4 | |
5 | |
Hier ist die Referenz dann gar anonym.
Danke! (Wobei das in deinem Fall nur funktioniert, weil für jede Arraygröße eine neue zeigeArrayGroesse()-Funktion gebaut wird. Die Größeninformation ist also trotzdem nicht in der Referenz enthalten, sondern in der instantiierten Funktion.)
Gast
#6847209
S. R. schrieb: > Die > Größeninformation ist also trotzdem nicht in der Referenz enthalten, > sondern in der instantiierten Funktion.) Eigentlich im Datentype der Referenz, die beiden sind fest verbandelt. S. R. schrieb: > weil für jede > Arraygröße eine neue zeigeArrayGroesse()-Funktion gebaut wird. Ohne das Template könnte man gar nicht unterschiedliche Arrays per Referenz übergeben. Unmöglich! Das wird da nur so teuer, weil für die Ausgabe Code generiert wird. Die eigentliche Größenermittlung erfolgt zur Kompilezeit. Die frisst kein Brot.
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
------ Interessant wird das so, mit der Referenz und ihrem Datentype auf diese Art:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
Auch da wird Größe Zellentype mitgeschleppt.
Gast
#6847393
EAF schrieb: > S. R. schrieb: >> Hä? Eine Referenz (in C++) ist ziemlich genau das gleiche wie ein >> Zeiger, nur dass es (durch den Compiler garantiert) kein Nullzeiger ist. >> (Gibt schöne Bugs, wenn es dann doch mal einer ist.) > > Nicht wirklich, oder nicht nur! > Ein Zeiger zeigt auf die erste Zelle des Arrays. > So kann man auch innerhalb einer Funktion die Größe der Zelle ermitteln, > wenn es nicht gerade ein void Zeiger ist. Moment mal! "Man" kann das wohl überhaupt nicht, sondern der Compiler weiß, was er sich bei der Deklaration für eine Typinformation gemerkt hat. Das ist der entscheidende Knackpunkt. Und mal abgesehen davon ist C mit der Prämisse entwickelt worden, dem benutzenden Programmierer alle nur erdenklichen Freiheiten zu geben, was auch die Freiheit einschließt, jegliche Art von Fehlern zu programmieren. Es könnte ja eventuell mal jemanden geben, der das gebrauchen kann. Da geraten 2 entgegengesetzte Ideen aneinander: maximale Freiheit <--> vorsorgliche Einschränkung Und an welcher Stelle zwischen diesen beiden Polen man sich selber befindet, muß wohl ein jeder für sich selbst entscheiden. Allerdings ist das alles schon weit weg von der Ausgangsfrage des Threads, wo jemand seine Uhrzeit "10;56 Uhr" über eine Serielle schicken möchte und sich darüber wundert, daß die gewählte Funktion nur die Stunden liefert und die Minuten in den Rundordner befördert. Tja, sowas kommt vom nicht genügenden Nachdenken vor dem Programmieren besagter Funktion, das kann man dem TO nicht als dessen eigenen Fehler anlasten, sondern das Konstrukt aus "Beachte den Zeichenstrom bis zum Trennzeichen" und "Endekondition ist eine Zeitbegrenzung" ist per se falsch angelegt. W.S.
Stefan ⛄ F. schrieb: > Bei einem > ESP8266 hat readStringUntil() mal mehrere Kilobytes am Stück empfangen, > das hatte zu einem Speicherüberlauf geführt. sehr guter Hinweis ich mache das zwar so, ist aber auch geschützt vor Überlauf! im loop();
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
Gast
#6847411
Joachim B. schrieb: > ich mache das zwar so Ist ja prinzipiell das Gleiche wie meine append_until() Methode. Man sammelt die Zeichen ein, bis das "Ende"-Zeichen erkannt wurde und beachtet dabei die Puffergröße.
Stefan ⛄ F. schrieb: > Ist ja prinzipiell das Gleiche wie meine append_until() Methode. Man > sammelt die Zeichen ein, bis das "Ende"-Zeichen erkannt wurde und > beachtet dabei die Puffergröße. eben, es führen wie immer viele Wege nach Rom, Hauptsache man kommt am Ziel an und verläuft ich nicht.
Gast
#6847423
Max M. schrieb: > Deine Variante sieht ganz gut und logisch aus, ist allerdings > ziemlich viel Code für eine doch recht überschaubare Aufgabe. Max, konnten wir dich inzwischen überzeugen?
Gast
#6847563
W.S. schrieb: > Moment mal! > "Man" kann das wohl überhaupt nicht, sondern der Compiler weiß, was er > sich bei der Deklaration für eine Typinformation gemerkt hat. Das ist > der entscheidende Knackpunkt. Natürlich weiß der Compiler die Größe seiner Datentypen. Aber "ich" muss ihn beauftragen diese Größe als numerischen Wert kund zu tun, damit sie ausgegeben, oder sonst wie im Programm verwendet werden kann. Das nenne ich "ermitteln". Ohne Auftrag rückt er das nicht raus. Das ist Programmieren. Man teilt dem Compiler mit, was man haben möchte, und er erfüllt einem alle Ansagen, im Rahmen seiner Möglichkeiten. W.S. schrieb: > Und mal abgesehen davon ist C mit der Prämisse entwickelt worden, dem > benutzenden Programmierer alle nur erdenklichen Freiheiten zu geben, was > auch die Freiheit einschließt, jegliche Art von Fehlern zu > programmieren. Während der C++ Entwicklung, und ja, wir reden hier über C++ (genauer: gnu++11), hat man die Möglichkeit geschaffen, vielen der in C möglichen Fehlern, aus dem Wege zu gehen. z.B. die Verwendung von Referenzen statt Zeigern, wo immer es möglich/sinnvoll ist, ist eine davon. W.S. schrieb: > das kann man dem TO nicht als dessen eigenen Fehler anlasten, Doch schon... Es ist ein logischer Fehler und liegt damit ganz klar in der Verantwortung des Programmierers. Nur er kann ihn beheben. Der Compiler tut nur das was man ihm sagt. Ein Compiler- oder Versionswechsel, bringt da gar nix. Das Eingangsprogramm des TO funktioniert genau so, wie es programmiert wurde. Dass es nicht so tut, wie er es sich wünscht, liegt eben einzig alleine beim TO. Es braucht halt ein wenig Zeit(einige Jahre) bis man einigermaßen Sattelfest ist, und in C++ denken kann. Zudem: Fehler zu machen ist voll menschlich. Nur Unmenschen machen keinen Fehler, irren sich nie.
Gast
#6847575
EAF schrieb: > W.S. schrieb: >> das kann man dem TO nicht als dessen eigenen Fehler anlasten, > Doch schon... Hmm... das sehe ich anders. Der TO schrieb: Max M. schrieb: > if (Serial.available() > 0) > { > pwm_1 = Serial.readStringUntil(";").toFloat(); > pwm_2= Serial.readString().toFloat(); > ... Und da sehe ich den Verfasser dieser Serial.readStrinUnil(...) als Karnickel, denn er hat offenbar alles, was zwischen dem Trennzeichen und dem Timeout gekommen ist, in den Rundordner befördert. So etwas ist zu kurz gedacht. Und ein Benutzer dieser Funktion rennt dann genau damit vor die Wand. W.S.
EAF schrieb: > z.B. die Verwendung von Referenzen statt Zeigern, wo immer es > möglich/sinnvoll ist, ist eine davon. Da du in deinem Beispiel eine eigene Funktion pro Arraygröße definiert hast, hat das ganze Thema nur mit "Referenz statt Zeiger" nix zu tun.
Gast
#6847728
S. R. schrieb: > Da du in deinem Beispiel eine eigene Funktion pro Arraygröße definiert > hast, Was zumindest in der letzten Version völlig egal ist, da weder RAM noch Flash dadurch beansprucht wird. Es sich alles nur im Compiler abspielt. S. R. schrieb: > hat das ganze Thema nur mit "Referenz statt Zeiger" nix zu tun. Eben doch, alleine schon, weil du das mit deinen Zeigen nicht hinbekommst. Das sollte doch schon klar geworden sein..... Die anderen Vorteile von Referenzen liegen ganz klar dort, dass sie nicht wie Zeiger in die Wiese zeigen können. (Außer man stellt sich ganz besonders blöd an) Arrays kann man per Referenz an eine Funktion übergeben. Der Type bleibt vollständig erhalten. Incl. Größe des Arrays. Arrays kann man per Pointer an eine Funktion übergeben. Dabei zerfällt der Bezeichner des Arrays, zu einem Pointer auf das erste Element. Bei der Transformation geht die Arraygröße verloren. Ist also innerhalb der Funktion nicht zu ermitteln. Die Größe muss man dann als weiteren Parameter übergeben, wenn man sie in der Funktion benötigt. Eine Übergabe von Arrays, per Value, so wie z.B. bei int oder Strukturen, ist nicht möglich/vorgesehen. Natürlich kann man die Größe eines Arrays auch nur aus seinem Type ermitteln. Die std C++ Lib macht es vor. Haben wir nur nicht auf den kleinen AVRs.
Gast
#6847739
W.S. schrieb: > Und da sehe ich den Verfasser dieser Serial.readStrinUnil(...) als > Karnickel, denn er hat offenbar alles, was zwischen dem Trennzeichen und > dem Timeout gekommen ist, in den Rundordner befördert. Das ist fasch. Serial.readStringUntil() liefert alles was vor dem Timeout da seriell rein getropft ist. Es hat 2 Abbruchbedingungnen. 1. der Terminator, dieser wird konsumiert. 2. der Timeout.
Stefan ⛄ F. schrieb: > Max M. schrieb: >> Deine Variante sieht ganz gut und logisch aus, ist allerdings >> ziemlich viel Code für eine doch recht überschaubare Aufgabe. > > Max, konnten wir dich inzwischen überzeugen? Noch bin ich nicht 100% überzeugt, warum mein ursprünglicher code nicht funktioniert hat, klar eure Varianten sind durchdachter und so oder so ähnlich habe ich es jetzt auch umgesetzt, aber laut anderen Foren sollte das auch Funktionieren: void loop() { if (Serial.available() > 0) { // First read the string until the ';' in your example // "1;130" this would read the "1" as a String String servo_str = Serial.readStringUntil(';'); // But since we want it as an integer we parse it. int servo = servo_str.toInt(); // We now have "130\n" left in the Serial buffer, so we read that. // The end of line character '\n' or '\r\n' is sent over the serial // terminal to signify the end of line, so we can read the // remaining buffer until we find that. String corner_str = Serial.readStringUntil('\n'); // And again parse that as an int. int corner = corner_str.toInt(); // Do something awesome! } } oder kurz: void loop() { if (Serial.available() > 0) { int servo = Serial.readStringUntil(';').toInt(); int corner = Serial.readStringUntil('\n').toInt(); // Do something awesome! } } Das war ist ein Beispiel für eine Servo Steuerung für den Input String 1;130. Eigentlich ziemlich ähnlich zu meinem Vorhaben. Den String bis zum delimiter lesen, den rest im buffer dann in einen zweiten String schreiben.
Man könnte wahrscheinlich auch den input String in 2 variabel
abspeichern, dann ein readUntil(";") beim einen machen und dann beide
strings voneinander abziehen, um das zweite zu bekommen. Aber
mittlerweile läufts bei mir, das ist ja die Hauptsache!
Gast
#6848651
Max M. schrieb: > aber laut anderen Foren sollte das auch Funktionieren: Da hasste dich aber wirklich drauf fest gefressen. Kennst du den Spruch mit den "Tausend Fliegen"?
Hier meine finale Variante ohne Timeout, dafür kurz und knapp:
input = Serial.readStringUntil('\n');
pwm1 = input.substring(0, input.indexOf(";")).toFloat();
pwm 2 = input.substring(input.indexOf(";") + 1,
input.length()).toFloat();
Gast
#6854719
Das Ergebnis von indexOf() ist -1 wenn der String kein Semikolon enthält. Also effektiv: > pwm2 = input.substring(0, input.length()).toFloat(); Oder anders gesagt: pwm2 enthält dann id erste Zahl vom Input, nicht die zweite. > pwm1 = input.substring(0, -1).toFloat(); Was macht substring() eigentlich, wenn der Range ungültig ist? Steht leider nicht in der Doku.
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
Am Quelltext kann ich ablesen, dass er in diesem Fall die beiden Parameter tauscht, so als ob da dies gestanden hätte: > pwm1 = input.substring(-1, 0).toFloat(); Und dann folgt: > out = buffer + left; > return out; Buffer ist ein char[]. Durch die Addition von -1 zeigt Buffer nun ins Nirvana. Tolle Wurst. Von einer C++ Bibliothek erwarte ich eine vernünftigere Fehlerbehandlung. Wenn man die Vorteile von C++ nicht nutzt kann man gleich bei C bleiben. Ich bin immer noch kein Arduino Fan.
Gast
#6854744
Stefan ⛄ F. schrieb: > Am Quelltext kann ich ablesen, Du fantasierst. String String::substring(unsigned *int* left, unsigned int right) Stefan ⛄ F. schrieb: > Wenn man die Vorteile von C++ nicht > nutzt kann man gleich bei C bleiben. Alles klar! Nicht lesen können aber urteilen. Stefan ⛄ F. schrieb: > Ich bin immer noch kein Arduino > Fan. Ein Arduino Basher du bist! Selbst vor Lügen und Täuschungen schreckst du nicht zurück. Wobei ich eher vermute, dass du dich da selber täuscht und belügst. Schätze, dass die Meisten da fix hinter schauen.
Gast
#6854749
EAF schrieb: > Du fantasierst. > String String::substring(unsigned int left, unsigned int right) Guter hinweis, habe ich übersehen. Was wird dann aus der -1? > Selbst vor Lügen und Täuschungen schreckst du nicht zurück. Nicht nur das, ich bin bösartig, hinterhältig, stinke und ich koche meine Kinder an Heiligabend.
Gast
#6854757
Stefan ⛄ F. schrieb: > Was wird dann aus der -1? Soll ich die jetzt noch aus dem Handbuch vorlesen, wie die implizite Konvertierungen erfolgen? Das lies mal schön selber nach.
Gast
#6854781
Stefan ⛄ F. schrieb: > Wenn man die Vorteile von C++ nicht > nutzt kann man gleich bei C bleiben. Nana, jetzt trompetest du ein bissel zu laut. Nenne mir mal einen wirklich gewichtigen Vorteil von C++. Ich habe den Stroustrup seit etwa 30 Jahren im Schrank stehen und habe nach dem Lesen dieses Buches beschlossen, diese Verschlimmbesserung von C nicht anzufassen. > Ich bin immer noch kein Arduino Fan. Kann ich verstehen. Damit kriegen die Kinder zwar eine blinkende LED hin, aber gewinnen nichts an wirklichen Elektronik-Kenntnissen. OK, ich muß dabei einräumen, daß andere Kompetenz-Bereiche genauso wichtig sind. Wer Geige spielen oder Blinddarm-Operationen kann, dem sind andere Dinge wichtiger. Verständlichermaßen. W.S.
Gast
#6854782
EAF schrieb: > Soll ich die jetzt noch aus dem Handbuch vorlesen Ja bitte wäre nett von dir.
Gast
#6854830
W.S. schrieb: > Nenne mir mal einen > wirklich gewichtigen Vorteil von C++. Ich habe den Stroustrup seit etwa > 30 Jahren im Schrank stehen und habe nach dem Lesen dieses Buches > beschlossen, diese Verschlimmbesserung von C nicht anzufassen. Nicht nötig. Der Zug ist abgefahren. Nee.. gar das Geleis schon abgebaut. Stefan ⛄ F. schrieb: > Ja bitte wäre nett von dir. Auch das lohnt sich nicht. Die Vorurteile sitzen so fest, da kann keine Ansage mehr was ausrichten. Nennt sich "selektive Wahrnehmung". > .... das Gehirn beginnt, Deutungen auf die Umwelt zu projizieren, > die dann wenig bis nichts mit der objektiven Betrachtung ..... Du hast einfach keinen Bock kauf Arduino. Das ist eigentlich gar nicht schlimm. Geht sicherlich einigen so, ist auch ganz sicher keine Krankheit. Aber so einen Film durchzuziehen, ohne das (je?) zu bemerken, ist schon eine harte Nummer.
Antwort schreiben
Bitte melde dich an, um einen Beitrag zu schreiben.