Forum: Mikrocontroller und Digitale Elektronik Unbekannte Zahl in AT+ Antwort lesen und konvertieren


von Manni (manni_84)


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!

von Steve van de Grens (roehrmond)


Lesenswert?


von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Was kommt den vor den Ziffern?

Du könntest, wenn ich dich richtig verstanden habe, das erste Auftreten 
von '\r' im Array suchen, von da rückwärts gehen bis du etwas siehst, 
was keine Ziffer ist. Ab da+1 dann strtoul() aufrufen.

von Manni (manni_84)


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?

: Bearbeitet durch User
von Björn W. (bwieck)


Lesenswert?

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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Manni schrieb:
> Nichts, der Befehl sendet direkt die erste Ziffer in buffer[0].

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

Manni schrieb:
> Ok, aber dann bekomme ich doch eine 5 und eine 2 raus.

Nein. Du bekommst eine Zahl zurück, nicht irgendwelche einzelnen 
Ziffern.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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.

von Manni (manni_84)


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);

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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)

: Bearbeitet durch Moderator
von Manni (manni_84)


Lesenswert?

Jörg W. schrieb:
> (int)strtol(nptr, NULL, 10)

Das muss ich mal selbst probieren hier im Code um dafür ein Feeling zu 
kriegen.

Vielen Dank für eure Hilfe!

von Björn W. (bwieck)


Lesenswert?

Jörg W. schrieb:
> Komplett daneben.

...war auch nur son Knaller aus der Hüfte

Aber ist alles richtig was du bemängelst.

von Jens G. (jensig)


Lesenswert?

Björn W. schrieb:
> Jörg W. schrieb:
>> Komplett daneben.
>
> ...war auch nur son Knaller aus der Hüfte
>
> Aber ist alles richtig was du bemängelst.

Nicht nur ein Knaller, sondern absolut falsch im Ergebnis ...

von Peter D. (peda)


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/

von Steve van de Grens (roehrmond)


Lesenswert?

Manni schrieb:
> Ok, aber dann bekomme ich doch eine 5 und eine 2 raus.

Wenn im String "52" steht, bekommst du bei atoi() eine 52 raus, in hex: 
0x34

von Bauform B. (bauformb)


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()...

von Heinz B. (Firma: Privat) (hbrill)


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.

: Bearbeitet durch User
von Bauform B. (bauformb)


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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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.

von Bauform B. (bauformb)


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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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.

von Peter D. (peda)


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.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

ist das Protokoll bekannt was übertragen wird? Mikrocontroller 
Anwendung?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Peter D. schrieb:
> Ich kann mich nicht erinnern, ob mir jemals eine nackte Zahl als Antwort
> an den Kopf geschmissen wurde.

Naja, es geht hier ja nicht um menschliche Eingaben, sondern um eine 
AT-Protokoll-Antwort. Selbst bei den alten Modems gab es da schon 
genügend Befehle, die einem als Antwort einfach eine nackte Zahl 
produziert haben.

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
Noch kein Account? Hier anmelden.