ESP8266 Webserver Seite OHNE String-Orgie?

Hallo zusammen, ich bastel gerade ganz frisch (ca. 4 Wochen von Null an) an einer Solarsteuerung auf einem Sonoff DIY 4CH SV
mit kleinem WebIf zum Werte ändern.

Ich weiß (?), dass man 3 Möglichkeiten hat, die benötigte HTML-Struktur für die Oberfläche zu speichern:

  1. Im Sketch
  2. Im SPIFFs
  3. Im Web (Cloud)

Ich möchte meine 2 Seiten mit

  1. 4 Checkboxen und 6 Input type=number Feldern und einer
  2. Temp-Anzeige-Seite mit 4 Temps

im Sketch einbinden (erstmal!) :wink:

Gelesen habe ich, dass man generell auf strings verzichten soll,
da diese sehr Speicherintensiv sind -
finde aber in den Beispielen immer regelrechte "string-Orgien", um die Seiten darzustellen (string webpage += ).

Ist das noch der aktuelle Stand oder geht es moderner/besser/hübscher ?
Alternativ const char mit PROGMEM ?

Danke für eure Meinungen :slight_smile:

Du kannst Dir auch mal RAW-Stringliterale anschauen.

Gruß Tommy

progger:
2) Im SPIFFs
Danke für eure Meinungen :slight_smile:

Meine Meinung, die ich auch so umgesetzt habe: Dateien im ESP32-SIFFS (bei ESP8266 besser LittleFS), kurze Texte auch als const char. PROGMEM eher nicht.

Ich kenne das von Apache-Servern, ist also eher Gewohnheit bei mir als klug durchdachte Strategie ::slight_smile:

Du kannst versuchen HTML, CSS, Js, ... als statische Dateien ins SPIFFS zu legen und dann per Javascript mit einem AJAX-request (/Fetch-API) die variablen Daten vom ESP nachzuladen. Der ESP müsste dann eine REST-Schnittstelle die JSON zurückgibt zur Verfügung stellen.

Es gibt auch noch andere Varianten, als AJAX, z.B. die Fetch-API. Auch JSON/REST kann sein, muss es aber nicht.

Gruß Tommy

Gelesen habe ich, dass man generell auf strings verzichten soll,
da diese sehr Speicherintensiv sind -
finde aber in den Beispielen immer regelrechte "string-Orgien", um die Seiten darzustellen (string webpage += ).

am ESP ist das nicht so streng, und wenn die Größe des String entweder mit reserve vordefiniert ist oder eh nur eine Lokale Variable (besser Objekt) ist, dann ist das gar nicht mehr so böse.

Außerdem kannst du String mit dem F Makro leicht kombinieren.
Weiters braucht es auch nicht für jede Zeile ein eigenes webpage += sondern das kannst du auch über mehrere Zeile gehen lassen.

Beispiel:

{
 message += F("<h2>Homepage</h2>\n" // here you write your html code for your homepage. Let's give some examples...
              "<p>This is an example for a webserver on your ESP8266.<p>\n"
              "<p>Values are getting updated with AJAX/JSON. "
              "I'm still using old style XMLHttpRequest instead of fetch-API. It's up to "
              "the reader to update - but old style works fine for me ;-P</p>\n");

 message += F("<h2>Values (with update)</h2>\n");
 message += F("<p>Internal Voltage measured by ESP: <span id='internalVcc'>"); // example how to show values on the webserver
 message += ESP.getVcc();
 message += F("</span>mV</p>\n");

 message += F("<p>Button 1: <span id='button1'>"); // example how to show values on the webserver
 message += digitalRead(BUTTON1_PIN);
 message += F("</span></p>\n");
}

oder ein ganzes statisches CSS:

