ESP32 Arduino String Funktion - wo liegt der Fehler?

Gast #7379732
Lesenswert?

Hallo,

unten stehender Code soll meine Eigabe über BT einfach parsen und mir die entsprechende Datei im SPIFFS auf mein Terminal (Handy) holen. Klappt auch aber ich bin wohl mit String etwas durcheinander gekommen. Da die String Klasse (und auch Stream) sehr komfortabel sind nutze ich sie auch und versuche nur "by reference" zu arbeiten. Probleme mit Speicherfragmentierung hatte ich noch nie.

Hier schaue ich, ob nur "show" gesendet wurde oder "show filename" kam. Der Fehler wird sichtbar in der Ausgabe. Der dateiname dname ist am Ende mit ^@ versehen, also vermutlich nicht Null Terminiert. Ich suche das Leerzeichen und zerschneide damit den String. Den zweiten Tag übergebe ich der SPIFFS Funktion, die die Datei ausliest. Mit String Filename klappt es wo der aktuelle Dateiname global steht, mit dem zerteilten String nicht.

Im BT Terminal Programm sende ich Befehle NULL Terminiert ab, das kann man ankreuzen ob /r/n oder NULL oder was anderes.

debugln(dname) zeigt "230323.txt^@ an. Erzeugt substring keinen Null terminierten Strring?

1
String Filename; /* Enthält aktuellen Dateinamen */
2

