Webserver Tabellen formatieren

Moin,

ich bin gerade dabei einen nodeMCU zum Webserver zu basteln.
Dabei soll einfach nur auf einer lokalen Website ca. 100 verschiedene Messdaten dargestellt werden.

Den Webserver hab ich schon laufen, die Messdaten werden später implementiert (die Daten werde ich wahrscheinlich per MQTT einlesen, die Kanalfarben würde ich ebenfalls gerne aus einer Datenbank einlesen und für die Kanäle verwenden).

Nun ist die Frage, ob es irgendwo ein gute Tutorial gibt, wie man die Website mit Tabellen und Farben formatieren kann?

Ich hab schon einiges gefunden, aber nichts was dieses intuitiv erklärt.

Der HTML Teil sieht momentan noch so aus (aus einem Beispiel herauskopiert und bereits begonnen dies anzupassen):

client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close");  // the connection will be closed after completion of the response
client.println("Refresh: 5");  // refresh the page automatically every 5 sec
client.println();
client.println("<!DOCTYPE html>");
client.println("<html xmlns='http://www.w3.org/1999/xhtml'>");
client.println("<head>\n<meta charset='UTF-8'>");
client.println("<title>Volkszähler</title>");
client.println("</head>\n<body>");
client.println("<H2>HelloWorld</H2>");
client.println("<pre>");
client.print("Humidity (%)         : ");
client.println((float)10, 2);
client.print("Temperature (°C)  : ");
client.println((float)20, 2);
client.print("Temperature (°F)  : ");
client.println(Fahrenheit(20), 2);
client.print("Temperature (°K)  : ");
client.println(Kelvin(20), 2);
client.println("</pre>");
client.print("</body>\n</html>");

Wofür dient die folgende Zeile?!?!:

client.println("<html xmlns='http://www.w3.org/1999/xhtml'>");

Die eigentliche Frage ist, ob es irgendwo ein intuitives Tutorial gibt, wie man eine Tabelle mit Farben erstellt? Ich habe da noch nichts brauchbares gefunden.

Danke & lieben Gruß,
Chris

Hallo Chris,
hier anfangen und dann damit weitermachen.
Gruß Walter

themanfrommoon:
Nun ist die Frage, ob es irgendwo ein gute Tutorial gibt, wie man die Website mit Tabellen und Farben formatieren kann?

Hier sollte alles stehen, was Du suchst.

Gruß

Gregor

Ansonsten hier.

Tipp: Nimm besser HTML5 und nicht xhtml.

Gruß Tommy

Moin,

Das alles sind schonmal sehr gute Tipps, vielen Dank!

Tommy56:
Tipp: Nimm besser HTML5 und nicht xhtml.

Nach kurzer Recherche scheint XHTML definierter zu sein.
Aus welcher Erfahrung und welchen Gründen rätst du denn zu HTML5?

Und wer oder was sind diese W3? W3.org, w3schools.com?
Was macht das w3.org in dem Code? Siehe mein Eröffnungspost?

Lieben Gruß,
Chris

Hallo,

warum legst Du den statischen html code nicht auf dem Filesystem des NodeMCU Moduls ab. Dann kannst Du das vorher auf dem PC testen. Anschliesend holst Du die Daten mit eine einem fetch in einem kleinen Javaskript auf die Seite.

Die Links zu html Tutorials hast Du ja schon bekommen.

Schau Dr das mal auf der Web Seite von Fips`` oder auch hier an.

ich habe mal für Anfänger eine kleines Tutorial dazu geschrieben Webserver mit fetch

Heinz

client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");

tu dir selber was gutes und nutze am ESP den ESP8266Webserver Basisbeispiel HelloServer

Heute würde ich xhtml nicht mehr neu beginnen. Hab zwar auch noch eine Seite drauf laufen, aber nimm lieber html5. Warum kein xhtml - weil html5 gefühlt 10 Jahre jünger ist.

Welches Problem hast mit der Tabelle?
Einleiten,
Überschriftszeile vs "normale" Zeilen
Zellen
Zeile Abschließen
wenn fertig Tabelle abschnließen.

Farben machst mit CSS wie sonst auch.

85% deiner Tabellen hast damit erledigt. Was brauchst noch?

themanfrommoon:
Nach kurzer Recherche scheint XHTML definierter zu sein.
Aus welcher Erfahrung und welchen Gründen rätst du denn zu HTML5?

