Unbekannte Zahl in AT+ Antwort lesen und konvertieren

OP #7596598

Hallo ans Netz,

ich stehe gerade etwas auf der Leitung.

Ich habe ein Gerät was sich mit AT Befehlen steuern lässt. Dem schicke ich einen AT Befehl bis es mir mit "OK" antwortet. Das macht es auch.

1
    if(strstr((char *)usart1_buffer,"OK"))
2
    {
3
      HAL_GPIO_WritePin(LED_R_GPIO_Port, LED_R_Pin, RESET); //red LED ON
4
    }

Beim zweiten Befehl frage ich das Gerät nach der Anzahl der gespeicherten Datensätze. Das Gerät antwortet auch (ASCI):

1
usart1_buffer[0] = 53
2
usart1_buffer[1] = 50
3
usart1_buffer[2] = /r

Das Problem: Er sendet erst die erste Ziffer, dann die zweite. Aber eben immer an eine Stelle des Arrays. "53" = 5, "50" = 2. Macht 52 Datensätze (das passt auch, hab auf der SD Karte nachgeschaut). Wie kriege ich das ganze in einen uint16, damit ich damit normal weiterrechnen kann? Ich habe dafür keinen string Befehl gefunden (oder kann ihn nicht richtig anwenden). Ich müsste ja alle Stellen im Array durchgehen bis "/r" kommt. Die Zahl kann auch drei oder vierstellig sein. Ich bin mir fast sicher das es da was brauchbares "von der Stange" gibt, ich finde es nur nicht und komm nicht drauf.

Vielen Dank!

OP #7596623

Jörg W. schrieb:

Dann hilft strtol() bzw. das schon genannte atoi().

OK, danke. Wenn ich das Beispiel von Steve nehme, dann klappt es. Dazu kopiere ich halt den ganzen "buffer" in den string "str". Dann kommt bei val = 52 raus. Passt erstmal soweit. Sollte ja auch mit 3 und 4 Stellen klappen. Für bessere Vorschläge bin ich offen, funktional ist es erstmal das was ich wollte. Danke!

1
    int val;
2
    char str[64];
3

4
    strcpy(str, usart1_buffer);
5
    val = atoi(str);
Moderator Persönliche Seite #7596626

Manni schrieb:

Dazu kopiere ich halt den ganzen "buffer" in den string "str".

Du musst da nichts kopieren. Du kannst strtol() direkt auf deinen Buffer loslassen.

ps: Der Unterschied zwischen strtol() und atoi() ist, dass du bei der erstgenannten Funktion noch eine Variable angeben kannst, in der du erfährst, bis zu welcher Stelle der Parser gekommen ist. Das sollte bei dir ja immer ein '\r' sein, ansonsten ist was foul.

atoi() ist äquivalent zu

1
(int)strtol(nptr, NULL, 10)
#7597067

Manni schrieb:

Wie kriege ich das ganze in einen uint16, damit ich damit normal weiterrechnen kann? Ich habe dafür keinen string Befehl gefunden

Doch, den gibt es. Typisch nimmt man sscanf, um formatierte Eingaben in Variablen zu speichern. Und im Gegensatz zu atoi sagt Dir sscanf auch, wieviel Argumente es lesen konnte, d.h. Du weißt, ob wirklich eine "0" empfangen wurde.

Das Internet ist voll von Erklärungen und Beispielen dazu, z.B.: https://cplusplus.com/reference/cstdio/sscanf/

#7597108

Peter D. schrieb:

Typisch nimmt man sscanf, um formatierte Eingaben in Variablen zu speichern.

s/nimmt/nahm/ Inzwischen sagt die manual page zu sscanf

1
BUGS
2
  Numeric conversion specifiers
3
    Use of the numeric conversion specifiers produces Undefined
4
    Behavior for invalid input. See C11 7.21.6.2/10
5
    <https://port70.net/%7Ensz/c/c11/n1570.html#7.21.6.2p10>.
