ESP32 und die "String" Funktion: Finger weg!

Gast #7815212
Lesenswert?

Hallo,

für diejenigen, die es interessiert. Ich habe lange verzweifelt an einem Server, der mir Messdaten aufs Handy spielt als Webseite und der immer wieder mal einfror, d.h. das Hauptprogramm zur Steuerung der Balkonsolaranlage zur Speisung ins Netz, in die Batterien oder von den Batterien ins Netz (mit einem Inverter, der Batterien akzeptiert und den MPPT abschaltet) lief noch aber nix mehr mit Webanzeige. Aus lauter Not ein Wifi Relais eingebaut als Reset EIn/Aus. Er stürzte dabei völlig unregelmäßig ab, mal nach ein paar Stunden, mal erst nach einem Tag usw.

Bis mir dann die idee kam mit der KI "Gemini Advanced" (kostet was aber gut) mal den ganzen Arduino Code hoch zu laden und analysieren zu lassen. Da kann man einfach die Dateien reinziehen, muss nichts abtippen. Die Analyse spuckte vieles aus, u.a. dass die exzessive Verwendung von String Literalen und String Umwandlungen, die man ja gern macht bei HTML Seiten Erzeugung um den Riesenstring zu erzeugen, der mit send an den Client geschickt wird Probleme auf dem Heap erzeugen kann. denn String nicht nicht nur den Heap sondern erzeugt auch temporäre lokale Fragmente auf dem Stack, wenn es Float etc umwandelt in Text. Der Stack ist nur 4bKB gross beim ESP32, die sind schnell voll mit lokalen Buffern.

Gemini schrieb mir die komplexe Webausgabe um auf Standard C. Irre was die KI da leistet! Sie machte auf Anhieb alles richtig, schneller als jeder Mensch das hinkriegen würde. Selbst eine Analyse nach MISRA kann das Ding durchführen. Da sie den ganzen Code hatte wurde auch Buffer gleich richtig dimensioniert für die json Ausgabe an weitere Clients, z.b die Anzeigetafel. If then else Gräber wurden aufgelöst und verkleinert usw. Und das Ding lief auf Anhieb! Seit Tagen absturzfrei durch. HTML Code erzeugen mit dieser KI ist ein Kinderspiel, ob Buttons, javascript, CSS Styles oder was auch immer. Damit kriegt man sehr komplexe Sachen hin mit dem ESP32.

Falls es also noch jemand nicht glaubt: String ist ein heisses Eisen! Kann funktionieren, muss aber nicht. Vor allem nicht bei dynamischen Ausgaben, denn es hat zb. keinen Destruktor, das Zeug stopft den Speicher zu, bis Absturz.

Wieder was dazugelernt :-) Der Code hier ist komplett KI erzeugt, auch die Variablennamen wurden angepasst.

1
   /* Bei Inverter Betrieb Batterie grau machen */
2
    strcat(msg, "Batterie   ");                // Batterie Text immer hinzufügen
3
    dtostrf(Data.UBatterie / 2.0, 1, 1, fbuf); // Konvertiere float zu String
4
    if (Flags.fInvertSolarPower)
5
    {
6
      strcat(msg, "<span style=\"color:#A4A4A4\">"); // Graue Schrift wenn Inverter aktiv
7
      strcat(msg, fbuf);
8
      strcat(msg, " V</span><br/>");
9
    }
10
    else
11
    {
12
      strcat(msg, fbuf);
13
      strcat(msg, " V<br/>"); // Keine spezielle Formatierung wenn kein Inverter aktiv
14
    }
15
  }
16

17
  /* Gib die Messwerte aus */
18
  strcat(msg, "I/Ah Batt  ");
19
  dtostrf(Data.ISolar, 1, 2, fbuf); // Konvertiere ISolar (float)
20
  strcat(msg, fbuf);
21
  strcat(msg, " A / ");
22
  itoa((int)Data.AhSolar, fbuf, 10); // Konvertiere AhSolar (int)
23
  strcat(msg, fbuf);
24
  strcat(msg, " Ah<br/>");
#7815222
Lesenswert?

Thorsten M. schrieb:

dass die exzessive Verwendung von String Literalen und String Umwandlungen, die man ja gern macht bei HTML Seiten Erzeugung um den Riesenstring zu erzeugen

Das ist halt die hässliche und suboptimale Variante.

Liefere doch lieber einfach ein statisches HTML aus. Beim ESP kannst du die Dateien sogar nachträglich auf das Filesystem laden und neue Versionen aktualisieren bei Bedarf.

Dann musst du auf ESP Seite rein garnichts ersetzen oder mit Strings machen. Sobald das HTML auf PC Seite angekommen ist soll es lieber asynchrone Requests über JS machen. Dann brauchst du nur relativ kleine JSON "Strings" mit den aktuellen Prozessdaten zusammensetzen die noch nicht einmal eine dynamische allokierung von Speicher benötigen. So sinkt der dynamische Speicherbedarf sehr schnell und die Latenz sucht.

Wo ist überhaupt die Frage?

#7815224
Lesenswert?

So, und um wirklich was zu gewinnen, könntest du die KI das noch auf einen AsyncHTTPServer umschreiben lassen, unter Verwendung der Template-Engine, mit HTML, CSS und JS auf dem Dateisystem.

Die Herangehensweise "Erstmal das ganze HTML im RAM zusammenbauen, und dann erst raussenden" (Egal ob strcat oder String) passt zwar für so kleine "Hallo Welt"-Beispiele, kommt aber schnell an ihre Grenzen.

Und ja, das kriegt die KI auch (zumindest großteils) hin.

Gast #7815226
Lesenswert?

Εrnst B. schrieb:

Die Herangehensweise "Erstmal das ganze HTML im RAM zusammenbauen, und dann erst raussenden" (Egal ob strcat oder String) passt zwar für so kleine "Hallo Welt"-Beispiele, kommt aber schnell an ihre Grenzen.

Aber für jemand, der das noch nie gemacht hat ist das ja ein Anfang. Die fortschrittlicheren Dinge lernt man ja erst nachher. Ich haue das alles als HTML raus oder als großer json Container, je nachdem was im GET steht. Teilweise auch mit POST, wenn zb Daten in beide Richtungen fliessen sollen. OTA Update sowieso. Asynchroner Server Code ist schwerer zu debuggen, damit habe ich noch keine Erfahrungen gemacht.

#7815231
Lesenswert?

Thorsten M. schrieb:

Die Ablage im Filesystem wäre zu aufwendig, denn es werden viele Dinge erst berechnet und dann ein String daraus gebaut.

Genau das kannst du damit loswerden. In's HTML packst du Variablen wie z.B <span id="voltage>%BAT_VOLTAGE%</span>

Speicherst das HTML-File so ins Filesystem, und beim raussenden kannst du die Variable mit dem aktuellen Wert ersetzen lassen. Und das als Stream, es muss nie das ganze HTML auf einmal in den RAM. => Kein Problem mit sehr großen Dateien, kein Problem mehrere Requests gleichzeitig zu bearbeiten.

Oder du machst das HTML gleich komplett statisch, und aktualisierst die Werte per fetch/json, websocket, SSE, ... wie von Mani vorgeschlagen.

Die KI hat dir deinen Vorschlaghammer durch einen Pflasterstein ersetzt. Gut, der ist leichter erhältlich, vielleicht auch handlicher. Schonmal ein Fortschritt. Zum Schrauben reindrehen gehen beide irgendwie, schön ist keine Lösung. Wenn du die KI vorsichtig in Richtung Schraubendreher schubst, findet die auch was Passenderes.

#7815233
Lesenswert?

Thorsten M. schrieb:

Dachte mir, das könnte jemand interessieren :-)

Wie gesagt, mach es nicht so. Dann funktioniert das auch auf deutlich kleineren uC als einem ESP.

Thorsten M. schrieb:

Die Ablage im Filesystem wäre zu aufwendig

Eben gerade nicht. Ist viel einfacher weil es sich vorab auch einfach am PC entwickeln und debuggen lässt.

denn es werden viele Dinge erst berechnet und dann ein String daraus gebaut.

Das kannst du ja machen. Aber du musst das ja nicht auf dem schwächsten Glied (ESP) machen. Die HTML einfach so wie sie ist zu übertragen und nachträglich die Prozessdaten nachladen ist viel einfacher und Ressourcen schonender.

Gast #7815241
Lesenswert?

N. M. schrieb:

Das kannst du ja machen. Aber du musst das ja nicht auf dem schwächsten Glied (ESP) machen. Die HTML einfach so wie sie ist zu übertragen und nachträglich die Prozessdaten nachladen ist viel einfacher und Ressourcen schonender.

Ich behalte es im Kopf dass es so geht. Aber da muss ich mich erst einarbeiten. Webprogrammierung ist Neuland für mich, bin froh dass ich was angezeigt bekomme und das Daten hin und her laufen. Sobald die Notwendigkeit besteht schaue ich mir das mal an.

Im grunde stehen da texte wie Spannung = x.yz Volt

und Infotexte über den betriebsmode, sowie Bargraphen aus * und #, die anzeigen wie voll der Akkku ist.

#7815263
Lesenswert?

Thorsten M. schrieb:

Irre was die KI da leistet!

Der Code ist irre. Irre scheiße. Eine wilde strcat-Orgie. Alleine schon die Verwendung von strcat(). Dann noch ohne die aktuelle Belegung des Puffers mit zu zählen. Statt dessen der Glaube an:

Da sie den ganzen Code hatte wurde auch Buffer gleich richtig dimensioniert

Sicher, sicher ... Sogar wenn die KI wirklich für den gegebenen Fall richtig mit gerechnet hat (hast du das selber nachgerechnet oder behauptest du das nur?), spätestens wenn jemand anfängt an den Strings was zu ändern ist die ganze KI-Berechnung fürn Arsch.

Der Code bettelt dann förmlich nach einem Pufferüberlauf weil er nicht robust geschrieben ist. Echter Scheißdreck.

Falls es also noch jemand nicht glaubt: String ist ein heisses Eisen!

Es hilft ungemein zu wissen was man tut, statt sich von einer KI verarschen zu lassen. Ehrlich, als offensichtlicher Anfänger rumzutröten wie man solchen Code zu schreiben hat ist lachhaft.

Wieder was dazugelernt :-)

Nein, du hast dich von einer KI verarschen lassen.

Gast #7815269
Lesenswert?

Hannes J. schrieb:

Der Code ist irre. Irre scheiße. Eine wilde strcat-Orgie.

Es ist ist in diesem Forum nur eine Frage der Zeit, wann die "Helden" mit narzisstischer Persönlichkeitsstörung auftauchen und jeden Thread in den Dreck ziehen, wo nicht alles so gemacht wird, wie sie es meinen. Gut, dass ich den Alptraum solche asozialen Kollegen zu haben schon lange hinter mir weiß. Ich beende daher meine Teilnahme in diesem Fred und wünsche alles Gute bis irgendwann mal wieder.

#7815312
Lesenswert?

Thorsten M. schrieb:

Link

Ich hab das fipps und den Server mit dieser Seite zum laufen gebracht: https://fipsok.de/Esp32-Webserver/Esp32

Aber Achtung, es sind einige Bugs drinn, und völlig unkommentiert!

Hier wird alles ganz genau erklärt: https://wolles-elektronikkiste.de/wlan-mit-esp8266-und-esp32

Arbeite mich momentan auch da rein, bis jetzt habe ich es auf einem ATmega2560 gemacht nach: https://werner.rothschopf.net/202001_arduino_webserver_post.htm

mit genau den Problemen! Allerdings mit der Stream-Library, da wird nicht der ganze Riesenstring am Schluss versendet, sondern Stück für Stück. Aber das mühsame zusammensetzen des HTML mit Einfügen der Daten blieb...

Gruss Chregu

#7815317
Lesenswert?

Thorsten M. schrieb:

Hannes J. schrieb:

Der Code ist irre. Irre scheiße. Eine wilde strcat-Orgie.

Es ist ist in diesem Forum nur eine Frage der Zeit, wann die "Helden" mit narzisstischer Persönlichkeitsstörung auftauchen und jeden Thread in den Dreck ziehen, wo nicht alles so gemacht wird, wie sie es meinen. Gut, dass ich den Alptraum solche asozialen Kollegen zu haben schon lange hinter mir weiß. Ich beende daher meine Teilnahme in diesem Fred und wünsche alles Gute bis irgendwann mal wieder.

Die Worte von Hannes sind schon sehr drastisch, und das geht so nicht.

In der Sache hat er aber recht.

#7815341
Lesenswert?

Thorsten M. schrieb:

Hast du mal einen Link wo man sich da einarbeiten kann wie ein async. Server arbeitet und wie man die Daten in diese Platzhalter einpackt?

https://github.com/ESP32Async/ESPAsyncWebServer?tab=readme-ov-file#respond-with-content-coming-from-a-file-containing-templates

in der processor-funktion dann (zum Beispiel oben) für " if (var == "BAT_VOLTAGE") " deine Spannung (formatiert als String) ausgeben.

(Und statt SPIFFS geht auch LittleFS)

#7815352
Lesenswert?

Das sieht ja gruselig aus, diese strcat Orgie. Spaghetticode at its best. Warum nimmt man nicht einfach sprintf, um die Ausgaben in einem Rutsch zu erzeugen?

Es ist auch praktisch, wenn Ausgaben immer nach dem gleichen Schema erfolgen, dann kann sie auch der Mensch besser parsen. Dann kann man sich dafür Hilfsfunktionen schreiben, denen dann nur die Unterschiede übergeben werden.

1
void make_answer(buff, prolog, value, epilog);
#7815356
Lesenswert?

Thorsten M. schrieb:

Der Code hier ist komplett KI erzeugt, auch die Variablennamen wurden angepasst.

Und der Code ist großer Mist, weil er /strcat/ benutzt. Damit gibt es überhaupt keinen Schutz vor Pufferüberläufen.

Wie groß ist "msg"?

Eben. Funktionen wie /strcpy/, /strcat/ oder auch /sprintf/ sollte man konsequent vermeiden, außer in Fällen, wo ganz offensichtlich ist, daß das Ziel niemals überschrieben werden kann.

Stattdessen verwendet man /strncpy/, /strncat/ und /snprintf/.

Thorsten M. schrieb:

Es ist ist in diesem Forum nur eine Frage der Zeit, wann die "Helden" mit narzisstischer Persönlichkeitsstörung auftauchen und jeden Thread in den Dreck ziehen, wo nicht alles so gemacht wird, wie sie es meinen.

So kann man natürlich auch reagieren. Ist dann halt nur blöd, wenn man a) nichts gelernt hat und b) der Code einem auf die Füße fällt.

#7815373
Lesenswert?

Peter D. schrieb:

Warum nimmt man nicht einfach sprintf, um die Ausgaben in einem Rutsch zu erzeugen?

Die 2025 Variante davon wäre fmt::format (oder std::format). https://en.cppreference.com/w/cpp/utility/format/format

Der kleinste ESP32 hat meines Wissens nach 520kB RAM, ich verstehe nicht wieso man da nicht einfach mit std::string arbeitet?

Ich hab z.B. eine kleine Anwendung die ein "Captive Portal" liefert. Im Code wird hierzu statisches HTML mit "{}" Plathaltern hinterlegt. Ein Element darin kann etwa so aussehen:

1
<td>
2
  <input type="text" id="sta_ssid" name="sta_ssid" list="list_sta_ssid" maxlength="32" value="{}">
3
</td>

Und diese Platzhalter lassen sich dann in einem Einzeiler auffüllen:

1
fmt::format_to_n(
2
  begin(my_std_string),
3
  my_std_string.capacity(),
4
  fmt::runtime(/*statisches HTML*/),
5
  arg1,
6
  arg2,
7
  // ...
8
);
#7815611
Lesenswert?

Hallo,

man sollte eine abschätzbare Mindestgröße des Strings anlegen. Im Grunde kennt man die Größe die man benötigt + etwas Reserve. Das wirkt der dynamischen Speicherreservierung entgegen. Was auf dem PC egal ist, ist auf einem µC nicht egal. Der Umgang damit ist entscheidend. Man lernt immer dazu.

Gast #7815652
Lesenswert?

Thorsten M. schrieb:

Falls es also noch jemand nicht glaubt: String ist ein heißes Eisen!

Das ist mir beim ESP8266 aufgefallen, mein erster Kontakt zu Arduino. Dass man schnell zu viel Speicher belegt war mir eigentlich schon vorher klar. Aber die vielen praktischen Methoden dieser Klasse können den Heap je nach Anwendung dermaßen fragmentieren, dass nach einiger Zeit trotz freiem Speicher nichts mehr geht.

Ich bevorzuge []char und die zugehörigen Funktionen der standard C Bibliothek. Sie sind weniger komfortabel, aber es ist offensichtlicher, was dabei im Speicher passiert.

Die Stream-Methoden readString() und readStringUntil() sind auch gemein, denn sie können mehr Daten lesen, als in den Stack passen. DOS Angriffe leicht gemacht :-)