XHTML war eher ein Zwischenstand auf Basis von HTML 4.01 mit vielen Anleihen bei XML. Er ist wesentlich kritischer zu schreiben.

HTML 5 ist der aktuelle Standard.

Gruß Tommy

Edit: w3 ist eine andere Schreibweise für www, den Rest verrät Dir die Suchmaschine Deiner Wahl.

Bei Fips findest Du in onboardled.html ein Beispiel zum situationsbedingten Umschalten von backgroundColor.

Ich lagere html, ico und css im SPIFFS mit Aktualisierung der Anzeige durch Javascript. Für mich war Javascript die Herausforderung, aber dank Fips funktioniert es nun.

themanfrommoon:
...
Nach kurzer Recherche scheint XHTML definierter zu sein.
Aus welcher Erfahrung und welchen Gründen rätst du denn zu HTML5?
...

Nur mal so aus dem Nähkästchen: Bei HTML bin ich bei Version 4.01 stehengeblieben. Zum Einen hatte ich nie Bock mich neben vielen anderen Sachen auch noch in eine aktuellere Version hineinzuarbeiten, außerdem kann ich damit alles anstellen, was ich brauche und damit hat m.W.n. wirklich kein Browser irgendwelche Probleme - da wird alles von jedem Browser voll unterstützt.
Java/-script, XHTML u.dgl. bieten nette Gimmicks, aber alles, was auf der Plattform des Betrachters läuft, muss einem Stand entsprechen, der lange Zeit nicht überall gegeben war. Deshalb musste man (müsste man vielerorts m.E. immer noch) sog. Browser-Weichen programmieren, wenn man's nicht per Copy&Paste machen wollte.

Da die Qualität von Hard- und Software in den letzten Jahren eher ab- als zugenommen hat, halte ich mich an „nimm, was funktioniert (und zwar immer und überall)“.

Gruß

Gregor

es gibt Elemente in html5 die du mit html 4.01 nicht bekommst. Jeder halbwegs aktuelle Browser verteht heute html5. Außer einem "never change a running system" gibts kein Argument auf html 4.01 zu bleiben. Für eine Neuentwicklung aus meiner Sicht noch weniger

noiasca:
es gibt Elemente in html5 die du mit html 4.01 nicht bekommst.

Habe ich etwas Anderes behauptet?

Aus meiner Sicht braucht man vieles nicht. Die Möglichkeiten, die seit HTML 4.01 hinzugekommen sind, sind nach meinem Eindruck größtenteils lediglich dazu da, Webseiten komfortabler mit mehr „Hui“ ausstatten zu können. Einen echten Mehrwert, der einen zwingt, HTML 5 zu benutzen/zu lernen, konnte ich bislang nicht ausmachen.
Sachen wie Buttons, die ihre Farbe o.ä. ändern, wenn man mit der Maus darüberfährt, sind IMO überflüssiger Schnickschnack für Leute, die zu blöd sind, einen geänderten Mauszeiger zu erkennen.
Das Gleiche gilt für Sensorwerte, die sich zehntelsekundengenau ändern, denn derlei Zeug braucht man nur selten. Wenn sich der Inhalt einer Seite im Sekundentakt ändert, reicht das in den allermeisten Fällen vollkommen, und das bekommt man mit einem automatischen Refresh per Meta-Tag hin.

Gruß

Gregor

du vermischt jetzt Sachen die thematisch nicht zusammengehören.
Farbwechsel Buttons machst mit CSS, gerne auch schon mit html 4.01

Werte auf der Seite änderst mit JavaScript, die Daten nachladen mit fetch API oder xmlhttprequest. Auch das hat per se nichts mit html5 zu tun.

Eine Meta-Refresh jede Sekunde bringt zusätzlichen Traffic. Warum soll ich das ganze http wieder rauschicken, wenn ich einen Sensorwert auf der Seite ändern will. Außerdem hat so ein refresh einen Riesennachteil für mich:
schlägt ein Request fehl - ist die ganze Seite weg. Mach ich es mit Javascript, kann ich den Fehler abfangen und im nächsten Intervall wird der Sensor vieleicht wieder erfolgreich nachgeladen. Jedenfalls habe ich da volle Kontrolle darüber.

