Forum: Mikrocontroller und Digitale Elektronik ESP32 und die "String" Funktion: Finger weg!


von Thorsten M. (Gast)


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

von N. M. (mani)


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?

: Bearbeitet durch User
von Εrnst B. (ernst)


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.

von Thorsten M. (Gast)


Lesenswert?

N. M. schrieb:
> Wo ist überhaupt die Frage?

Gibt keine :-) Dachte mir, das könnte jemand interessieren :-) Die 
Ablage im Filesystem wäre zu aufwendig, denn es werden viele Dinge erst 
berechnet und dann ein String daraus gebaut.

von Thorsten M. (Gast)


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.

von Εrnst B. (ernst)


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.

von N. M. (mani)


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.

von Thorsten M. (Gast)



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.

von Richard (user1234567890)


Lesenswert?

Thorsten M. schrieb:
> Der
> Stack ist nur 4bKB gross beim ESP32, die sind schnell voll mit lokalen
> Buffern.


Das ist doch Unsinn.
Der Stack ist so gross, wie Du ihn machst, natürlich je nach 
RAM-Ausstattung des ESP. Auf jeden Fall erheblich mehr als nur 4kB.

von Hannes J. (Firma: _⌨_) (pnuebergang)


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.

von Klaus (feelfree)


Lesenswert?

Hannes J. schrieb:
> , du hast dich von einer KI verarschen lassen

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

von Thorsten M. (Gast)


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.

von Thorsten M. (Gast)


Lesenswert?

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

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?

von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?


: Bearbeitet durch User
von Christian M. (christian_m280)


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

von Peter (pittyj)


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.

von Rainer W. (rawi)


Lesenswert?

Thorsten M. schrieb:
> Ich beende daher meine Teilnahme in diesem Fred ...

Das hast du ja nicht lange durchgehalten 😄

Thorsten M. schrieb:
> Hast du mal einen Link ...

: Bearbeitet durch User
von Rahul D. (rahul)


Lesenswert?

Komisch, dass man mit solchen Probleme schon vor 20 Jahren umgehen 
konnte.
Damals gab es den SitePlayer, basierend auf einem 20MHz-8051-Derivat.

Wer faul ist, braucht ne KI statt des eigene Hirnschmalzes.

von Εrnst B. (ernst)


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)

von Roger S. (edge)


Lesenswert?

Ach, da macht mal wieder einer 'String' handling nach Shlemiel [1]

[1] https://www.joelonsoftware.com/2001/12/11/back-to-basics/

von Peter D. (peda)


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

von Harald K. (kirnbichler)


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.

von Vincent H. (vinci)


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

von Veit D. (devil-elec)


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.

von Sherlock 🕵🏽‍♂️ (rubbel-die-katz)


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

: Bearbeitet durch User
von Arduino F. (Firma: Gast) (arduinof)


Lesenswert?

Sherlock 🕵🏽‍♂️ schrieb:
> 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 :-)
Stack?
Nee..
Die nutzen den Heap.

von Marci W. (marci_w)


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

: Bearbeitet durch User
von Irgend W. (Firma: egal) (irgendwer)


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

- https://en.cppreference.com/w/c/string/byte/strcat
- https://en.cppreference.com/w/c/string/byte/strcpy
- https://en.cppreference.com/w/c/io/fprintf
- 
https://learn.microsoft.com/en-us/cpp/c-runtime-library/security-enhanced-versions-of-crt-functions?view=msvc-170

von Marci W. (marci_w)


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.

von Thorsten M. (Gast)


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

von Peter (pittyj)


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.

von Klaus (feelfree)


Lesenswert?

Peter schrieb:
> Davon lernt die KI
> nur das schlechte.

Was interessiert dich die KI? Die brauchst und willst du doch gar nicht.

Ich wiederhole mich: der TE hat alles richtig gemacht.

von Rahul D. (rahul)


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.

von Peter D. (peda)


Lesenswert?

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

Wozu überhaupt erst einen konstanten String in eine Variable kopieren?

Ein Pointer reicht doch:
1
char* color = "";
2
color = "<span style=\"color:#FE2E2E\">";
3
// .. some code
4
sprintf(buff, "bla" "%s" "blub", color);

: Bearbeitet durch User
von Peter D. (peda)


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:
 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.

von Oliver S. (oliverso)


Lesenswert?

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.

Aber ChatGPT wirds schon irgend wie richten - hoffentlich.

Oliver

von Thorsten M. (Gast)


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

von Rahul D. (rahul)


Lesenswert?

Thorsten M. schrieb:
> Da hast Du recht aber das ist nur für mich, damit ich weiss, dass die
> Batteriespannung / 2 geteilt wird.

Man könnte auch einfach den Programmteil kommentieren, wie es sich für 
ordentlichen Quellcode gehört.

von Oliver S. (oliverso)


Lesenswert?

Thorsten M. schrieb:
> Das kürzt sich schon beim Kompilieren raus.

Nein, tut es nicht.

Oliver

von Obelix X. (obelix)


Lesenswert?

Thorsten M. schrieb:
> Ich beende daher meine Teilnahme in diesem Fred
> und wünsche alles Gute bis irgendwann mal wieder.

von Rahul D. (rahul)


Lesenswert?

Obelix X. schrieb:
> Thorsten M. schrieb:
>> Ich beende daher meine Teilnahme in diesem Fred

Das war gelogen.

von Bernhard M. (boregard)


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://en.cppreference.com/w/c/string/byte/strcat
> - https://en.cppreference.com/w/c/string/byte/strcpy
> - https://en.cppreference.com/w/c/io/fprintf
> -
> 
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

von Oliver S. (oliverso)


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

von Klaus (feelfree)


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.

von Daniel A. (daniel-a)


Angehängte Dateien:

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.

von Oliver S. (oliverso)


Lesenswert?

Klaus schrieb:
> Wieder die Tatsachen verdreht. Vorher stand er ratlos da und hatte mir
> sporadischen Crashes zu kämpfen.

Nee. Vorher hatte er Code, dessen Nebenwirkungen ihm unbekannt waren. 
Jetzt hat er Code, von dem er gar nichts mehr versteht.

Oliver

von Daniel A. (daniel-a)


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)

von Cyblord -. (cyblord)


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.

: Bearbeitet durch User
von Daniel A. (daniel-a)


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
}

von Frank M. (ukw) (Moderator) Benutzerseite


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.

: Bearbeitet durch Moderator
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.