Unbekannte Zahl in AT+ Antwort lesen und konvertieren

OP #7596598
Lesenswert?

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 #7596609
Lesenswert?

Steve van de Grens schrieb:

https://www.tutorialspoint.com/c_standard_library/c_function_atoi.htm

Ok, aber dann bekomme ich doch eine 5 und eine 2 raus. Ich verstehe noch nicht wie ich da buffer[0] + buffer[1] zusammen übergeben kann.

Jörg W. schrieb:

Was kommt den vor den Ziffern?

Nichts, der Befehl sendet direkt die erste Ziffer in buffer[0]. Oder meinst du etwas anderes?

Moderator Persönliche Seite #7596615
Lesenswert?

Björn W. schrieb:

value = (buffer0-48)+(buffer1-48)

Komplett daneben. Davon abgesehen, dass es schlechter Stil ist, "magische" Zahlen wie "48" zu schreiben, darfst du mal drüber nachdenken, was aus deiner Rechnung heraus kommt … ganz davon abgesehen, dass sie die Bedingung, dass es auch drei oder vier Ziffern sein können, völlig ignoriert.

OP #7596623
Lesenswert?

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
Lesenswert?

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
Lesenswert?

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
Lesenswert?

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
Lesenswert?

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
Lesenswert?

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
Lesenswert?

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
Lesenswert?

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
Lesenswert?

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
Lesenswert?

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

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren