Forum: Mikrocontroller und Digitale Elektronik Problem mir C-Pointer oder Brett vorm Kopf


von anfänger (Gast)


Lesenswert?

Hallo,

ich habe ein Problem mit einem Base64 Beispielcode bzw einen Konten im 
Kopf.

das Beispiel:
1
char * toDecode = "SGVsbG8gV29ybGQgVGVzdGluZyBiYXNlNjQgZW5jb2Rpbmch";
2
uint16_t test = 40;
3
size_t outputLength;
4
unsigned char * decoded = base64_decode((const unsigned char *)toDecode, test, &outputLength);
5
Serial.print("Length of decoded message: ");
6
Serial.println(outputLength);
7
   
8
Serial.printf("%.*s", outputLength, decoded);
9
free(decoded);

Das Beispiel funktioniert einwandfrei.

Aber ich möchte ein uint8_t [] array decodieren und am liebsten auch ein 
array zurückbekommen.

Aber mein Controller rebootet sobald ich es mit einem Array als input 
versuche
1
char * toDecode = &input[0];
2
uint16_t test = 40;
3
size_t outputLength;
4
unsigned char * decoded = base64_decode((const unsigned char *)toDecode, test, &outputLength);
5
Serial.print("Length of decoded message: ");
6
Serial.println(outputLength);
7
   
8
Serial.printf("%.*s", outputLength, decoded);
9
free(decoded);

kann mir jemand sagen wo mein Denkfehler liegt?

von Nikolaus S. (Firma: Golden Delicious Computers) (hns)


Lesenswert?

in "input".
1) enthält das wirklich einen base64-String?
2) was passiert wirklich wenn nicht (decoded == NULL)? Vermutlich 
Serial.printf(..., NULL).
3) welches System? Welcher Compiler? Aus welcher library stammt 
base64_decode()?
4) Was sagt das Manual zum Parameter wo die Variable "test" (aber mit 
Konstantem Wert 40) übergeben wird?

von blub (Gast)


Lesenswert?

angenommen "input" enthält tatsächlich einen base64 String, dann sollte 
das auch so gehen:
1
unsigned char * decoded = base64_decode(input, test, &outputLength);

von anfänger (Gast)


Lesenswert?

Nikolaus S. schrieb:
> in "input".
> 1) enthält das wirklich einen base64-String?
> 2) was passiert wirklich wenn nicht (decoded == NULL)? Vermutlich
> Serial.printf(..., NULL).
> 3) welches System? Welcher Compiler? Aus welcher library stammt
> base64_decode()?
> 4) Was sagt das Manual zum Parameter wo die Variable "test" (aber mit
> Konstantem Wert 40) übergeben wird?

1) Ja,
1
uint8_t * toDecode = &ble_rx[0];
2
    Serial.printf("%.*s", ble_rx_counter, toDecode);
Ausgabewert: AAECAwQFBgcICQoLDA0ODx

2) Length of decoded message: 2148463512
-> Also viel zu lang
-> direkt danach stürzt der controller ab

3) ESP32 mit der Aktuellen Arduino Library

4) Das original beispiel nimmt strlen()
Ich habe es schon auf eine variable geändert und es funktioniert damit.

Das Array mit der Base64 kodierung kommt per BLE. Ich erhalte ein 
uint8_t array und einen uint16 counter wieviele Bytes per BLE empfangen 
wurden

von anfänger (Gast)


Lesenswert?

blub schrieb:
> angenommen "input" enthält tatsächlich einen base64 String, dann sollte
> das auch so gehen:
> unsigned char * decoded = base64_decode(input, test, &outputLength);

selbes verhalten:

Length of decoded message: 2148463512
und reboot

von Stefan F. (Gast)


Lesenswert?

Vielleicht fehlt bei deinem base64 String das abschließende \0 Zeichen. 
Byte-Arrays schließt man ja typischerweise nicht so ab.