Gast #7815863
Lesenswert?

Thorsten M. schrieb:

solche asozialen Kollegen

OT: was bitte soll an den bisherigen Reaktionen asozial oder narzisstisch sein? Meinungsverschiedenheiten und die Darstellung von Unzulänglichkeiten oder Verbesserungsmöglichkeiten haben auch rein gar nichts mit asozialem Verhalten oder gar Narzissmus zu tun. Schlimm ist hingegen, dass Du die Leute hier mit diesen Attributen titulierst.

ciao

Marci

(Firma: egal) #7815867
Lesenswert?

Harald K. schrieb:

Eben. Funktionen wie strcpy, strcat oder auch sprintf sollte man konsequent vermeiden, außer in Fällen, wo ganz offensichtlich ist, daß das Ziel niemals überschrieben werden kann.

Stattdessen verwendet man strncpy, strncat und snprintf.

Eigentlich sind seit C11 eher strcpy_s, strcat_s und sprintf_s als "secure" nachfolger vorgesehen (keine Ahnung ob der von Arduino verwendete GCC das unterstützt):

Gast #7815868
Lesenswert?

Peter schrieb:

Die Worte von Hannes sind schon sehr drastisch

Finde ich überhaupt nicht. Ja, er ist sehr deutlich geworden, aber sind wir denn inzwischen so zart besaitet, dass man solch harmloses Geschimpfe bzw. deren Verfasser gleich als asozial oder narzisstisch titulieren muss?

Ich habe auch keinerlei Verständnis und bin sehr empfindsam darüber, wie im Netz bzw. in social media miteinander umgegangen wird. Aber Hannes' Worte hätte ich jetzt sicher nicht in diese Schublade gepackt. Wenn natürlich nur noch das Warmduschergeplänkel von ChatGPT(*) als erträglich eingestuft wird, wird einem ein rauerer Ton verstörend vorkommen. Wie gesagt, ich bin auch ein großer Freund höflicher Umgangsformen. Aber ich habe auf mc schon sehr viel schlimmere Texte gelesen als in diesem Thread.

ciao

Marci

(*) Das ist jetzt nicht negativ gemeint. Mir gefällt die sehr höfliche und angenehme Ausdrucksweise von ChatGpt auch. Aber das muss meinetwegen nicht zum Standard der zwischenmenschlichen Kommunikation werden.

Gast #7815876
Lesenswert?

Peter D. schrieb:

Das sieht ja gruselig aus, diese strcat Orgie. Spaghetticode at its best. Warum nimmt man nicht einfach sprintf, um die Ausgaben in einem Rutsch zu erzeugen

Das ist der Maschine sowas von egal! Das ist der gleiche Quark wie mit den msg += String(..) + String ("Lalalaa") Gräbern, die sich in hunderten Programmen finden und auch die Libs strotzen vor Strings.

Für mich sieht das alles ordentlich aus und nachvollziehbar. Außerdem wird eben der "Riesenstring" gebraucht, der an send übergeben wird und bei etlichen "Bargraphen", die aus Ascii Zeichen bestehen und die berechnet werden müssen kommt printf an seine Grenzen.

  1. Es läuft einwandfrei durch und hat sich nie wieder aufgehängt
  2. Dank KI wenig Arbeit damit gehabt
  3. "Schönheit" des Codes interessiert hier niemanden, es muss einfach nur funktionieren und Platz hat der ESP soviel, dass es wumpe ist.

Danke für den Link, diese Async Sache schaue ich mir mal an, das scheint doch "fortschrittlicher" zu sein, wenn man es mal durchdrungen hat.

Und der Ton hier ist mir egal, ich bin nur froh solche Leute nicht um mich haben zu müssen. Den letzten haben wir rausgeworfen, der meinte andere Kollegen ständig abwerten zu müssen, die seinem "Genie" nicht huldigen, Narzissmus eben. Ich muss mich da nicht anpassen, ich drehe mich um und gehe.

1
 memset(msg, 0, sizeof(msg)); /* Wichtig, msg wieder löschen! */
2

3
  /* Document Header */