void handleCss()
{
// output of stylesheet
// this is a straight forward example how to generat a static page from program memory
String message;
message = F("*{font-family:sans-serif}\n"
             "body{margin:10px}\n"
             "h1, h2{color:white;background:" CSS_MAINCOLOR ";text-align:center}\n"
             "h1{font-size:1.2em;margin:1px;padding:5px}\n"
             "h2{font-size:1.0em}\n"
             "h3{font-size:0.9em}\n"
             "a{text-decoration:none;color:dimgray;text-align:center}\n"
             ".small{font-size:0.6em}\n"
             ".value{font-size:1.8em;text-align:center;line-height:50%}\n"
             "footer p, .infodaten p{font-size:0.7em;color:dimgray;background:silver;text-align:center;margin-bottom:5px}\n"
             "nav{background-color:silver;margin:1px;padding:5px;font-size:0.8em}\n"
             "nav a{color:white;padding:10px;text-decoration:none}\n"
             "nav a:hover{text-decoration:underline}\n"
             "nav p{margin:0px;padding:0px}\n"
             ".btn{background-color:#C0C0C0;color:dimgray;text-decoration:none;border-style:solid;border-color:dimgray}\n" //fake style for button
             ".on, .off{margin-top:0;margin-bottom:0.2em;margin-left:3em;font-size:1.4em;background-color:#C0C0C0;border-style:solid;width:5em;height:1.5em;text-decoration:none;text-align:center}\n"
             ".on{border-color:green}\n"
server.send(200, "text/css", message);
}

von hier: ESP8266 Webserver - HTML und CSS Stylesheet zum Gestalten der Webseite

Wenn ich Eine Seite so manuell mit fixen Inhalten und variablen Inhalten zusammestelle, dann verwende ich das F-Makro und unterbreche eben für die variablen Inhalte.

Tommy56:
Es gibt auch noch andere Varianten, als AJAX, z.B. die Fetch-API. Auch JSON/REST kann sein, muss es aber nicht.

Gruß Tommy

Was könnte man noch nehmen? Bei SOAP und XML hätte ich Bedenken wegen des Overheads.

Einfache durch ein Trennzeichen getrennte Werte wäre noch eine Möglichkeit. Bei 4 Temperaturen auch einfach realisierbar und auf JS-Seite auch einfach mit split zu verarbeiten.

Gruß Tommy

bei 4 Temperaturen wäre mir der "Overhead" von ein paar JSON Bezeichnern aber wirklich egal, ... da möchte ich nicht auf die leichte Adaptierbarkeit verzichten wollen.

Er hatte gefragt, was man noch nehmen könnte. Man muss es nicht.
Ich würde auch ein JSON nehmen, bei dem die Keys so heißen, wie die ID der Zielfelder.

Gruß Tommy

Tommy56:
Einfache durch ein Trennzeichen getrennte Werte wäre noch eine Möglichkeit. Bei 4 Temperaturen auch einfach realisierbar und auf JS-Seite auch einfach mit split zu verarbeiten.

Gruß Tommy

Da müsstest du vermutlich auf beiden Seiten (ESP und Browser) die (De-)Serialisierung schreiben und die Erweiterbarkeit halte ich für begrenzt. Wie vermeidest du dabei eine String-orgie auf ESP-Seite?

Rintin:
Wie vermeidest du dabei eine String-orgie auf ESP-Seite?

Mit snprintf.

Gruß Tommy

noiasca:
Sorry, worum gehts dir jetzt?

Ich möchte vermeiden, das man ein Datenformat erfindet, das einem bei der ersten Erweiterung direkt auf die Füße fällt.

Darum ging es doch überhaupt nicht. Du wolltest issen, welche Möglichkeit es noch gibt und da habe ich Dir eine aufgezeigt.
Es hat keiner behauptet, dass man die verwenden sollte/müsste. Also ist die Diskussion um ein "neues Datenformat" eigentlich fruchtlos.

Ich habe nur andere mögliche Formate ins Spiel gebracht, da Dein Beitrag so klang, als gäbe es nur JSON, was ja nicht der Fall ist.

Gruß Tommy

progger:
Ist das noch der aktuelle Stand oder geht es moderner/besser/hübscher ?

Du möchtest es hübscher?

siehe: #1

mit C Strings

void espLed() {
  if (server.hasArg("led")) {
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));        // LED umschalten
    Serial.println(digitalRead(LED_BUILTIN) ? "LED ist aus" : "LED ist an");
  }
  char temp[760];
  snprintf(temp, sizeof temp, R"(<!DOCTYPE html>
<html lang="de">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
      body {
        background-color: #87cefa;
        display: flex;
        flex-flow: column;
        align-items: center;
      }
      input {
        height: 2.5em;
        width: 8em;
        font-size: 1em;
      }
      [value$=n] {
        background-color: #adff2f;
      }
      [value$=s] {
        background-color: red;
      }
    </style>
    <title>Onboard Led</title>
  </head>
  <body>
    <h2>Onboard Led schalten</h2>
    <h3>ESP8266</h3>
    <p>LED ist %s<p>
    <form action="/led">
      <input name="led" type="submit" value="LED %s">
    </form>
  </body>
</html>)", digitalRead(LED_BUILTIN) ? "aus" : "an", digitalRead(LED_BUILTIN) ? "Ein" : "Aus");
  server.send(200, "text/html", temp);
}

mit Arduino String

void espLed() {
  if (server.hasArg("led")) {
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));        // LED umschalten
    Serial.println(digitalRead(LED_BUILTIN) ? "LED ist aus" : "LED ist an");
    server.send(204);
  }
  String temp = R"(