3
void HandleBT(String mystring) {
4
  /* Inhalt einer Datei auflisten? */
5
  if (mystring.indexOf("show") != -1) {
6
    int pos = mystring.indexOf(' ');
7
    if (pos != -1) {
8
      String dname = mystring.substring(pos+1); // Hole Dateiname
9
      showTodayFile(&dname); /* Läuft nicht */
10
    } else {
11
      showTodayFile(&FileName); /* Läuft ! */
12
    }
13
    return;
14
  }
15
 usw. usw

Hier noch die Ausgabe Funktion. debug geht direkt auf den BT Comport drauf, statt Kabel halt.

1
/* Zeige das heutige File */
2
void showTodayFile(String *dateiname) {
3

4
  /* Datei öffnen */
5
  File file = SPIFFS.open(*dateiname, "r");
6
  if (!file) {
7
    debugln("showTodayFile: Failed to open" + *dateiname + " for reading");
8
    return;
9
  }
10

11
  debugln("Inhalt von " + *dateiname);
12
  String txt;
13
  while (file.available()) {
14
    txt = file.readStringUntil('\r');
15
    debugln(txt);
16
  }
17
  file.close();
18
  debugln("---End of file --");
19

20
  return;
21
}
Gast #7379785
Lesenswert?

Sebastian W. schrieb:

Hab deinen Code nicht nachvollzogen, aber: ^@ könnte die Darstellung eines \0-Zeichens durch dein Terminal sein.

Dann wäre das ja bei den anderen Strings auch so. ist es aber nicht. Ich versuche es als letzte Möglichkeit alles in ansi c umzu schreiben mit string funktionen aber das ist eben mühsamer.

War eh noch ein Fehler drin, das Root zeichen fehlte String dname = "/" + mystring.substring(pos+1); // Hole Dateiname

Ist auch nur Gebastel auf Gut Glück....

1
  char eingabe[40];
2
  char name[16];
3

4
  mystring.toCharArray(eingabe, sizeof(eingabe));
5

6
  if (strstr(eingabe, "show")) {
7
    char *p = strstr(eingabe, ' ');
8
    if (pos != NULL) {
9
      strcpy(name, p);
10
      showTodayFile(&name);
11
    }
12
  }
#7379795
Lesenswert?

Thorsten M. schrieb:

Dann wäre das ja bei den anderen Strings auch so. ist es aber nicht.

Das meine ich nicht. Wenn ^@ das \0-Zeichen am Stringende sein sollte, dann dürfte es gar nicht auf dem Terminal erscheinen. Das könnte nur passieren wenn das Längenfeld in der String-Klasse nicht mit dem Inhalt des Zeichenkettenpuffers des Strings übereinstimmt. Und das deutet auf Speicherkorruption hin. Womöglich existiert der String an der Stelle, wo er ausgegeben wird, schon gar nicht mehr?

LG, Sebastian

Gast #7379799
Lesenswert?

Sebastian W. schrieb:

Womöglich existiert der String an der Stelle, wo er ausgegeben wird, schon gar nicht mehr?

Wie meinst Du das? Soll ich ihn mal global volatile definieren? Nur zur Info. Hänge ich ein '\0' mit dran habe ich zweimal ^@^@ da stehen. Also hast Du wohl recht.

String dname; void HandleBT(String mystring) {

nach außern verlegen klappt aber auch nicht.

Grübel... grübel...

(Firma: egal) #7379830
Lesenswert?

Thorsten M. schrieb:

und versuche nur "by reference" zu arbeiten.

Wohl nicht so ganz (und ist hier auch nicht wirklich nötig, weil du den Wert eh nicht innerhalb der Funktion verändern willst):

Thorsten M. schrieb:

Hänge ich ein '\0' mit dran

Ich glaube du vermischst hier "Null-Terminierte Char-Arrays" und die String-Klasse munter miteinander. Und dann gibt es auch noch einen Unterschied zwischen "string" und "String". Bei den Klassen heißt eine Null nicht automatisch Stringende.

Gast #7379837
Lesenswert?

Irgend W. schrieb:

Ich glaube du vermischst hier "Null-Terminierte Char-Arrays" und die String-Klasse munter miteinander. Und dann gibt es auch noch einen Unterschied zwischen "string" und "String". Bei den Klassen heißt eine Null nicht automatisch Stringende.

Das hilft mir bei meinem Problem jetzt aber auch nicht weiter. Woher diese extra zeichen kommen? Ich benutze String seit jeher und nie gab es Probleme damit. Leider habe ich keinen echten Debugger wie beim stm32, so dass ich nicht eben ins Ram schauen kann wie es da aussieht.

Die Stringlaenge wird wohl irgendwo im private Teil des Objektes stehen,.

Ist übrigens egal ob by reference oder by value, gleiches Ergebnis.

1
  debugln("--------------------------");
2
  debugln("CPU Frequenz    : " + String(ESP.getCpuFreqMHz()) + " Mhz");
3
  debugln("Total Heap      : " + String(ESP.getHeapSize() / 1024) + " kb");
4
  debugln("Free Heap Size  : " + String(ESP.getFreeHeap() / 1024) + " kb");
5
  debugln("Sketch Size     : " + String(ESP.getSketchSize() / 1024) + " kb");
6
  debugln("Free memory     : " + String(ESP.getFreeHeap() / 1024) + " kb");
7
  debugln("Size of Data    : " + String(sizeof(Data)) + "Bytes");
8
  debugln("MAC Adresse     : " + String(WiFi.macAddress()));
9
  debugln("---- Solar Daten ----------");
10
  debugln("UBatt        = " + String(Data.UBatt) + "V");
11
  debugln("UBattAvg     = " + String(AvgValues.UBattAvg) + "V")
Angehängte Dateien:
Gast #7379890
Lesenswert?

Ich gebe für heute auf. Da stimmt was nicht.

show unsinn müsste eine fehlermeldung erzeugen, dass die Datei nicht exisiert. Tut es aber nicht, rennt er so drüber. Und der Rest hat vermutlich mit dem Terminal zu tun. String können genausogut \r\n terminiert sein. Oder eben gar nicht, wenn die Länge irgendwo steht.

1
/* Zeige das heutige File */
2
void showTodayFile(String *dateiname) {
3

4
  String lokal = *dateiname;
5

6
  /* Datei öffnen */
7
  debugln("Oeffne Datei " + lokal);
8
  File file = SPIFFS.open(lokal);
9
  if (!file) {
10
    debugln("showTodayFile: Failed to open " + lokal + " for reading");
11
    return;
12
  }
13
  
14
  while (file.available()) {
15
    debug((char)file.read());
16
  }
17
  file.close();
18
  debugln("---End of file --");
19

20
  return;
21
}
#7379901
Lesenswert?

Thorsten M. schrieb:

Ich denke heute nicht mehr

Dann mach wenigstens ganz ohne Nachdenken ein

if (mystring.indexOf("show") == 0) {

draus. Edit: Oder startsWith, wie du sagst. Hast ja doch nachgedacht :)

Dann muss "show" immer am Anfang des Befehls stehen, was ja mit deinen Makros kein Problem ist.

Und hier:

File file = SPIFFS.open(lokal);

solltest du angeben, dass du die Datei zum Lesen öffnen willst. Sonst wird einfach eine Datei mit falschem Namen angelegt, und dein

if (!file) {

Check läuft ins Leere.

Gast #7379912
Lesenswert?

Εrnst B. schrieb:

solltest du angeben, dass du die Datei zum Lesen öffnen willst. Sonst wird einfach eine Datei mit falschem Namen angelegt, und dein

Nicht ganz. Default ohne Parameter ist lesen. Und gelesen habe ich alles was drüber gibt heute, seit 10 Uhr. Sitze hier im Schlafanzug, den ganzen Tag nur Kaffee und Kippen. Ah, Berlin Entscheid auch gescheitert, sehr gut. Gibt ja noch Vernünftige dort.

Ende für Heute...

Gast #7379997
Lesenswert?

Ich glaube da haben wir den Übeltäter... so sieht das aus was diese Zeile erzeugt.

String dname = "/" + mystring.substring(pos + 1) + "txt";

Das BT Terminal stellt diese Null als ^@ dar.

substring scheint entweder fehlerhaft zu sein oder das ist ein Feature, dass da eine Null zwischenrutscht. Abgesehen davon dass ich den Punkt vergessen habe. Ich werde heute abend mal ne Kanne Kaffee bereitstellen und schreibe den ganzen Ramsch auf string Funktionen um, da weiss ich wenigstens was bei rauskommt. ChatGPT leistet da auch sehr gute Hilfe.

Angehängte Dateien:
Gast #7380009
Lesenswert?

Das spuckt die Code analyse von ChatGPT aus:

Es sieht so aus, als ob das Problem darin besteht, dass Sie einen Pointer auf dname an die Funktion showTodayFile übergeben, obwohl dname eine lokale Variable in der if-Anweisung ist. Sobald die if-Anweisung beendet ist, wird die Variable dname außerhalb ihres Gültigkeitsbereichs fallen gelassen und ihr Speicherplatz wird möglicherweise von anderen Daten überschrieben werden. Der Pointer, der an showTodayFile übergeben wird, würde daher auf einen ungültigen Speicherbereich zeigen, was zu unvorhersehbarem Verhalten führen kann.

Um dieses Problem zu beheben, könnten Sie stattdessen eine Variable vom Typ String deklarieren und diese dann an showTodayFile übergeben, anstatt einen Pointer auf dname zu übergeben. Hier ist eine modifizierte Version des Codes:

#7380014
Lesenswert?

Thorsten M. schrieb:

String dname = "/" + mystring.substring(pos + 1) + "txt";

Wo genau steht denn diese Zeile in dem gezeigten Code oben?

Thorsten M. schrieb:

Das spuckt die Code analyse von ChatGPT aus:

Es sieht so aus

ChatGPT sollte besser auch ein paar Tassen Kaffee trinken, wenn sich das auf den von dir gezeigten Code bezieht. Wenn du dem natürlich anderen Code gegeben hast, kann das durchaus sein.

Oliver

Gast #7380017
Lesenswert?

Oliver S. schrieb:

Wo genau steht denn diese Zeile in dem gezeigten Code oben?

1
/* ---------------------------------------------------------------
2
    Verarbeitet Befehle aus dem Smartphone über BT Terminal 
3
    Die Strings müsse NUL terminiert sein! 
4
---------------------------------------------------------------- */
5

6

7
void HandleBT(String mystring) {
8

9
  /* Inhalt einer Datei auflisten? */
10
  if (mystring.startsWith("show")) {
11
    int pos = mystring.indexOf(" ");
12
    if (pos != -1) {
13
      String dname = "/" + mystring.substring(pos + 1) + ".txt";
14
      debugln("echo:" + dname);
15
      String fileNameWithPath = dname; // create a new String variable to pass to the function
16
      showTodayFile(fileNameWithPath);
17
    } else {
18
      showTodayFile(FileName);
19
    }
20
    return;
21
  }
#7380035
Lesenswert?

Ich denke es liegt eher daran, dass der String, den deine HandleBT-Funktion übergeben bekommt, am Ende ein NULL enthält. Dann ist die NULL natürlich auch im Ergebnis des substring-Aufrufs vorhanden.

An deiner Stelle würde ich prüfen, woher die NULL am Ende kommt. Entweder dann an der Aufruf-Stelle ändern oder am Anfang der HandleBT-Funktion abschneiden (bzw. trimmen).

#7380114
Lesenswert?

Constanze H. schrieb:

open in spiffs erwartet einen cstring aka. const char * path als Argument, keinen String.

Das ESP32-SPIFFS unterscheidet sich von der ESP8266-Variante, bin ich oben auch schon drüber gestolpert... dort gibt's

1
class FS {
2
...
3
   File open(const char* path, const char* mode = FILE_READ, const bool create = false);
4
   File open(const String& path, const char* mode = FILE_READ, const bool create = false);
Gast #7380178
Lesenswert?

Constanze H. schrieb:

open in spiffs erwartet einen cstring aka. const char * path als Argument, keinen String. Wie man diesen cstring erzeugt, habe ich im post

Ich schätze wirklich jede Hilfe. Aber wenn jemand ohne jeden Nachweis falsche Behauptungen aufstellt dann winke ich auch ab. Andere schrieben es schon, spiffs ist ein Fs Objekt und das lässt String zu. Rechter Mausklick und schon sieht man das. Ich werde das alles trotzdem umschreiben ohne String. Ist zwar aufwendiger mit der stdlib aber ich weiss da genau was da passiert.

#7380179
Lesenswert?

Julian L. schrieb:

Ich denke es liegt eher daran, dass der String, den deine HandleBT-Funktion übergeben bekommt, am Ende ein NULL enthält. Dann ist die NULL natürlich auch im Ergebnis des substring-Aufrufs vorhanden.

sehe ich auch so. Ein String kann '\0' Zeichen enthalten.

1
  String s("test");
2
  s +='\0';
3
  String s2 = s.substring(2);
4
  Serial.println(s2.length());

liefert Länge = 3, die 0x00 ist im String s und Substring s2 enthalten.

Gast #7380253
Lesenswert?

J. S. schrieb:

Der Fehler passiert einfach schon vorher im nicht gezeigten Code.

String dname = "/" + mystring.substring(pos + 1) + ".txt";

Hier passiert er! Ich bin es gewohnt, dass der + Overload Operator Strings nahtlos zusammen schreibt als Ascii Zeichen. Und das ist eben nicht der Fall hier.

Gast #7380593
Lesenswert?

Läuft!

Da die Befehle eh auf Makro Tasten liegen und feste Größen bestehen ist die Null jetzt weg. Die kommt von irgendwoher rein, vermutlich vom BT Terminal. Und Kollegen sagten mir auch dass String keinen Terminator hat, die Länge wird ständig angepasst von den Methoden, je nachdem wie der verwurstelt wird.

1
/* Inhalt einer Datei auflisten? */
2
  if (mystring.startsWith("show")) {
3
    if (mystring.charAt(4) == ',') {
4
      dname = "/" + mystring.substring(5, 11) + ".txt";
5
      showTodayFile(dname);
6
    } else {
7
      showTodayFile(FileName);
8
    }
9
    return;
10
  }

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