von foobar (Gast)


Lesenswert?

Der zweite Parameter von base64_decode ist die Länge des Eingabebuffers.
1
>   char * toDecode = &input[0];
2
>   uint16_t test = 40;
3
>   ...
4
>   ... base64_decode((const unsigned char *)toDecode, test, &outputLength);

Wenn das input-Array nicht mindestens 40 Zeichen lang ist, kann es 
knallen, ja.

von anfänger (Gast)


Lesenswert?

Hallo Stefan,

hier die Hex Werte des Strings:

Data (23) is written to  Device Status Service: 41 41 45 43 41 77 51 46 
42 67 63 49 43 51 6F 4C 44 41 30 4F 44 78 0

der ble_rx_counter ist 23 und das letzte Byte ist 0

von Yalu X. (yalu) (Moderator)


Lesenswert?

Vermutlich solltest du ble_rx_counter - 1 als Längeninformation an
base64_decode übergeben. Sicher kann ich das aber auch nicht sagen,
weil ich nicht weiß, wo ble_rx und ble_rx_counter herkommen.

von anfänger (Gast)


Lesenswert?

foobar schrieb:
> Der zweite Parameter von base64_decode ist die Länge des Eingabebuffers.
>>   char * toDecode = &input[0];
>>   uint16_t test = 40;
>>   ...
>>   ... base64_decode((const unsigned char *)toDecode, test, &outputLength);
>
> Wenn das input-Array nicht mindestens 40 Zeichen lang ist, kann es
> knallen, ja.

danke foobar

hier leigt anscheinend der fehler. Ich habe die Länge mal fest auf 20 
gesetzt (23 Bytes kriege ich ja) und es geht.

jetzt muss ich mir mal das array von decoded ausgeben lassen

von foobar (Gast)


Lesenswert?

> Data (23) is written to  Device Status Service: 41 41 45 43 41 77 51 46
> 42 67 63 49 43 51 6F 4C 44 41 30 4F 44 78 0

Wenn hinten immer eine 0 hängt, sollte der Code so sein:
1
   char *toDecode = &input[0];
2
   uint16_t inputLength = strlen(toDecode);
3
   ...
4
   ... base64_decode((const unsigned char *)toDecode, inputLength, &outputLength);

von anfänger (Gast)


Lesenswert?

komisch

mit 20 gehts
mit 21,22,23 geht's nicht

Aber die Ausgabe sah auf den ersten Blick gut aus.

Vielleucht liegt's daran:

die .bin Datei wird von der App gelsen und komplett in Base64 kodiert.
Das ganze wird dann in BLE Packete geteilt. jedes BLE paket endet mit 
0x00

Morgen werde ich weiter testen

Schonmal vielen Dank an euch

von Dirk B. (dirkb2)


Lesenswert?

anfänger schrieb:
> mit 20 gehts
> mit 21,22,23 geht's nicht

Base64 macht aus 3 Byte (zu 8 Bit) 4 Byte (mit 6 Bit)

Demnach sollte die Länge des Base64-String auch ohne Rest durch 4 
teilbar sein.

von Yalu X. (yalu) (Moderator)


Lesenswert?

anfänger schrieb:
> mit 20 gehts
> mit 21,22,23 geht's nicht

Dann liegt es daran, dass das Padding fehlt. Dazu musst du die Länge des
Base64-kodierten Strings am Ende mit 0 bis 2 '='-Zeichen auffüllen, so
dass die Gesamtlänge ein Vielfaches von 4 wird.

Probier also mal

  AAECAwQFBgcICQoLDA0ODx==   (Länge 24 durch 4 teilbar)

statt

  AAECAwQFBgcICQoLDA0ODx     (Länge 22)

zu dekodieren. Das dekodierte Ergebnis sollten 16 Bytes mit den Werten
0, 1, 2, ... 15 sein.