Was mir an html 4.01 fehlt wäre z.B. die native Unterstützung von slider (input range) oder die zerteile Gestaltungsmöglichkeit von forms.

noiasca:
Eine Meta-Refresh jede Sekunde bringt zusätzlichen Traffic. Warum soll ich das ganze http wieder rauschicken, wenn ich einen Sensorwert auf der Seite ändern will.

Das Traffic-Argument ist längst überholt. Sich wegen ein paar überflüssig übertragener Bytes in die Hosen zu machen und gleichzeitig Videos zu streamen ist doch albern. Das gilt IMO genauso für Sensorwerte, die sich im Zehntelsekundentakt ändern, obwohl man nur fünfmal am Tag hinguckt.

Wenn es um eilige Notfälle oder medizinisches Zeug geht, okay. Aber praxisrelevant sind zappelnde Werte ziemlich selten.

Nunja, manche glauben, dass sie ohne Sekundenzeiger nicht überleben können, mir genügt es, wenn meine Verabredung auf drei Minuten pünktlich kommt.

Gruß

Gregor

Moin,

jetzt wird es langsam im Detail interessant!

Ich hatte zwar noch keine Gelegenheit weiter zu machen, das wird sich aber in den nächsten Tagen ändern.

Ich habe aber schon jetzt etwas beobachten können:

Der Traffic ist wahrscheinlich tatsächlich relativ unwichtig, aber ich habe schon beobachtet, dass die ganze Seite nach einigen Minuten komplett weg ist und dann nicht wiederkommt. Da haben wir dann schon mal ein Thema.

Zehntelsekundenweise brauche ich die Daten nicht, sekundenweise wäre schon cool. Bisher hatte ich so 3-5 Sekunden anvisiert.

Farbe wechseln brauche ich nicht, aber ich möchte die Farben aus einer Datenbank auslesen und diese benutzen.

Das ganze dient dazu ein Gesamtübersicht zu bekommen, und dann auch noch etwas auszuwerten, wie z.B ob ein Wert in den vermuteten Grenzen liegt, wann der Wert das letzte mal refreshed wurde, und auch wie groß die Differenztemperaturen zwischen Vor- und Rücklauf sind. Ist auch kein Dauerbetrieb, sondern nur ein temporäres Monitoring. Für dauerhaft ist das zu unübersichtlich und zu detailliert. Reicht also locker wenn das mal ne halbe Stunde läuft. Aber dazu mehr. Ich muss erstmal anfangen und wahrscheinlich auch ein Testsketch bauen, der ohne die Datenbank funktioniert.

Ich melde mich wenn ich in ein paar Tagen soweit bin.

Lieben Gruß und lieben Dank schonmal für die ganzen wertvollen Informationen!
Chris

themanfrommoon:
Der Traffic ist wahrscheinlich tatsächlich relativ unwichtig,...
Zehntelsekundenweise brauche ich die Daten nicht, sekundenweise wäre schon cool. Bisher hatte ich so 3-5 Sekunden anvisiert.

Solange man entwickelt und sich in ein Thema einarbeitet, sind schnelle Aktualisierungen sicherlich hilfreich. Als ich meine kleine Browser-Startseite im Netz eingerichtet habe, war ein Refresh im 10 Sekunden-Takt ein muss - jetzt genügt mir, wenn die Bilder nur alle zehn Minuten neu geladen werden. Und ich bin ansich auch jemand, der auf Datensparsamkeit achtet und hin und wieder weit über das Ziel hinausschießt, aber ab und zu fällt mir ein, dass man mal ein Auge auf die Vehältnismäßigkeit werfen sollte. Ich find's halt hübsch, wenn ich mir den Quellcode einer Seite ansehen kann und auch 5 Jahre nach dem Programmieren noch weiß, was ich mir damals dabei gedacht habe.

Ich melde mich wenn ich in ein paar Tagen soweit bin.

Tu das!

Wenn Du Dich sowieso einarbeiten musst, nimm das aktuelle HTML. Mir genügt nach wie vor, was ich seit meinem Start mit HTML 3.2 gelernt habe.

Gruß

Gregor

gregorss:
Mir genügt nach wie vor, was ich seit meinem Start mit HTML 3.2 gelernt habe.