<!DOCTYPE html>
<html lang="de">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
      body {
        background-color: #87cefa;
        display: flex;
        flex-flow: column;
        align-items: center;
      }
      input {
        height: 2.5em;
        width: 8em;
        font-size: 1em;
      }
    </style>
    <title>Onboard Led</title>
  </head>
  <body>
    <h2>Onboard Led schalten</h2>
    <h3>ESP8266</h3>
    <form action="/led" method="POST">
      <input name="led" type="submit" value="ON / OFF">
    </form>
  </body>
</html>)";
  server.send(200, "text/html", temp);
}

oder auch mit C++ string Objekten

#include <string>

void espLed() {
  if (server.args()) {
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));        // LED umschalten
    Serial.println(digitalRead(LED_BUILTIN) ? "LED ist aus" : "LED ist an");
    server.send(304, "message/http");
  }
  string temp = R"(
<!DOCTYPE html>
<html lang="de">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
      body {
        background-color: #87cefa;
        display: flex;
        flex-flow: column;
        align-items: center;
      }
      button {
        height: 2.5em;
        width: 8em;
        font-size: 1em;
      }
    </style>
    <title>Onboard Led</title>
  </head>
  <body>
    <h2>Onboard Led schalten</h2>
    <h3>ESP8266</h3>
    <button id="b">
      Press
    </button>
    <script>
      b.addEventListener('click', () => { fetch('/led?p');});
    </script>
  </body>
</html>)";
  server.send(200, "text/html", temp.c_str());
}

Schau dir den Quelltext im Browser an, er ist genau so formatiert wie hier zu sehen!

Gruß Fips

Wow Leute, das ist mal viel Input für einen Anfänger :slight_smile:
Aber ich habe es ja so gewollt... :smiley:

Danke an alle!
Ich werde mich da mal durchkämpfen... :astonished:

Aber so ganz "falsch" wäre es ja mit einer string-Orgie dann doch nicht (so wie ich das überblicke mit meinem Anfängerwissen) 8)

progger:
Ich werde mich da mal durchkämpfen... :astonished:

Mir haben die Seiten von Fips geholfen, die ersten Hürden zu überqueren. Anleitung: Einführung zu fipsok.de

Ich nochmal :confused:

Nach weiteren Recherchen fand ich noch folgende Schreibeweisen, um die Seite im Sketch zu coden:

  1. const char index_html[] PROGMEM = R"rawliteral(

  2. const char index_html[500] PROGMEM = R"rawliteral(

  3. client.print

Diese decken sich teilweise mit den schon von euch vorgeschlagenen, aber der Teufel sitzt (für mich noch) im Detail:

  • Warum lieber keine PROGMEM?
  • Warum wird beim 2. Beispiel die Größe 500 mit vorgegeben? Und wie wurde diese vorher ermittelt? Ist
    es nicht besser, die Größe offen zu lassen?
  • Welchen Nachteil hat es, client.print zu verwenden? Das sah für mich recht anfängerfreundlich aus...

Danke für eure Einschätzungen aller Art :slight_smile: Ich möchte aber auch nicht nerven...

  1. const char index_html[500] PROGMEM = R"rawliteral(

kam von niemanden hier, also eher nicht sinnvol hier danach zu fragen. Und nein, ich würde das nicht so machen.

ad

Welchen Nachteil hat es, client.print zu verwenden? Das sah für mich recht anfängerfreundlich aus...

sehr vereinfacht: jedes client.print wird als separates Packerl auf die Reise geschickt, damit zerstückelst du dir auf TCP/IP Ebene deine Nachricht.

In deinem Ausgangspost ging es aber um String Concatinierungen, nicht um einzelne client.prints.

Viele Client.prints deuten auf falsche Beispiele für den ESP8266.

Daher nimm den ES8266Webserver | HelloServer als Beispiel und bastle dir deine Message in einem Buffer zusammen und übergib das der jeweiligen callback Methode.

Wenn das klappt, und du mal rein statische Texte (CSS, JS,...) hast, dann schau dir noch mal das Post von Fips an. Und noch mal. Und noch mal.

Am Schluss dann das Filesysytem, wie man dorthin Daten hochladet und wie man aus dem FS dann direkt die statischen Dateien schickt.

OK Danke :slight_smile:

Wegen PROGMEM kam ich deswegen nochmal drauf:

agmue:
kurze Texte auch als const char. PROGMEM eher nicht.

Es wird halt in vielen Sketches verwendet und das warum "eher nicht" wollte ich noch verstehen...

Und warum mal die Größe mit übergeben wird und mal nicht?