6
    This is a bug in the ISO C standard, and not an inherent design
7
    issue with the API. However, current implementations are not
8
    safe from that bug, so it is not recommended to use them.
9
    Instead, programs should use functions such as strtol(3) to
10
    parse numeric input. This manual page deprecates use of the
11
    numeric conversion specifiers until they are fixed by ISO C.

Und im Gegensatz zu atoi sagt Dir sscanf auch, wieviel Argumente es lesen konnte, d.h. Du weißt, ob wirklich eine "0" empfangen wurde.

strtol() sagt es noch genauer: einmal per *endptr und zusätzlich wird overflow erkannt (errno == ERANGE). Ich würde vorschlagen, gewöhn' dich gleich an strtol(), strtoul(), strtoll()...

(Firma: Privat) #7597111

Ich kann zwar jetzt kein C, aber von der Logik her ist das doch einfach :

  • die Größe des Array abfragen
  • in einer Schleife bis zum vorletzten iterieren (letztes ist ja "\r")
  • in einem String aneinanderpappen Basic: s = s + Chr$(Val(A[index]))
  • nach der Schleife s in einen INT konvertieren (zahl = Val(s) )
  • dann kannst du damit rechnen.

Müßte in C entsprechend gehen, egal wie groß das Array auch ist.

#7597127

Heinz B. schrieb:

einfach :

  • die Größe des Array abfragen
  • in einer Schleife bis zum vorletzten iterieren (letztes ist ja "\r")

noch einfacher: bis zum ersten Zeichen, das keine gültige Ziffer ist. Genau das gibt es mit strtol() gratis. Bonus: strtol() funktioniert mit jeder Basis von 2 und 36, nicht nur dezimal und hex. Und erkennt optional den Prefix 0 und 0x und demnächst auch 0b.

Moderator Persönliche Seite #7597214

Bauform B. schrieb:

Use of the numeric conversion specifiers produces Undefined Behavior for invalid input.

Keine Ahnung, wie sie auf diese schräge Interpretation kommen. UB entsteht, wenn die Konvertierung nicht zum Typ des Arguments passt, bspw. für %s ein Zeiger auf int geliefert wird. Das Verhalten für fehlerhafte Eingaben ist sehr wohl definiert.

Ich würde trotzdem eher stro*l benutzen, wenn es nur um eine einzelne Zahl geht.

#7597291

https://lore.kernel.org/linux-man/20221208123454.13132-1-abbotti@mev.co.uk/T/#u

gleich am ersten Patch(-Versuch) sieht man, dass auf jeden Fall ein Bug drin steckt. Für den gibt es anscheinend keine gute Lösung. "Just remove any mention of the ERANGE error to avoid confusion" könnte von Microsoft stammen ;)

Der Link stammt aus einer langen Diskussion da nebenan:

https://stackoverflow.com/questions/77601832/man-sscanf-d-is-deprecated-in-c-or-glibc

Moderator Persönliche Seite #7597304

All das wird durch einen typecast von strto*l nach (int) allerdings genauso wenig behoben – und ist mit an Sicherheit grenzender Wahrscheinlichkeit für das Thema des TEs völlig irrelevant.

Wenn man das wirklich vermeiden will, spielt es so gut wie keine Rolle, ob man in scanf() einfach nur "long"-Formate benutzt (und entsprechende Parameter natürlich), oder ob man das mit strto*l() macht – in jedem Falle muss man die domain boundary checks hinterher durchführen, wenn der Wertebereich kleiner als der von long sein soll.

#7597349

Ich kann mich nicht erinnern, ob mir jemals eine nackte Zahl als Antwort an den Kopf geschmissen wurde. Ich parse meistens mehrere Argumente zusammen und das geht mit sscanf recht bequem. Bei Strings gebe ich auch immer die Pufferlänge mit an, damit kein Überlauf möglich ist.

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 2 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.