Ich kann mich noch an begeisterte Entwickler entsinnen, die endlich Daten und Layout voneinander trennen konnten, nannte sich XML. Als das dann im WWW zugänglich war, gab es eine Flut von "bei mir sieht das falsch aus" Meldungen, weil jeder Browser in jeder Version es anders darstellte. Anfängliche Versuche, nach Browsertypen zu unterscheiden, führten in ein nicht wartbares Gestrüpp von Verästelungen, weshalb dieser Ansatz wieder Richtung einfaches html beendet wurde.

themanfrommoon:
Ist auch kein Dauerbetrieb, sondern nur ein temporäres Monitoring.

Dann ist einfach einfach besser!

Dann kommst Du mit client.print schnell zum Ziel, also ohne Dateien im SPIFFS und ohne Javascript. Stylesheets kannst Du in html einbinden. Letztlich muß es nur mit Deinem Lieblingsbrowser vernünftig dargestellt werden.

Ich habe mit einem normalen Editor eine statische html-Datei erstellt, um die Gestaltung zu testen, dann diesen Text in die ino-Datei übertragen und erst dann die Variablen ergänzt.

Fips verwendet in Admin.ino das R-Makro, um eine Zeichenkette zusammenzusetzen. Möglicherweise gefällt Dir das.

Dann bis die Tage!

Bei Websiten die ein ESp zur Verfügung stellen soll, benutze ich folgendes:

Statisches HTML + CSS
Mit Javascript werden dann variable Daten nachgeladen,
die der ESP als JSON zur Verfügung stellt.

Den Vorteil sehe ich darin, das bei Änderungen am HTML das Programm nicht geändert werden muss, sondern nur das SPIFFS aktualisiert wird.
Das Statische HTML kann ich lokal testen, wodurch Compilieren und Flashen während der Testphase wegfällt.

Soweit, so gut, die grundsätzliche Funktion ist da:
Screenshot vom Edge und Firefox:

Und hier die Ausgabe am Serial Monitor:
MQTTSerialMonitor01.PNG

  1. Problem: Nach ein paar Refresh (15-20 Sekunden) gibt's einen Seiten-Ladefehler und es wird nichts mehr angezeigt. Ich kann dann zwar die Seite manuell wieder laden, aber so soll es ja nicht sein.
    Wie kann man verhindern, dass es zu diesen Seiten Ladefehlern kommt? Die Daten sollen gerne im 2-3 Sekunden Takt aktualisiert werden.

  2. Problem: Wie kann ich die MQTT Messages (Payload) in die Tabelle schreiben? (Da wo aktuell "vzlogger/data/chn1/raw" steht)

  3. Problem: Das Ganze muss noch skaliert werden. Mein Ziel sind rund 100 Werte. Nach dieser manuellen Methode wird das sehr lang und unübersichtlich (denke ich).

  4. Als Gimmick hätte ich dann noch gerne für einen kurzen Moment, z.B. 1-3 Sekunden soll die Zelle in einer anderen Farbe aufblinken, wenn sich ein Wert geändert hat, bzw. aktualisiert wurde.

Hier der Code:

#include <ESP8266WiFi.h>
#include <PubSubClient.h>

const char* ssid     = "xxxxxxxxxxxxxxxxxxxx"; // Your ssid
const char* password = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx"; // Your Password
const char* mqtt_server = "192.168.178.10";
int rssi = 0;
WiFiServer server(80);
WiFiClient espClient;
PubSubClient client(espClient);
long lastReconnectAttempt = 0;
long lastMsg = 0;
char msg[50];
int value = 0;

void setup() {
  Serial.begin(115200);
  setup_wifi();
  server.begin();
  Serial.println("Server started");
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);
  lastReconnectAttempt = 0;
}

void setup_wifi() {
  delay(10);
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
  Serial.print("RSSI: ");
  Serial.println(WiFi.RSSI());
}

void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
  Serial.print(" Stringlaenge: ");
  Serial.print(length);
  Serial.print(" ");
  Serial.println();
}

boolean reconnect() {
  Serial.print("Attempting MQTT connection...");
  if (client.connect("ESP8266Client")) {
    Serial.println("connected");
    client.subscribe("vzlogger/data/chn1/raw");
    client.subscribe("vzlogger/data/chn2/raw");
    client.subscribe("vzlogger/data/chn3/raw");
    client.subscribe("vzlogger/data/chn4/raw");
    client.subscribe("vzlogger/data/chn5/raw");
    client.subscribe("vzlogger/data/chn6/raw");
  } else {
    Serial.print("failed, rc=");
    Serial.println(client.state());
  }
  return client.connected();
}