PS: Normalerweise sollte der Kodierer das Padding vornehmen. Umgekehrt
gibt es auch tolerante Dekodierer, die ohne das Padding auskommen. In
deinem Fall haben wir es wohl einen laxen Kodierer und einen peniblen
Dekodierer zu tun :)

PS2: Da fehlt nicht nur das Padding, der Eingabestring scheint auch
sonst unvollständig zu sein. Würde nur das Padding fehlen, wären die
letzten 4 Bits alle 0, d.h. das letzte Zeichen wäre kein 'x', sondern
ein 'w'.

von foobar (Gast)


Lesenswert?

Du kannst nicht einfach Teile eines Base64-kodierten Datenstrom 
häppchenweise in den Dekoder schicken.  Wie dirkb2 schrieb, müssen es 
immer ein mehrfaches von 4-Byte-Paketen sein (pro 4 Byte rein kommen 3 
Byte raus).  Kürzere Bröckchen mit "=" auffüllen klappt nur am Ende des 
gesamten Datenstroms (das macht der Encoder normalerweise von selbst) - 
wenn man das mittendrin macht, kommen fehlerhafte Daten raus.

von Peter D. (peda)


Lesenswert?

Ohne die Funktion base64_decode zu kennen ist das nur ein Rätsel raten.
Die Funktion soll ein Array zurückliefern, aber wo wird es gespeichert?

Liefert sie gleichviel oder weniger Bytes zurück, könnte das der 
Eingabestring sein. Dann braucht man aber auch den Rückgabewert nirgends 
zuzuweisen.
Ist der Eingabestring von der Kommandozeile des OS, dann wird er 
read-only sein, d.h. Schreiben ergibt eine Exception.

von Dirk B. (dirkb2)


Lesenswert?

Yalu X. schrieb:
> PS: Normalerweise sollte der Kodierer das Padding vornehmen.

Wird er auch machen, aber das segmentieren bei der Übertragung macht da 
wohl Ärger.

Da wäre es besser nur 20 Byte zu übertragen.

von Dirk B. (dirkb2)


Lesenswert?

Peter D. schrieb:
> Die Funktion soll ein Array zurückliefern, aber wo wird es gespeichert?

auf dem Heap, da ja danach ein free() nötig ist.

> Liefert sie gleichviel oder weniger Bytes zurück,

Dafür gibt es ja den 3. Parameter in der Funktion.

von Peter D. (peda)


Lesenswert?

anfänger schrieb:
> 2) Length of decoded message: 2148463512
> -> Also viel zu lang

Vor allem länger als uint16_t. Da stimmt also schon das Anzeigeformat 
nicht.
Kannst Du nicht mal den Quelltext des geheimen base64_decode verlinken?
Vermutlich stimmen die Aufrufparameter nicht.

von Marc (Gast)


Lesenswert?

Hast Du überhaupt genug Heap-Speicher übrig? Vielleicht hängt es einfach 
daran.
Seltsame Funktion. Bin kein Freund von Funktionen, die 
Speicherallokierung verbergen.

von Dirk B. (dirkb2)


Lesenswert?

Peter D. schrieb:
> Vor allem länger als uint16_t. Da stimmt also schon das Anzeigeformat
> nicht.

Der Parameter ist auch vom Typ *size_t.

von anfänger (Gast)


Lesenswert?

Vielen Dank nochmal an alle

die Hinweise zu den 4 Bytes waren Goldrichtig

Ich habe die App angepasst und teile nun den Base64 String (insgesamt 
349 Bytes) in 20 Byte Stücke
jetzt funktioniert auch das dekodieren ohne Reboot

P.s. im ESP32 Gitter wurde empfohlen eine andere Base64 decode funktion 
zu nutzen.
1
int mbedtls_base64_decode( unsigned char *dst, size_t dlen, size_t *olen, const unsigned char *src, size_t slen );

das probiere ich mal bei Gelegenheit aus.

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.