Hallo!
MCU: ESP8266 auf NodeMCU Board
IDE: Visual Micro für Visual Studio
Core: Arduino
Ich bin grade auf einen seltsamen Bug in meiner Firmware für mein
aktuelles ESP8266 Projekt gestoßen, und zwar gebe ich per Webinterface
die Möglichkeit, die Uhrzeit + Datum einzustellen.
Hierfür verwende ich die Standard ESP8266 Webserver Library für das
Arduino Framework und übermittle von einem Formular die eingegebenen
Daten (Uhrzeit + Datum) per HTTP POST an eine Funktion, die diese Daten
dann verarbeitet.
Das Objekt Webserver ist von der Klasse ESP8266WebServer instanziert.
Ich erhalte mittels
1
Webserver.arg("mo");
z.B. das Monat (Eingabe durch den User über das Formular von 1-12,
wobei 01, 02, etc. auch möglich ist). Ich hab nur im INPUT Element im
HTML Teil definiert, dass das eine Zahl von 1-12 sein soll. Keine
Sanitychecks oder so.
Webserver.arg() retourniert einen String (WString.h).
Nun will ich diesen String in ein uint8_t konvertieren und habe das
bisher folgendermaßen gemacht:
Solange man keine führende Null eingibt, ist alles gut. wenn ich aber
z.B. als Monat 09 eingebe, ist uint8_t mo gleich 0.
Mache ich das jedoch mit atoi:
1
uint8_tmo=atoi(Webserver.arg("mo").c_str());
...passt das Ergebnis.
Abgesehen davon, dass das bestimmt keine sichere Methode ist,
Usereingaben zu übernehmen und von der Performance her ist das alles
sicher auch nicht das schnellste: kann sich das wer erklären?
Liegt das an strtoul oder ist es wahrscheinlicher, dass bei mir noch
irgendwo ein Fehler begraben liegt?
Ich hab im Arduino Core des ESP8266 nach strtoul gesucht, habe aber nur
die Header Files gefunden. Schätze mal, dass diese Funktion nicht im
Quellcode vorliegen wird...
Danke für euren Input!
Gruß
Lucas N. schrieb:> wenn ich aber> z.B. als Monat 09 eingebe, ist uint8_t mo gleich 0.
Was korrekt ist.
Da du als Basis 0 angibst, ermittelt strtoul die Basis anhand des
Zahlenformats selber. Führende Null bedeutet Oktal-Zahl. Die 9 ist keine
gültige Oktal-Ziffer, also endet damit die Wandlung -> Ergebnis = 0.
strtoul:
"... If the value of base is 0, the numeric base is auto-detected:
if the prefix is 0, the base is octal,
if the prefix is 0x or 0X, the base is hexadecimal,
otherwise the base is decimal..."
Works as designed!
Wobei ein Vorteil der strtoxyz Funktionen ist, daß man die Zahlenbasis
mitgeben kann.
Edit:
Zu langsam getouched ;-)
OK, danke! Darüber hab ich auch schon was gelesen, war mir aber nicht
ganz sicher ob das nun wirklich zutrifft.
Was wäre nun eine elegante Lösung für dieses Problem, oder ist atoi hier
genauso gut?
Nehme auch gerne Vorschläge für Sanity checks & Co, was halt bei
Usereingaben immer empfehlenswert ist, entgegen. Fürchte aber, dass das
den Rahmen etwas sprengen würde.
Gruße
Hm. Was hast Du denn an der Erklärung nicht verstanden, die Du gelesen
hast?
Unter bestimmten Bedingungen, - hier der Wert für base -, wird die 0 als
Prefix verstanden. Im Umkehrschluss folgt, dass für andere Werte von
Base, dass nicht der Fall ist, bzw. sein könnte (je nachdem welche
Erklärung Du liest). Du musst also den Wert für base ändern.
An sich ist die Wahl von Basis 0 ohnehin kontra-intuitiv. Du gibst eine
Zahl in Basis 10 ein; wählst als Basis aber 0.
Darüber denk mal ein bisschen nach, schlage ich vor.
oh mann, die uhrzeit.. sorry.
dh wenn ich als letzten parameter 10 mitgebe, bekomme ich das gewünschte
ergebnis?
bin nicht mehr am pc, sonst würd ich es einfach ausprobieren, anstatt
blöd zu fragen...
Lucas N. schrieb:> bin nicht mehr am pc, sonst würd ich es einfach ausprobieren, anstatt> blöd zu fragen...
Für derartige kurze Spielereien gibt es online-C-Compiler.
https://www.jdoodle.com/c-online-compiler
(willkürlich ausgesuchter Google-Treffer)
hat funktioniert!
Wie würdet ihr denn Sanitychecks durchführen oder wie ist generell bei C
und Mikrocontrollern mit Usereingaben vorzugehen? Ich erwähne
absichtlich explizit Mikrocontroller, da man dort ja i.d.R. nicht die
Ressourcen hat, die man auf einem PC hat und hier wohl einen Kompromiss
aus Sicherheit und Leistung/Platzverbrauch finden muss, oder?
Oder soll ich hierfür einen neuen Thread erstellen?
Du könntest damit anfangen, niemals mehr Daten zu verarbeiten, als in
den Speicher passen. Das gilt ganz besonders für sämtliche String
operationen. Die C-Library enthält einige Funktionen, die String mit
beliebiger länge zurück liefern, die würde ich meiden.
Ganz besonders kritisch sehe ich hier die String Klasse im Arduino
Umfeld bei Netzwerk-Kommunikation.
Stefan U. schrieb:> Du könntest damit anfangen, niemals mehr Daten zu verarbeiten, als in> den Speicher passen. Das gilt ganz besonders für sämtliche String> operationen. Die C-Library enthält einige Funktionen, die String mit> beliebiger länge zurück liefern, die würde ich meiden.>> Ganz besonders kritisch sehe ich hier die String Klasse im Arduino> Umfeld bei Netzwerk-Kommunikation.
Hilf mir bitte kurz auf die Sprünge, siehst du in den von mir geposteten
Zeilen so einen Fall?
Bei
wäre ich davon ausgegangen, dass eigentlich nichts passieren kann.
Höchstens das uint8_t könnte hier doch überlaufen, wobei das auch nicht
zu einem Speicherüberlauf sondern nur zu falschen Ergebnissen im
Programm führen würde.
Oder liege ich da völlig falsch? Falls ja, warum?
OK Moment, es hat gerade ding gemacht. Sollte ich zuerst mit z.B.
memcpy Webserver.arg("mo") in ein Buffer definierter Größe kopieren und
dann weiterarbeiten? Theoretisch könnte man ja per POST einen ewig
langen String an diese Funktion schicken und c_str() sowie strtoul
versuchen diese dann zu verarbeiten und brauchen jeglichen Speicher auf
-> Booom.
Richtig?
Danke!
> wäre ich davon ausgegangen, dass eigentlich nichts passieren kann.> Höchstens das uint8_t könnte hier doch überlaufen,
Richtig.
> Theoretisch könnte man ja per POST einen ewig> langen String an diese Funktion schicken
Das meine ich, das sind die interessanten Stellen. Gehe nicht einfach
davon aus, daß der Web Request ins RAM passen wird. Du könntest den
Request zum Beispiel Zeilenweise bis maximal 100 Zeichen* pro Zeile
einlesen. Alles, was darüber hinaus geht, wird bis zum nächsten
Zeilenumbruch ignoriert.
*) oder wie viel deine Anwendung auch immer erfordert.
So stellst du sicher, daß deine Anwendung bei richtiger Benutzung
funktioniert und bei falscher Benutzung wenigstens nicht abschmiert.