void loop() {
  if (!client.connected()) {
    long now = millis();
    if (now - lastReconnectAttempt > 5000) {
      lastReconnectAttempt = now;
      if (reconnect()) {
        lastReconnectAttempt = 0;
      }
    }
  } else {
    // Client connected
    client.loop();
  }

  rssi = WiFi.RSSI();
  /*  Serial.print("RSSI: ");
    Serial.println(WiFi.RSSI());
  */
  WiFiClient client = server.available();
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html");
  client.println("Connection: close");  // the connection will be closed after completion of the response
  client.println("Refresh: 5");  // refresh the page automatically every 5 sec
  client.println();
  client.print("<!DOCTYPE html>");
  client.print("<html xmlns='http://www.w3.org/1999/xhtml'>");
  client.print("<head>\n<meta charset='UTF-8'>");
  client.print("<title>MQTT Monitor</title>");
  client.print("</head>\n<body>");
  //client.println("<H2>MQTT Monitor</H2>");
  client.print("<pre>");
  client.print("<font size=2>");
  client.print("RSSI       : ");
  client.println(WiFi.RSSI());
  //client.println("");
  client.println("<table border=1 cellspacing=0 cellpadding=0>");
  //client.println("<caption align=top>Daten:</caption>");
  client.println("<tr>");
  client.println("<th>Kanal</th>");
  client.println("<th>Wert</th>");
  client.println("</tr>");
  client.println("<tr>");
  client.println("<td align=right>Channel 1:</td>");
  client.println("<td>vzlogger/data/chn1/raw</td>");
  client.println("</tr>");
  client.println("<tr>");
  client.println("<td align=right>Channel 2:</td>");
  client.println("<td>vzlogger/data/chn2/raw</td>");
  client.println("</tr>");
  client.println("<tr>");
  client.println("<td align=right>Channel 3:</td>");
  client.println("<td>vzlogger/data/chn3/raw</td>");
  client.println("</tr>");
  client.println("<tr>");
  client.println("<td align=right>Channel 4:</td>");
  client.println("<td>vzlogger/data/chn4/raw</td>");
  client.println("</tr>");
  client.println("<tr>");
  client.println("<td align=right>Channel 5:</td>");
  client.println("<td>vzlogger/data/chn5/raw</td>");
  client.println("</tr>");
  client.println("<tr>");
  client.println("<td align=right>Channel 6:</td>");
  client.println("<td>vzlogger/data/chn6/raw</td>");
  client.println("</tr>");
  client.println("<tr>");
  client.println("<td align=right>Channel 120:</td>");
  client.println("<td>vzlogger/data/chn120/raw</td>");
  client.println("</tr>");
  client.println("</table>");
  client.println("</pre>");
  client.print("</body>\n</html>");
}

Lieben Gruß,
Chris

MQTTSerialMonitor01.PNG

1.) Du könntest eine Seite drumherum bauen, das per Javascript ein Iframe refreshed. Das kann dann auch Ladefehler abfangen.

2.)Also im callback() musst du das Topic soweit parsen, damit du einen Index für ein Array oder eine map hast, um dann die payload entsprechend abzuspeichern.

Und beim Generieren des HTML musst du an passender Stelle auf deine gespeicherten Daten zugreifen.

3.) Schleifen sind dein Freund. Irgendwie so ähnlich:

...
client.println("<tr>");
client.println("<th>Kanal</th>");
client.println("<th>Wert</th>");
client.println("</tr>");

for(int x=0; x<10; x++){
  client.println("<tr>");

  client.print("<td align=right>Channel ");
  client.print(x);
  client.println(":</td>");

  client.println("<td>");
  client.println(data[x]);
  client.println("</td>");

  client.println("</tr>");
}

client.println("</table>");
client.println("</pre>");
client.print("</body>\n</html>");
...

4.) Kannst beim generieren des HTML Farben per CSS an Tabellenzellen setzen. Wären dann aber nicht 1-3 Sekunden sondern bis zum nächsten Refresh so gesetzt. Müsstest dann aber im ESP den vorhergehenden Wert speichern, damit du entsprechend die Änderung feststellen kannst.