4
  strcat(msg, "<!DOCTYPE html>\
5
  <html lang=\"de\">\
6
  <head>\
7
    <title>ESP Solar Debug</title>\
8
    <style>\
9
         body {\
10
           background-color: #0A0A2A;\
11
           color: #2EFE2E;\
12
           font-family: Verdana, Arial, sans-serif;\
13
           font-size: 16px;}\
14
     </style>\
15
    <meta charset=\"UTF-8\">\
16
    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"/>\
17
    <meta http-equiv=\"refresh\" content=\"10\">\
18
  </head>");
19

20
  // Titel und Trennstrich
21
  strcat(msg, "<body>\
22
        <span style=\"color:#FFFF00\">\
23
          <h2>ESP32 Debug Tool<p></h2>\
24
        </span>\
25
        <pre>");
26

27
  /* Ab hier kommt normaler Text */
28
  char temp[64];  // Temporärer Puffer für formatierte Strings
29

30
int prozent;
31
  /* Gib einen prozentuale Ladezustand der Batterie an */
32
  char colorcode[30];  // Ausreichend Platz für den Farbcode
33
  /* Ladezustand nach unten 12.0 - 12.7, 12.7 - 14.2 nach oben */
34
  if (Data.UBatterie >= UBATT_FULL_RUHE) {
35
    prozent = (int)(((Data.UBatterie / 2.0f) - (UBATT_FULL_RUHE / 2.0f)) / ((BATT_BOOST / 2.0f) - (UBATT_FULL_RUHE / 2.0f)) * 100.0f);
36
    strcpy(colorcode, "<span style=\"color:#2EFE2E\">");  // Grün
37
  } else {
38
    prozent = (int)(((UBATT_FULL_RUHE / 2.0f) - (Data.UBatterie / 2.0f)) / ((UBATT_FULL_RUHE / 2.0f) - (BATT_LOW_LIMIT / 2.0f)) * -100.0f);
39
    strcpy(colorcode, "<span style=\"color:#FE2E2E\">");  // Rot
40
  }
41

42
  if (prozent < -100)
43
    prozent = -100;
44
  if (prozent > 100)
45
    prozent = 100;
46

47
  /* Bargraph erzeugen */
48
  strcat(msg, "Ladezustand      ");  // Text
49
  strcat(msg, colorcode);          // mit Colorcode
50

51
  for (int i = 0; i < (abs(prozent) / 10); i++) {
52
    strcat(msg, "#");
53
  }
54

55
  /* Textfeld abschliessen */
56
  dtostrf(prozent, 1, 0, fbuf);  // Konvertiere float zu String
57
  strcat(msg, " ");
58
  strcat(msg, fbuf);
59
  strcat(msg, "%</span><hr>");
#7815894
Lesenswert?

Sorry, der Code geht für mich gar nicht. Mag für ein Hobbyprojekt gehen. Aber wenn man nach 5 Jahren da Änderungen vornehmen muss, dann fragt man sich selber, was man damals für einen Mist gebaut hat. Oder man bekommt solch einen Code vom Kollegen, und darf sich mit obskuren Fehlern durch kleine Änderungen durchschlagen.

Beispiel: char colorcode[30]; strcpy(colorcode, "<span style="color:#FE2E2E">"); // Rot

Sobald man nur etwas an dem style ändert, reicht möglicherweise der Platz in colorcode nicht aus. Da ist doch schon ein Fehler vorbereitet.

Für mich ist das keine defensive, portable Programmierung, sondern einmaliger Hobby-Müll, der im Keller bleiben sollte. Aber auf keinen Fall veröffentlicht werden sollte. Davon lernt die KI nur das schlechte.

#7815899
Lesenswert?

Thorsten M. schrieb:

Das ist der Maschine sowas von egal! Das ist der gleiche Quark wie mit den msg += String(..) + String ("Lalalaa") Gräbern, die sich in hunderten Programmen finden und auch die Libs strotzen vor Strings.

Schön, wenn jemand C und C++ (oder C# etc.) dureinanderwirft. Die String-Überladung von "+=" gibt es in C gar nicht (da man da auch gar nichts überladen kann).

Bei C darf man sich um die Einhaltung von Speichergrenzen selber kümmern. Die interessieren den Compiler einen Dreck, weil der davon ausgeht, dass man weiß, was man tut.

#7815912
Lesenswert?

Thorsten M. schrieb:

memset(msg, 0, sizeof(msg)); /* Wichtig, msg wieder löschen! */

Es reicht völlig aus, nur das 1. Byte zu leeren:

1
msg[0] = 0;

Thorsten M. schrieb im Beitrag #7815876: prozent = (int)(((Data.UBatterie / 2.0f) - (UBATT_FULL_RUHE / 2.0f)) / ((BATT_BOOST / 2.0f) - (UBATT_FULL_RUHE / 2.0f)) * 100.0f);

Was soll die "/ 2.0f" Orgie bewirken? Das kürzt sich doch eh raus.

Gast #7815919
Lesenswert?

Peter D. schrieb:

Thorsten M. schrieb: prozent = (int)(((Data.UBatterie / 2.0f) - (UBATT_FULL_RUHE / 2.0f)) / ((BATT_BOOST / 2.0f) - (UBATT_FULL_RUHE / 2.0f)) * 100.0f);

Was soll die "/ 2.0f" Orgie bewirken? Das kürzt sich doch eh raus.

Da hast Du recht aber das ist nur für mich, damit ich weiss, dass die Batteriespannung / 2 geteilt wird. Mir sind die Spannungen von 12V geläufiger als die von 24V. Musst Du jetzt nicht verstehen, ich habe mir dabei was gedacht. Das kürzt sich schon beim Kompilieren raus. Im Übrigen gute Einwürfe aber von "Peda" kenne ich das nicht anders :-)

#7816004
Lesenswert?

Irgend W. schrieb:

Harald K. schrieb:

Eben. Funktionen wie strcpy, strcat oder auch sprintf sollte man konsequent vermeiden, außer in Fällen, wo ganz offensichtlich ist, daß das Ziel niemals überschrieben werden kann.

Stattdessen verwendet man strncpy, strncat und snprintf.

Eigentlich sind seit C11 eher strcpy_s, strcat_s und sprintf_s als "secure" nachfolger vorgesehen (keine Ahnung ob der von Arduino verwendete GCC das unterstützt):

https://learn.microsoft.com/en-us/cpp/c-runtime-library/security-enhanced-versions-of-crt-functions?view=msvc-170

Hmm, scheint das mit den _s ist ein Microsoft Vorschlag den sonst niemand so richtig unterstützt, damit bezweifle ich dass das auf ESP32 geht...

https://stackoverflow.com/questions/47867130/stdc-lib-ext1-availability-in-gcc-and-clang

#7816013
Lesenswert?

Bernhard M. schrieb:

Hmm, scheint das mit den _s ist ein Microsoft Vorschlag den sonst niemand so richtig unterstützt, damit bezweifle ich dass das auf ESP32 geht...

Die _s-Funktionen sind Teil des C11-Standards. Die hat jede C-Toolchain, die C11 unterstützt, im Gepäck. Auch auf einem ESP32.

C++ ist halt nicht C. Andere Programmiersprache, andere lib-Funktionen. Da gibt es die _s nicht.

Oliver

#7816066
Lesenswert?

Oliver S. schrieb:

Klaus schrieb:

Er hat sein Problem schneller gelöst, als er es ohne KI hätte können. Also alles richtig gemacht.

Bis ihm der Code spätestens nächste Woche auf die Füße fällt, und er dann noch ratloser da steht, weil er nun gar nicht mehr versteht, was da eigentlich passiert.

Wieder die Tatsachen verdreht. /Vorher/ stand er ratlos da und hatte mir sporadischen Crashes zu kämpfen. Jetzt läuft es stabil und das Problem ist gelöst.

Dass der Code nicht schön, nicht gut dokumentiert, und im Falle weiterer Änderungen fehleranfällig ist stelle ich nicht in Abrede.

Aber ChatGPT wirds schon irgend wie richten

Eben.

#7816072
Lesenswert?

Da das es hier wohl C++ ist, seit C++23 gibt es std::ospanstream:

1
#include <iostream>
2
#include <spanstream>
3
 
4
int main()
5
{
6
    char out_buf[29];
7
    std::ospanstream ost {
8
      std::span<char>{out_buf, sizeof(out_buf)-1} // Make sure there will be at least 1 byte left for a 0 byte
9
    };
10

11
    ost << "Test " << 123;
12

13
    *ost.span().end() = '\0'; // Important, 0 terminate the string!
14

15
    std::cout << "data: " << out_buf << std::endl;
16
    std::cout << "eof: " << ost.eof() << std::endl;
17
    std::cout << "good: " << ost.good() << std::endl;
18
    std::cout << "fail: " << ost.fail() << std::endl;
19
    std::cout << "bad: " << ost.bad() << std::endl;
20

21
    ost << '\n';
22
    ost << "The quick brown fox jumps over the lazy dog";
23

24
    *ost.span().end() = '\0'; // Important, 0 terminate the string!
25

26
    std::cout << "data: " << out_buf << std::endl;
27
    std::cout << "eof: " << ost.eof() << std::endl;
28
    std::cout << "good: " << ost.good() << std::endl;
29
    std::cout << "fail: " << ost.fail() << std::endl;
30
    std::cout << "bad: " << ost.bad() << std::endl;
31
}

In C könnte man sowas natürlich auch nachbauen, siehe Anhang.

Angehängte Dateien:
#7816078
Lesenswert?

Der Vollständigkeit halber, strcat hat 2 Nachteile:

  • Man muss aufpassen, dass man den Buffer wirklich gross genug gewählt hat. Sonst crashts.
  • Jedes mal, wenn man strcat aufruft, ist das wie "strcpy(dest+strlen(dest),src)". Anders gesagt, der String, den man erweitert, wird jedes mal nach dem 0 byte durchsucht.

(Ich verstehe ehrlich gesagt nicht, warum man strcpy nicht einen Pointer auf das Ende des Strings zurückgeben liess, aber das ist jetzt nunmal so, wie es ist)

#7816080
Lesenswert?

Daniel A. schrieb:

(Ich verstehe ehrlich gesagt nicht, warum man strcpy nicht einen Pointer auf das Ende des Strings zurückgeben liess, aber das ist jetzt nunmal so, wie es ist)

Wenn du das für so sinnvoll hälst, könntest du leicht einen Wrapper schreiben der das so macht.

Peter D. schrieb:

void make_answer(buff, prolog, value, epilog);

Hier hat Peda ausnahmsweise mal Abstraktion verstanden.

Genau so sollte man es machen: Man macht (String) Funktionen genau für die aktuelle Applikation. Und da drin kann man dann sämtliche str Sauereinen veranstalten die man will. Man kann die Implementierung jederzeit auch auf printf ändern oder auf sonst was. Und man muss genau in dieser Funktion, und nur dort, nach Fehler und Überläufen in str Funktionen suchen.

Einfach an zig Stellen im programm mit strcat rumpfuschen ist eben genau das: Pfusch.

#7816093
Lesenswert?

Cyblord -. schrieb:

Daniel A. schrieb:

(Ich verstehe ehrlich gesagt nicht, warum man strcpy nicht einen Pointer auf das Ende des Strings zurückgeben liess, aber das ist jetzt nunmal so, wie es ist)

Wenn du das für so sinnvoll hälst, könntest du leicht einen Wrapper schreiben der das so macht.

Ja, das ist einfach:

1
#include <stdio.h>
2
#include <string.h>
3

4
char* strecat(char* dst, const char* src){
5
  dst += strlen(dst);
6
  while( *dst++ = *src++ );
7
  return dst-1;
8
}
9

10
int main(){
11
  char out[32];
12
  char* it = out;
13
  *it = 0;
14
  it = strecat(it, "Hello ");
15
  it = strecat(it, "World!");
16
  puts(out);
17
}
Moderator Persönliche Seite #7816131
Lesenswert?

Warum muss eigentlich der komplette Output zuvor in einem riesengroßen Buffer landen? Man kann durchaus zwischendurch auch mal irgendeine write-Funktion aufrufen, um schon mal einen Teil des Ausgabestrings loszuwerden. So kann man die benötigte Buffergröße auf einen sinnvollen Wert eindampfen.

Möchte man doch erst den kompletten Buffer füllen, dann noch eine Bemerkung zum vorgeschlagenen strcat() in der C-Variante:

Abgesehen von möglichen Buffer-Overflows ist diese Funktion für große Buffer extrem ineffizient. Das liegt daran, dass strcat() jedes mal die aktuelle Stringlänge des Buffers erst ermitteln muss, bevor es dann etwas anhängen kann. Das macht strlen() in der Regel durch Iteration über alle Zeichen im Buffer.

Führt man hier aber immer die Länge des Buffers mit, dann wird aus:

1
strcat (buffer, str);

ein:

1
static int len = 0;
2
...
3
strcpy (buffer + len, str);
4
len += strlen (str);

Dann wird die Stringlänge nur für die kleinen anzuhängenden Strings ermittelt und nicht jedes Mal für den riesengroßen Brocken. Das geht wesentlich schneller.

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