HTML Kochbuch mit ESP8266 und Arduino IDE

Mit der Arduino IDE kann man Programme für den ESP8266 entwickeln, die über WLAN und WEB-Browser die Anwendung steuern. Die Benutzeroberfläche und die Benutzeraktionen werden mit Hilfe von HTML definiert.

Der Vorteil dieses Konzepts liegt darin, dass man mit PC, Tablet oder Smartphone die Anwendung steuern kann. Das ist unabhängig von Betriebssystem und Gerätehardware möglich. WLAN-Fähigkeit und Web-Browser sind ohnehin so gut wie immer vorhanden und reichen völlig aus.

Hier wird gezeigt wie man mit der Arduino IDE Programme für den ESP8266 schreiben kann, die die HTML-Seiten aufbauen, an den WEB-Partner senden und die empfangenen Benutzereingaben auswerten.

HTML-Editoren können hier nicht eingesetzt werden, Die HTML-Seiten müssen Element für Element selbst definiert werden.

Hier das erste Beispiel. Es sieht so aus :
 HTML_Kochbuch_Bild1

In diesem Beispielen wird nichts gesteuert. Es werden nur die Klicks auf „OK“ und „nicht OK“ gezählt. Die Besonderheit : Es wird auch ein kleines Bild dargestellt.

Das dazugehörenden Beispielprogramm mutet auf den ersten Blick ein wenig „oversized“ an, weil es viele Unterroutinen enthält, die teilweise hier nicht aufgerufen werden.

Diese Routinen werden jedoch für andere Steuerelemente benötigt. Das Programm eignet sich in der vorliegenden Form als Kopiervorlage. Es enthält die wichtigsten Unterroutinen und kann auf einfache Weise ausgebaut werden.

Coding
//---------------------------------------------------------------------
//ESP866 HTML Demo 01
//---------------------------------------------------------------------
// Author  : Hubert Baumgarten
//---------------------------------------------------------------------
#include <ESP8266WiFi.h>
#ifdef __AVR__
#include <avr/power.h>
#endif

#include "bild1.c"

#define BGTDEBUG 1

//---------------------------------------------------------------------
// WiFi

byte my_WiFi_Mode = 0;  // WIFI_STA = 1 = Workstation  WIFI_AP = 2  = Accesspoint

const char * ssid_sta     = "<your SSID>";
const char * password_sta = "<Your Password>";

const char * ssid_ap = "ESP_HTML_01";
const char * password_ap = "";    // alternativ :  = "12345678";

WiFiServer server(80);
WiFiClient client;

#define MAX_PACKAGE_SIZE 2048
char HTML_String[5000];
char HTTP_Header[150];

//---------------------------------------------------------------------
// Allgemeine Variablen

int ok_count = 0;
int notok_count = 0;
int Aufruf_Zaehler = 0;

#define ACTION_OK 1
#define ACTION_NOTOK 2
int action;

//---------------------------------------------------------------------
void setup() {
#ifdef BGTDEBUG
  Serial.begin(115200);

  for (int i = 10; i > 0; i--) {
    Serial.print("Warte ");
    Serial.print(i);
    Serial.println(" sec");
    delay(1000);
  }
  Serial.println("ESP_HTML_01");
#endif

  //---------------------------------------------------------------------
  // WiFi starten
  WiFi_Start_STA();
  if (my_WiFi_Mode == 0) WiFi_Start_AP();
}

//---------------------------------------------------------------------
void loop() {
  WiFI_Traffic();
  delay(10);
}

//---------------------------------------------------------------------
void WiFi_Start_STA() {
  unsigned long timeout;

  WiFi.mode(WIFI_STA);   //  Workstation

  WiFi.begin(ssid_sta, password_sta);
  timeout = millis() + 12000L;
  while (WiFi.status() != WL_CONNECTED && millis() < timeout) {
    delay(10);
  }

  if (WiFi.status() == WL_CONNECTED) {
    server.begin();
    my_WiFi_Mode = WIFI_STA;

#ifdef BGTDEBUG
    Serial.print("Connected IP - Address : ");
    for (int i = 0; i < 3; i++) {
      Serial.print( WiFi.localIP()[i]);
      Serial.print(".");
    }
    Serial.println(WiFi.localIP()[3]);
#endif
  } else {
    WiFi.mode(WIFI_OFF);
#ifdef BGTDEBUG
    Serial.println("WLAN-Connection failed");
#endif
  }
}

//---------------------------------------------------------------------
void WiFi_Start_AP() {
  WiFi.mode(WIFI_AP);   // Accesspoint
  WiFi.softAP(ssid_ap, password_ap);
  server.begin();
  IPAddress myIP = WiFi.softAPIP();
  my_WiFi_Mode = WIFI_AP;

#ifdef BGTDEBUG
  Serial.print("Accesspoint started - Name : ");
  Serial.print(ssid_ap);
  Serial.print( " IP address: ");
  Serial.println(myIP);
#endif
}
//---------------------------------------------------------------------
void WiFI_Traffic() {

  char my_char;
  int htmlPtr = 0;
  unsigned long my_timeout;

  // Check if a client has connected
  client = server.available();
  if (!client)  {
    return;
  }

  my_timeout = millis() + 250L;
  while (!client.available() && (millis() < my_timeout) ) delay(10);
  delay(10);
  if (millis() > my_timeout)  {
#ifdef BGTDEBUG
    Serial.println("Client connection timeout!");
#endif
    return;
  }
  //---------------------------------------------------------------------
  htmlPtr = 0;
  my_char = 0;
  while (client.available() && my_char != 'r') {
    my_char = client.read();
    HTML_String[htmlPtr++] = my_char;
  }
  client.flush();
  HTML_String[htmlPtr] = 0;
#ifdef BGTDEBUG
  Serial.println ("--------------------------------------------------------");
  Serial.print("Remote IP - Address : ");
  for (int i = 0; i < 3; i++) {
    Serial.print( client.remoteIP()[i]);
    Serial.print(".");
  }
  Serial.println(client.remoteIP()[3]);

  exhibit("Remote Port ", client.remotePort());
  exhibit ("Request : ", HTML_String);
#endif
  Aufruf_Zaehler++;
  if (Find_Start ("bild1.gif", HTML_String) > 0) {
    send_bin(Bild1, BILD1_LEN, "image/gif", "bild1.gif");
    return;
  }

  if (Find_Start ("/?", HTML_String) < 0 && Find_Start ("GET / HTTP", HTML_String) < 0 ) {
    send_not_found();
    return;
  }

  //---------------------------------------------------------------------
  // Benutzereingaben einlesen und verarbeiten
  //---------------------------------------------------------------------
  action = Pick_Parameter_Zahl("ACTION=", HTML_String);
  if ( action == ACTION_OK) ok_count++;
  if ( action == ACTION_NOTOK) notok_count++;

  //---------------------------------------------------------------------
  //Antwortseite aufbauen
  make_HTML01();

  //---------------------------------------------------------------------
  // Header aufbauen
  strcpy(HTTP_Header , "HTTP/1.1 200 OKrn");
  strcat(HTTP_Header, "Content-Length: ");
  strcati(HTTP_Header, strlen(HTML_String));
  strcat(HTTP_Header, "rn");
  strcat(HTTP_Header, "Content-Type: text/htmlrn");
  strcat(HTTP_Header, "Connection: closern");
  strcat(HTTP_Header, "rn");

#ifdef BGTDEBUG
  exhibit("Header : ", HTTP_Header);
  exhibit("Laenge Header : ", strlen(HTTP_Header));
  exhibit("Laenge HTML   : ", strlen(HTML_String));
#endif

  client.print(HTTP_Header);
  delay(20);

  send_HTML();

}

//---------------------------------------------------------------------
// HTML Seite 01 aufbauen
//---------------------------------------------------------------------
void make_HTML01() {

  strcpy( HTML_String, "<!DOCTYPE html>");
  strcat( HTML_String, "<html>");
  strcat( HTML_String, "<head>");
  strcat( HTML_String, "<title>HTML Demo</title>");
  strcat( HTML_String, "</head>");
  strcat( HTML_String, "<body bgcolor="#adcede">");
  strcat( HTML_String, "<font color="#000000" face="VERDANA,ARIAL,HELVETICA">");
  strcat( HTML_String, "<h1>HTML Demo   ");
  strcat( HTML_String, "<img src="bild1.gif" alt="mein Bild"></h1>");

  strcat( HTML_String, "<form>");

  strcat( HTML_String, "<button style= "width:120px" name="ACTION" value="");
  strcati(HTML_String, ACTION_OK);
  strcat( HTML_String, "">OK</button>");

  strcat( HTML_String, "   ");

  strcat( HTML_String, "<button style= "width:120px" name="ACTION" value="");
  strcati(HTML_String, ACTION_NOTOK);
  strcat( HTML_String, "">nicht OK</button>");

  strcat( HTML_String, "</form>");

  strcat( HTML_String, "<BR>");

  strcat( HTML_String, "<FONT SIZE=-1>");
  strcat( HTML_String, "Aufrufzähler : ");
  strcati(HTML_String, Aufruf_Zaehler);
  strcat( HTML_String, "  Anzahl OK : ");
  strcati(HTML_String, ok_count);
  strcat( HTML_String, "  Anzahl NOK : ");
  strcati(HTML_String, notok_count);
  strcat( HTML_String, "<br>");

  strcat( HTML_String, "</font>");
  strcat( HTML_String, "</font>");
  strcat( HTML_String, "</body>");
  strcat( HTML_String, "</html>");
}

//--------------------------------------------------------------------------
void send_bin(const unsigned char * bin , int bin_len, const char * type, const char * file) {
  int my_len, my_ptr, my_send;

  strcpy(HTTP_Header, "HTTP/1.1 200 OKrn");
  strcat(HTTP_Header, "Content-Length: ");
  strcati(HTTP_Header, bin_len);
  strcat(HTTP_Header, "rn");
  strcat(HTTP_Header, "Content-Type: ");
  strcat(HTTP_Header, type);
  strcat(HTTP_Header, "rn");
  strcat(HTTP_Header, "Content-Location: ");
  strcat(HTTP_Header, file);
  strcat(HTTP_Header, "rn");
  strcat(HTTP_Header, "Connection: closern");
  strcat(HTTP_Header, "rn");

#ifdef BGTDEBUG
  exhibit("Header : ", HTTP_Header);
  exhibit("Laenge Header : ", (unsigned long) strlen(HTTP_Header));
  exhibit("Laenge Bin    : ", bin_len);
#endif

  client.print(HTTP_Header);
  delay(20);

  //---------------------------------------------------------------------
  // in Portionen senden

  my_len = bin_len;
  my_ptr = 0;
  my_send = 0;

  while ((my_len - my_send) > 0) {
    my_send = my_ptr + MAX_PACKAGE_SIZE;
    if (my_send > my_len) {
      client.write(&bin[my_ptr], my_len - my_ptr);
      delay(20);
      my_send = my_len;
    } else {
      client.write(&bin[my_ptr], MAX_PACKAGE_SIZE);
      delay(20);
      my_ptr = my_send;
    }
  }
  client.stop();
}
//--------------------------------------------------------------------------
void send_not_found() {
#ifdef BGTDEBUG
  Serial.println("Sende Not Found");
#endif
  client.print("HTTP/1.1 404 Not Foundrnrn");
  delay(20);
  client.stop();
}

//--------------------------------------------------------------------------
void send_HTML() {
  char my_char;
int  my_len = strlen(HTML_String);
int  my_ptr = 0;
int  my_send = 0;

  //--------------------------------------------------------------------------
  // in Portionen senden
  while ((my_len - my_send) > 0) {
    my_send = my_ptr + MAX_PACKAGE_SIZE;
    if (my_send > my_len) {
      client.print(&HTML_String[my_ptr]);
      delay(20);
#ifdef BGTDEBUG
      Serial.println(&HTML_String[my_ptr]);
#endif
      my_send = my_len;
    } else {
      my_char = HTML_String[my_send];
      // Auf Anfang eines Tags positionieren
      while ( my_char != '<') my_char = HTML_String[--my_send];
      HTML_String[my_send] = 0;
      client.print(&HTML_String[my_ptr]);
      delay(20);
#ifdef BGTDEBUG
      Serial.println(&HTML_String[my_ptr]);
#endif
      HTML_String[my_send] =  my_char;
      my_ptr = my_send;
    }
  }
  client.stop();
}

//----------------------------------------------------------------------------------------------
void set_colgroup(int w1, int w2, int w3, int w4, int w5) {
  strcat( HTML_String, "<colgroup>");
  set_colgroup1(w1);
  set_colgroup1(w2);
  set_colgroup1(w3);
  set_colgroup1(w4);
  set_colgroup1(w5);
  strcat( HTML_String, "</colgroup>");
}

//------------------------------------------------------------------------------------------
void set_colgroup1(int ww) {
  if (ww == 0) return;
  strcat( HTML_String, "<col width="");
  strcati( HTML_String, ww);
  strcat( HTML_String, "">");
}

//---------------------------------------------------------------------
void strcati(char* tx, int i) {
  char tmp[8];

  itoa(i, tmp, 10);
  strcat (tx, tmp);
}

//---------------------------------------------------------------------
void strcati2(char* tx, int i) {
  char tmp[8];

  itoa(i, tmp, 10);
  if (strlen(tmp) < 2) strcat (tx, "0");
  strcat (tx, tmp);
}

//---------------------------------------------------------------------
int Pick_Parameter_Zahl(const char * par, char * str) {
  int myIdx = Find_End(par, str);
  
  if (myIdx >= 0) return  Pick_Dec(str, myIdx);
  else return -1;
}
//---------------------------------------------------------------------
int Find_End(const char * such, const char * str) {
  int tmp = Find_Start(such, str);
  if (tmp >= 0)tmp += strlen(such);
  return tmp;
}

//---------------------------------------------------------------------
int Find_Start(const char * such, const char * str) {
  int tmp = -1;
  int ww = strlen(str) - strlen(such);
  int ll = strlen(such);

  for (int i = 0; i <= ww && tmp == -1; i++) {
    if (strncmp(such, &str[i], ll) == 0) tmp = i;
  }
  return tmp;
}
//---------------------------------------------------------------------
int Pick_Dec(const char * tx, int idx ) {
  int tmp = 0;
  
  for (int p = idx; p < idx + 5 && (tx[p] >= '0' && tx[p] <= '9') ; p++) {
    tmp = 10 * tmp + tx[p] - '0';
  }
  return tmp;
}
//----------------------------------------------------------------------------
int Pick_N_Zahl(const char * tx, char separator, byte n) {

  int ll = strlen(tx);
  int tmp = -1;
  byte anz = 1;
  byte i = 0;
  while (i < ll && anz < n) {
    if (tx[i] == separator)anz++;
    i++;
  }
  if (i < ll) return Pick_Dec(tx, i);
  else return -1;
}

//---------------------------------------------------------------------
int Pick_Hex(const char * tx, int idx ) {
  int tmp = 0;

  for (int p = idx; p < idx + 5 && ( (tx[p] >= '0' && tx[p] <= '9') || (tx[p] >= 'A' && tx[p] <= 'F')) ; p++) {
    if (tx[p] <= '9')tmp = 16 * tmp + tx[p] - '0';
    else tmp = 16 * tmp + tx[p] - 55;
  }

  return tmp;
}

//---------------------------------------------------------------------
void Pick_Text(char * tx_ziel, char  * tx_quelle, int max_ziel) {

  int p_ziel = 0;
  int p_quelle = 0;
  int len_quelle = strlen(tx_quelle);

  while (p_ziel < max_ziel && p_quelle < len_quelle && tx_quelle[p_quelle] && tx_quelle[p_quelle] != ' ' && tx_quelle[p_quelle] !=  '&') {
    if (tx_quelle[p_quelle] == '%') {
      tx_ziel[p_ziel] = (HexChar_to_NumChar( tx_quelle[p_quelle + 1]) << 4) + HexChar_to_NumChar(tx_quelle[p_quelle + 2]);
      p_quelle += 2;
    } else if (tx_quelle[p_quelle] == '+') {
      tx_ziel[p_ziel] = ' ';
    }
    else {
      tx_ziel[p_ziel] = tx_quelle[p_quelle];
    }
    p_ziel++;
    p_quelle++;
  }

  tx_ziel[p_ziel] = 0;
}
//---------------------------------------------------------------------
char HexChar_to_NumChar( char c) {
  if (c >= '0' && c <= '9') return c - '0';
  if (c >= 'A' && c <= 'F') return c - 55;
  return 0;
}

#ifdef BGTDEBUG
//---------------------------------------------------------------------
void exhibit(const char * tx, int v) {
  Serial.print(tx);
  Serial.println(v);
}
//---------------------------------------------------------------------
void exhibit(const char * tx, unsigned int v) {
  Serial.print(tx);
  Serial.println(v);
}
//---------------------------------------------------------------------
void exhibit(const char * tx, unsigned long v) {
  Serial.print(tx);
  Serial.println(v);
}
//---------------------------------------------------------------------
void exhibit(const char * tx, const char * v) {
  Serial.print(tx);
  Serial.println(v);
}
#endif
Der Sketch zum download:

Erläuterungen zum Programmrahmen

Im Setup wird 10 Sekunden gewarten, damit man in Ruhe den Serial-Monitor starten kann und alle Meldungen mitbekommt

Dann wird zunächst versucht sich beim lokalen WLAN anzumelden. Wenn dies fehlschlägt wird ein eigener WEB-Server gestartet.

In der Loop wird die Routine WIFI-Traffic aufgerufen, um die über WALN empfangenen Benutzereingaben zu verarbeiten und die aktuelle HTML-Seite aufzubauen und zu senden.

Die Web-Browser rufen nicht nur die HTML-Seite und die darin enthaltenen Verweise auf Grafiken usw. ab. Sie fragen z.B. auch nach einem Favoriten-Ikon. Wenn man diese Requests ignoriert, weden sie alle paar Sekunden wiederholt. Daher wird in meinem Programm auf alles was nicht bekannt ist und bedient werden kann, mit „Not found“ geantwortet. Das merkt sich der Browser und weitere Anfragen unterbleiben.

Nach einem Abruf der HTML-Seite ohne jeden Parameter (Browsereingabe z.B. http://192.168.178.41/) kommt dieser Request an.:

GET / HTTP/1.1

Zwischen dem Slash und dem HTTP stehen ggf. Eingabe-Parameter oder der Name einer Datei die, abgerufen wird. In meinem Beispiel wird eine Bilddatei verlinkt :

HTML-Beispiel :

<img src=„bild.gif“ alt=„mein Bild“>

Die Bilddatei wird nach dem Senden der HTML-Seite automatisch angefordert :

GET /bild1.gif HTTP/1.1

Die Bilddatei wurde zu einem Char-Array umgesetzt und in einer eigenen Code-Datei Bild1.c gespeichert.:

#include "arduino.h"
#define BILD1_LEN  1515 
const unsigned char Bild1[] = {
 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x2B, 0x00, 0x28, 0x00, 0x70, 0x00, 0x00, 0x21, 0xF9, 0x04, 
 ....
 0x5B, 0xD1, 0xFD, 0xA2, 0xC8, 0x34, 0x5F, 0x01, 0x01, 0x00, 0x3B};

Diese binären Daten werden mit write übertragen. Write ist erforderlich, weil print beim ersten Auftreten eines 0x00 die Übertragung beenden würde.

Das Senden der HTML- und Binär-Daten geschieht in Portionen. Max. 2048 Bytes dürfen auf einmal geschickt werden, danach muß eine Pause von 20 mSec eingelegt werden.

Die Buttons haben die Attribute Name und Value. Beides wird beim Anklicken gesendet. So kann identifiziert werden, welcher Button angeklickt wurde.

HTML-Beispiel :

<button style=“width:200px“ name=„ACTION“ value=„1“>OK</button>

Wenn dieser Knopf angeklickt wird, kommt folgender Request :

GET /?ACTION=1 HTTP/1.1

Ich gebe die HTML-Seite auch auf dem Serialmonitor aus. Von dort kann sie mit Cut and Paste abgegriffen und z.B. zur Syntax-Prüfung in ein entsprechendes Programm übertragen werden.

Ein gute Seite zur Syntax-Prüfung im Netz ist z.B. :

http://www.dirtymarkup.com/

Das sieht so aus :

HTML_Kochbuch_Bild2

Wenn man seine Seite eingefügt hat, kann man mit Enter die Eingabe in mehrere Zeilen aufteilen. Das sieht dann so aus.

HTML_Kochbuch_Bild3

Hinweise auf Syntax-Fehler stehen ggf. am Anfang einer Zeile. Man kann den Fehler durch Aufteilen der Zeilen (Enter) einkreisen.

Es ist dringend angeraten, seine HTMLSeite zu überprüfen, um sicher zu gehen, dass sich kein Fehler eingeschlichen hat. Die verschiedenen WEB-Browser behandeln Fehler ganz unterschiedlich. Einige korrigieren bestimmte Fehler erfolgreich und alles sieht gut aus, einige nicht, und zeigen u.U. einen Scherbenhaufen (Browser in Voodoo-Mode) an.

Aufbau der HTML-Seite

Ich baue die HTML-Seite als Char-Array auf. Das benötigt weniger Speicher und läuft daher stabiler als der Aufbau mit Hilfe einer String-Variablen. Das Char-Array muß natürlich groß genug definiert sein. Es wird mit strcat zusammengesetzt. Eine eigenen Routine strcati dient zum Anfügen von Integerzahlen.

Die Benutzereingaben müssen in

<form> … </form>

eingebettet sein. Innerhalb dieses Blockes muß ein Button sein, der die Übertragung der Benutzereingaben dieses Blocks auslöst.

Auf einer HTML-Seite dürfen mehrere solcher Blöcke vorhanden sein, um z.B. die Eingaben zu strukurieren und unnötige Datenübertragungen zu vermeiden.

Wie oben bereits erwähnt, werden die Atrribute Name und Value beim Anklicken eines Button gesendet. Im Request kommen dann Name=Value an. Ich benutze bei allen Buttons immer den gleichen Namen „ACTION“ mit verschiedenen numerischen Werten in Value. Die Auswertung ist dann einfach. Mit der Routine Pick_Parameter_Zahl wird der Zahlenwert hinter „ACTION=“ ausgelesen. Der eingelesen Wert wird dann interpretiert. Das ist einfacher als verschiedenen Namen zu verwenden, die dann einzeln ausgelesen werden müssten,

Die verschieden Werte für ACTION habe ich als Kompilerkonstanten definiert z.B. ACTION_OK. Diese Konstanten verwende ich beim Aufbau der HTML-Seite und beim Interpretieren der Benutzereingaben. Das schafft Klarheit und Übersichtlichkeit und dokumentiert sich automatisch.

Dieses Anwendungsbeispiel

Hier werden nur die Anzahl Klicks auf „Ok“ und „Nicht OK“ gezählt. Das is denkbar einfach:

//  Variablen

int ok_count = 0;
int notok_count = 0;
int Aufruf_Zaehler = 0;

#define ACTION_OK 1
#define ACTION_NOTOK 2
int action;

// HTML Ausgabe

strcat( HTML_String, "<button style= "width:120px" name="ACTION" value="");
strcati(HTML_String, ACTION_OK);
strcat( HTML_String, "">OK</button>");

strcat( HTML_String, "   ");

strcat( HTML_String, "<button style= "width:120px" name="ACTION" value="");
strcati(HTML_String, ACTION_NOTOK);
strcat( HTML_String, "">nicht OK</button>");

strcat( HTML_String, "</form>");

strcat( HTML_String, "<BR>");

strcat( HTML_String, "<FONT SIZE=-1>");
strcat( HTML_String, "Aufrufzähler : ");
strcati(HTML_String, Aufruf_Zaehler);
strcat( HTML_String, "  Anzahl OK : ");
strcati(HTML_String, ok_count);
strcat( HTML_String, "  Anzahl NOK : ");
strcati(HTML_String, notok_count);

// Auswertung

action = Pick_Parameter_Zahl("ACTION=", HTML_String);
if ( action == ACTION_OK) ok_count++;
if ( action == ACTION_NOTOK) notok_count++;

2. Anwendungsbeispiel

Im folgenden Anwendungsbeispiel möchte ich zeigen, wie die wichtigsten Steuerelemente definiert und ausgewertet werden.

Textfelder

HTML-Beispiel :

<input type=„text“ style=“width:200px“ name=„VORNAME“ maxlength=„20“ value=„Bärbel“>

Das sieht so aus :
HTML_Kochbuch_Bild4

Der Request sieht so aus :

GET /?VORNAME=B%E4rbel&ACTION=4&NACHNAME=von+der+Waterkant HTTP/1.1

Die Felder sind durch ein & getrennt. Umlaute werden hexadezimal zurückgegeben. Dem Headezimalwert ist ein % vorangestellt. In diesem Beispiel wird der Buchstabe ä als %E4 codiert. Leerzeichen werden als + dargestellt. Die Routine Pick_Text bereitet die empfangenen Textfelder auf, dh. Hexcodes und + -Zeichen werden umgesetzt.

Auswertung :

  if ( action == ACTION_SET_NAME) {
    myIndex = Find_End("VORNAME=", HTML_String);
    if (myIndex >= 0) {
      Pick_Text(Vorname, &HTML_String[myIndex], 20);
    }
    myIndex = Find_End("NACHNAME=", HTML_String);
    if (myIndex >= 0) {
      Pick_Text(Nachname, &HTML_String[myIndex], 20);
    }
  }

Uhrzeit und Datum

HTML-Beispiel :

<input type=“time“ style=“width:100px“ name=“UHRZEIT“ value=„16:47:00“>
<input type=“date“ style=“width:100px“ name=“DATUM“ value=„2016-02-09“>

Das sieht so aus :

HTML_Kochbuch_Bild5

Der Request sieht so aus :

GET /?UHRZEIT=16%3A47%3A00&ACTION=3&DATUM=2016-02-09 HTTP/1.1

In der Uhrzeit sind die Trennzeichen : hexadezimal-codiert,. Das Datum wird in der angelsächsischen Reihenfolge Jahr, Monat Tag angegeben.

Auswertung :

if ( action == ACTION_SET_DATE_TIME) {
  myIndex = Find_End("UHRZEIT=", HTML_String);
  if (myIndex >= 0) {
    Pick_Text(tmp_string, &HTML_String[myIndex], 8);
    Uhrzeit_HH = Pick_N_Zahl(tmp_string, ':', 1);
    Uhrzeit_MM = Pick_N_Zahl(tmp_string, ':', 2);
    Uhrzeit_SS = Pick_N_Zahl(tmp_string, ':', 3);
  }
  myIndex = Find_End("DATUM=", HTML_String);
  if (myIndex >= 0) {
    Pick_Text(tmp_string, &HTML_String[myIndex], 10);
    Datum_JJJJ = Pick_N_Zahl(tmp_string, '-', 1);
    Datum_MM = Pick_N_Zahl(tmp_string, '-', 2);
    Datum_TT = Pick_N_Zahl(tmp_string, '-', 3);
  }
}


Auswahlfelder

HTML-Beispiel für Checkbox :

<input type=„checkbox“ name=„WOCHENTAG0“ id=„WT0“ value=„1“ checked>
<label for=„WT0“>Mo</label>

Man muß die Checkbox definieren und explizit ein Beschriftungslabel zuordnen. Das Attribut checked zeigt an, das diese Checkbox angekreuzt angezeigt werden soll. Im Request erhält man nur für angekreixte Checkboxen Name und Value. Über nicht angekreuzte Checkboxen wird geschwiegen. In meinem unternstehenden Beispiel habe ich alle 7 Wochentage aufgeführt und baue die angekreutenTage als Bit in ein 1-Byte-Datenfeld ein..

Auswertung :

Wochentage = 0;
for (int i = 0; i < 7; i++) {
  strcpy( tmp_string, "WOCHENTAG");
  strcati( tmp_string, i);
  strcat( tmp_string, "=");
  if (Pick_Parameter_Zahl(tmp_string, HTML_String) == 1)
    Wochentage |= 1 << i;
}

HTML-Beispiel für Radiobutton :

<input type=“radio“ name=„JAHRESZEIT“ id=“JZ0″ value=„0“ checked>
<label for=“JZ0″>Frühling</label>
<input type=“radio“ name=„JAHRESZEIT“ id=“JZ1″ value=„1“>
<label for=“JZ1″>Sommer</label>

Man muß auch hier zunächst den Radiobutton definieren und dann explizit ein Beschriftungslabel zuordnen.
Die Gruppierung der Gruppe geschieht über das Attribut Name, das bei allen Gruppenmitgliedern den gleichen Wert hat. Genau ein Radiobutton der Gruppe darf ein checked enthalten

Auswertung :

Jahreszeit = Pick_Parameter_Zahl("JAHRESZEIT=", HTML_String);

HTML-Beispiel für Combobox :

<select name=„WETTER“ style=“width:160px“>
<option selected value=„0“>Regen</option>
<option value=„1“>Wolken</option>
</select>

Zwischen select und endselect werden die Auswahloptionen aufgeführt. Die vorgewählte Option erhält das Attribut selected.

Auswertung :

Wetter = Pick_Parameter_Zahl("WETTER=", HTML_String);

Das sieht so aus :

HTML_Kochbuch_Bild6

Der Request für alles obige sieht so aus :

GET /?WOCHENTAG1=1&WOCHENTAG2=1&WOCHENTAG3=1&ACTION=5&JAHRESZEIT=2&WETTER=2 HTTP/1.1

Slider

HTML-Beispiel :

<input type=„range“ name=„VOLUME“ min=„0“ max=„30“ value=„15“>

Das sieht so aus :

HTML_Kochbuch_Bild7
Der Request sieht so aus :

GET /?VOLUME=15&ACTION=6 HTTP/1.1

Auswertung :

if ( action == ACTION_LIES_VOLUME) {
  Volume = Pick_Parameter_Zahl("VOLUME=", HTML_String);
}

Listing (ohne die oben bereits wiedergegebenen Unterroutinen)

Coding
//---------------------------------------------------------------------
//ESP866 HTML Demo 02
//---------------------------------------------------------------------
// Author  : Hubert Baumgarten
//---------------------------------------------------------------------
#include <ESP8266WiFi.h>
#ifdef __AVR__
#include <avr/power.h>
#endif

#define BGTDEBUG 1

//---------------------------------------------------------------------
// WiFi

byte my_WiFi_Mode = 0;  // WIFI_STA = 1 = Workstation  WIFI_AP = 2  = Accesspoint

const char * ssid_sta     = "<Your SSID>";
const char * password_sta = "<Your Password>";

const char * ssid_ap      = "ESP_HTML_02";
const char * password_ap  = "";    // alternativ :  = "12345678";

WiFiServer server(80);
WiFiClient client;

#define MAX_PACKAGE_SIZE 2048
char HTML_String[5000];
char HTTP_Header[150];

//---------------------------------------------------------------------
// Allgemeine Variablen

int Aufruf_Zaehler = 0;

#define ACTION_OK 1
#define ACTION_NOTOK 2
#define ACTION_SET_DATE_TIME 3
#define ACTION_SET_NAME 4
#define ACTION_LIES_AUSWAHL 5
#define ACTION_LIES_VOLUME 6

int action;

// Vor- Nachname
char Vorname[20] = "Bärbel";
char Nachname[20] = "von der Waterkant";

// Uhrzeit Datum
byte Uhrzeit_HH = 16;
byte Uhrzeit_MM = 47;
byte Uhrzeit_SS = 0;
byte Datum_TT = 9;
byte Datum_MM = 2;
int Datum_JJJJ = 2016;

// checkboxen
char Wochentage_tab[7][3] = {"Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"};
byte Wochentage = 0;

// Radiobutton
char Jahreszeiten_tab[4][15] = {"Frühling", "Sommer", "Herbst", "Winter"};
byte Jahreszeit = 0;

// Combobox
char Wetter_tab[4][10] = {"Sonne", "Wolken", "Regen", "Schnee"};
byte Wetter;


// Slider
byte Volume = 15;

char tmp_string[20];
//---------------------------------------------------------------------
void setup() {
#ifdef BGTDEBUG
  Serial.begin(115200);

  for (int i = 10; i > 0; i--) {
    Serial.print("Warte ");
    Serial.print(i);
    Serial.println(" sec");
    delay(1000);
  }
  Serial.println("ESP_HTML_02");
#endif

  //---------------------------------------------------------------------
  // WiFi starten

  WiFi_Start_STA();
  if (my_WiFi_Mode == 0) WiFi_Start_AP();

}

//---------------------------------------------------------------------
void loop() {

  WiFI_Traffic();
  delay(10);
}

//---------------------------------------------------------------------
void WiFI_Traffic() {

  char my_char;
  int htmlPtr = 0;
  int myIdx;
  int myIndex;
  unsigned long my_timeout;

  // Check if a client has connected
  client = server.available();
  if (!client)  {
    return;
  }

  my_timeout = millis() + 250L;
  while (!client.available() && (millis() < my_timeout) ) delay(10);
  delay(10);
  if (millis() > my_timeout)  {
    return;
  }
  //---------------------------------------------------------------------
  htmlPtr = 0;
  my_char = 0;
  while (client.available() && my_char != 'r') {
    my_char = client.read();
    HTML_String[htmlPtr++] = my_char;
  }
  client.flush();
  HTML_String[htmlPtr] = 0;
#ifdef BGTDEBUG
  exhibit ("Request : ", HTML_String);
#endif

  Aufruf_Zaehler++;

  if (Find_Start ("/?", HTML_String) < 0 && Find_Start ("GET / HTTP", HTML_String) < 0 ) {
    send_not_found();
    return;
  }

  //---------------------------------------------------------------------
  // Benutzereingaben einlesen und verarbeiten
  //---------------------------------------------------------------------
  action = Pick_Parameter_Zahl("ACTION=", HTML_String);

  // Vor und Nachname
  if ( action == ACTION_SET_NAME) {

    myIndex = Find_End("VORNAME=", HTML_String);
    if (myIndex >= 0) {
      Pick_Text(Vorname, &HTML_String[myIndex], 20);
#ifdef BGTDEBUG
      exhibit ("Vorname  : ", Vorname);
#endif
    }

    myIndex = Find_End("NACHNAME=", HTML_String);
    if (myIndex >= 0) {
      Pick_Text(Nachname, &HTML_String[myIndex], 20);
#ifdef BGTDEBUG
      exhibit ("Nachname  : ", Nachname);
#endif
    }
  }
  // Uhrzeit und Datum
  if ( action == ACTION_SET_DATE_TIME) {
    // UHRZEIT=12%3A35%3A25
    myIndex = Find_End("UHRZEIT=", HTML_String);
    if (myIndex >= 0) {
      Pick_Text(tmp_string, &HTML_String[myIndex], 8);
      Uhrzeit_HH = Pick_N_Zahl(tmp_string, ':', 1);
      Uhrzeit_MM = Pick_N_Zahl(tmp_string, ':', 2);
      Uhrzeit_SS = Pick_N_Zahl(tmp_string, ':', 3);
#ifdef BGTDEBUG
      Serial.print("Neue Uhrzeit ");
      Serial.print(Uhrzeit_HH);
      Serial.print(":");
      Serial.print(Uhrzeit_MM);
      Serial.print(":");
      Serial.println(Uhrzeit_SS);
#endif
    }
    // DATUM=2015-12-31
    myIndex = Find_End("DATUM=", HTML_String);
    if (myIndex >= 0) {
      Pick_Text(tmp_string, &HTML_String[myIndex], 10);
      Datum_JJJJ = Pick_N_Zahl(tmp_string, '-', 1);
      Datum_MM = Pick_N_Zahl(tmp_string, '-', 2);
      Datum_TT = Pick_N_Zahl(tmp_string, '-', 3);
#ifdef BGTDEBUG
      Serial.print("Neues Datum ");
      Serial.print(Datum_TT);
      Serial.print(".");
      Serial.print(Datum_MM);
      Serial.print(".");
      Serial.println(Datum_JJJJ);
#endif
    }
  }


  if ( action == ACTION_LIES_AUSWAHL) {
    Wochentage = 0;
    for (int i = 0; i < 7; i++) {
      strcpy( tmp_string, "WOCHENTAG");
      strcati( tmp_string, i);
      strcat( tmp_string, "=");
      if (Pick_Parameter_Zahl(tmp_string, HTML_String) == 1)Wochentage |= 1 << i;
    }
    Jahreszeit = Pick_Parameter_Zahl("JAHRESZEIT=", HTML_String);
    Wetter = Pick_Parameter_Zahl("WETTER=", HTML_String);

  }

  if ( action == ACTION_LIES_VOLUME) {
    Volume = Pick_Parameter_Zahl("VOLUME=", HTML_String);
  }


  //---------------------------------------------------------------------
  //Antwortseite aufbauen

  make_HTML01();

  //---------------------------------------------------------------------
  // Header aufbauen
  strcpy(HTTP_Header , "HTTP/1.1 200 OKrn");
  strcat(HTTP_Header, "Content-Length: ");
  strcati(HTTP_Header, strlen(HTML_String));
  strcat(HTTP_Header, "rn");
  strcat(HTTP_Header, "Content-Type: text/htmlrn");
  strcat(HTTP_Header, "Connection: closern");
  strcat(HTTP_Header, "rn");

#ifdef BGTDEBUG
  exhibit("Header : ", HTTP_Header);
  exhibit("Laenge Header : ", strlen(HTTP_Header));
  exhibit("Laenge HTML   : ", strlen(HTML_String));
#endif

  client.print(HTTP_Header);
  delay(20);

  send_HTML();

}

//---------------------------------------------------------------------
// HTML Seite 01 aufbauen
//---------------------------------------------------------------------
void make_HTML01() {

  strcpy( HTML_String, "<!DOCTYPE html>");
  strcat( HTML_String, "<html>");
  strcat( HTML_String, "<head>");
  strcat( HTML_String, "<title>HTML Demo</title>");
  strcat( HTML_String, "</head>");
  strcat( HTML_String, "<body bgcolor="#adcede">");
  strcat( HTML_String, "<font color="#000000" face="VERDANA,ARIAL,HELVETICA">");
  strcat( HTML_String, "<h1>HTML Demo</h1>");


  //-----------------------------------------------------------------------------------------
  // Textfelder Vor- und Nachname
  strcat( HTML_String, "<h2>Textfelder</h2>");
  strcat( HTML_String, "<form>");
  strcat( HTML_String, "<table>");
  set_colgroup(150, 270, 150, 0, 0);

  strcat( HTML_String, "<tr>");
  strcat( HTML_String, "<td><b>Vorname</b></td>");
  strcat( HTML_String, "<td>");
  strcat( HTML_String, "<input type="text" style= "width:200px" name="VORNAME" maxlength="20" Value ="");
  strcat( HTML_String, Vorname);
  strcat( HTML_String, ""></td>");
  strcat( HTML_String, "<td><button style= "width:100px" name="ACTION" value="");
  strcati(HTML_String, ACTION_SET_NAME);
  strcat( HTML_String, "">Übernehmen</button></td>");
  strcat( HTML_String, "</tr>");

  strcat( HTML_String, "<tr>");
  strcat( HTML_String, "<td><b>Nachname</b></td>");
  strcat( HTML_String, "<td>");
  strcat( HTML_String, "<input type="text" style= "width:200px" name="NACHNAME" maxlength="20" Value ="");
  strcat( HTML_String, Nachname);
  strcat( HTML_String, ""></td>");
  strcat( HTML_String, "</tr>");

  strcat( HTML_String, "</table>");
  strcat( HTML_String, "</form>");
  strcat( HTML_String, "<br>");



  //-----------------------------------------------------------------------------------------
  // Uhrzeit + Datum
  strcat( HTML_String, "<h2>Uhrzeit und Datum</h2>");
  strcat( HTML_String, "<form>");
  strcat( HTML_String, "<table>");
  set_colgroup(150, 270, 150, 0, 0);

  strcat( HTML_String, "<tr>");
  strcat( HTML_String, "<td><b>Uhrzeit</b></td>");
  strcat( HTML_String, "<td><input type="time"   style= "width:100px" name="UHRZEIT" value="");
  strcati2( HTML_String, Uhrzeit_HH);
  strcat( HTML_String, ":");
  strcati2( HTML_String, Uhrzeit_MM);
  strcat( HTML_String, ":");
  strcati2( HTML_String, Uhrzeit_SS);

  strcat( HTML_String, ""></td>");
  strcat( HTML_String, "<td><button style= "width:100px" name="ACTION" value="");
  strcati(HTML_String, ACTION_SET_DATE_TIME);
  strcat( HTML_String, "">Übernehmen</button></td>");
  strcat( HTML_String, "</tr>");

  strcat( HTML_String, "<tr>");
  strcat( HTML_String, "<td><b>Datum</b></td>");
  strcat( HTML_String, "<td><input type="date"  style= "width:100px" name="DATUM" value="");
  strcati( HTML_String, Datum_JJJJ);
  strcat( HTML_String, "-");
  strcati2( HTML_String, Datum_MM);
  strcat( HTML_String, "-");
  strcati2( HTML_String, Datum_TT);
  strcat( HTML_String, ""></td></tr>");

  strcat( HTML_String, "</table>");
  strcat( HTML_String, "</form>");
  strcat( HTML_String, "<br>");

  //-----------------------------------------------------------------------------------------
  // Checkboxen
  strcat( HTML_String, "<h2>Checkbox, Radiobutton und Combobox</h2>");
  strcat( HTML_String, "<form>");
  strcat( HTML_String, "<table>");
  set_colgroup(150, 270, 150, 0, 0);

  strcat( HTML_String, "<tr>");
  strcat( HTML_String, "<td><b>Wochentage</b></td>");

  strcat( HTML_String, "<td>");
  for (int i = 0; i < 7; i++) {
    if (i == 5)strcat( HTML_String, "<br>");
    strcat( HTML_String, "<input type="checkbox" name="WOCHENTAG");
    strcati( HTML_String, i);
    strcat( HTML_String, "" id = "WT");
    strcati( HTML_String, i);
    strcat( HTML_String, "" value = "1" ");
    if (Wochentage & 1 << i) strcat( HTML_String, "checked ");
    strcat( HTML_String, "> ");
    strcat( HTML_String, "<label for ="WT");
    strcati( HTML_String, i);
    strcat( HTML_String, "">");
    strcat( HTML_String, Wochentage_tab[i]);
    strcat( HTML_String, "</label>");
  }
  strcat( HTML_String, "</td>");

  strcat( HTML_String, "<td><button style= "width:100px" name="ACTION" value="");
  strcati(HTML_String, ACTION_LIES_AUSWAHL);
  strcat( HTML_String, "">Übernehmen</button></td>");
  strcat( HTML_String, "</tr>");

  //-----------------------------------------------------------------------------------------
  // Radiobuttons

  for (int i = 0; i < 4; i++) {
    strcat( HTML_String, "<tr>");
    if (i == 0)  strcat( HTML_String, "<td><b>Jahreszeit</b></td>");
    else strcat( HTML_String, "<td> </td>");
    strcat( HTML_String, "<td><input type = "radio" name="JAHRESZEIT" id="JZ");
    strcati( HTML_String, i);
    strcat( HTML_String, "" value="");
    strcati( HTML_String, i);
    strcat( HTML_String, """);
    if (Jahreszeit == i)strcat( HTML_String, " CHECKED");
    strcat( HTML_String, "><label for="JZ");
    strcati( HTML_String, i);
    strcat( HTML_String, "">");
    strcat( HTML_String, Jahreszeiten_tab[i]);
    strcat( HTML_String, "</label></td></tr>");
  }

  //-----------------------------------------------------------------------------------------
  // Combobox
  strcat( HTML_String, "<tr><td><b>Wetter</b></td>");

  strcat( HTML_String, "<td>");
  strcat( HTML_String, "<select name = "WETTER" style= "width:160px">");
  for (int i = 0; i < 4; i++) {
    strcat( HTML_String, "<option ");
    if (Wetter == i)strcat( HTML_String, "selected ");
    strcat( HTML_String, "value = "");
    strcati( HTML_String, i);
    strcat(HTML_String, "">");
    strcat(HTML_String, Wetter_tab[i]);
    strcat(HTML_String, "</option>");
  }
  strcat( HTML_String, "</select>");
  strcat( HTML_String, "</td></tr>");

  strcat( HTML_String, "</table>");
  strcat( HTML_String, "</form>");
  strcat( HTML_String, "<br>");

  //-----------------------------------------------------------------------------------------
  // Slider
  strcat( HTML_String, "<h2>Slider</h2>");
  strcat( HTML_String, "<form>");
  strcat( HTML_String, "<table>");
  set_colgroup(150, 270, 150, 0, 0);

  strcat( HTML_String, "<tr><td><b>Lautstärke</b></td>");

  strcat( HTML_String, "<td>");
  strcat( HTML_String, "<input type="range" name="VOLUME" min="0" max="30" value = "");
  strcati(HTML_String, Volume);
  strcat( HTML_String, "">");
  strcat( HTML_String, "</td>");

  strcat( HTML_String, "<td><button style= "width:100px" name="ACTION" value="");
  strcati(HTML_String, ACTION_LIES_VOLUME);
  strcat( HTML_String, "">Übernehmen</button></td>");
  strcat( HTML_String, "</tr>");

  strcat( HTML_String, "</table>");
  strcat( HTML_String, "</form>");
  strcat( HTML_String, "<BR>");

  strcat( HTML_String, "<FONT SIZE=-1>");
  strcat( HTML_String, "Aufrufzähler : ");
  strcati(HTML_String, Aufruf_Zaehler);
  strcat( HTML_String, "</font>");
  strcat( HTML_String, "</font>");
  strcat( HTML_String, "</body>");
  strcat( HTML_String, "</html>");
}
Der Sketch zum download:

Weitere Details zu HTML findet man hier :

www.w3schools.com Englische Seite. Kurz und bündig.

wiki.selfhtml.org Deutsche Seite. Ausführliche Beispiele.

Das war’s. Viel Spaß beim Ausprobieren.

Hubert Baumgarten

27 Gedanken zu „HTML Kochbuch mit ESP8266 und Arduino IDE

  1. Hallo,
    ich habe eine Frage:

    ich habe mit einem NodeMCU-Board und LuaLoader ein bißchen herumgespielt. Um das NodeMCU-Board mit Lua-Scripten laufen zu lassen ist die Lua-Firmware auf dem Board gespeichert.
    In diesem Artikel hier werden C-Programme in der Arduino-IDE geschrieben. Wenn man diese C-Programme auf das Board lädt wird dann die Lua-Firmware überschrieben?
    Könnte man das so einrichten das beides parallel laufen könnte?
    viele Grüße

    Stefan

  2. Hallo,
    ich wollte das Programm ausprobieren, habe mir die Library ESP8266wifi-Master von Github heruntergeladen, bekomme aber immer eine Fehlermeldung. Erst dass die passende Library nicht vorhanden ist, und wenn ich dann selbst die Library ESP8266wifi.h einfüge bekomme ich die Fehlermeldung ‚WifiServer‘ does not type a name. Kannst du mir da bitte weiterhelfen,
    danke Gerhard

  3. Hallo Hubert,
    das ist ein interessanter Ansatz.
    Wäre es aber nicht übersichtlicher, das in eine objektorientierte Struktur zu packen?
    Also eine Objektstruktur von Objekten, welche jeweils einem HTML Syntax-Konstrukt entsprechen und neben Start- und Endetag eine Objektliste enthalten, die untergeordnete Objekte aufnehmen kann.
    Eine insert-Methode würde diese dann in eine Objektliste aufnehmen eine print-Methode dann am Ende iterativ durchlaufen und den gesamten Code erzeugen.
    Was hältst du davon? Gibt es so was schon irgendwo?
    Lohnt es sich so was zus schreiben?
    Ich bin mit html leider noch wenig vertraut – wenn man aber mit dem ESP was machen will muss man da halt irgendwie durch.

    Gruß
    Rainer

  4. Hallo Hubert,

    ich habe schon einiges probiert, komme aber nicht weiter.
    Ich möchte gerne z.B. das Datum oder die Uhrzeit automatisch vom ESP aktualisieren, ohne immer wieder eine Taste im Browser drücken zu müssen.
    Habe schon was von einem refreshFrame gehört, habe aber keine Ahnung wie das gehen kann.
    Hast Du das schon mal gemacht?
    Falls ja, kannst Du es kurz beschreiben?

    Gruß Matthias

  5. Hallo Fans,
    Ich bin dabei, den sketch von Hubert Baumgarten ESP8266_HTM_01 zum Laufen zu bringen und bekomme die folgende Fehlerausschrift:
    Arduino: 1.8.4 (Windows 10), Board: „Arduino/Genuino Uno“
    In file included from C:\Users\Klaus\Documents\Arduino\libraries\ESP8266WiFi\src/ESP8266WiFiSTA.h:28:0,
    from C:\Users\Klaus\Documents\Arduino\libraries\ESP8266WiFi\src/ESP8266WiFi.h:34,
    from D:\Arduino\ESP_HTML_01\ESP_HTML_01.ino:6:
    C:\Users\Klaus\Documents\Arduino\libraries\ESP8266WiFi\src/ESP8266WiFiGeneric.h:27:22: fatal error: functional: No such file or directory
    #include
    ^
    compilation terminated.
    exit status 1
    Fehler beim Kompilieren für das Board Arduino/Genuino Uno.
    Ich habe schon ein wenig Erfahrung mit dem Arduino , habe aber wenig Erfahrung oder spezielle Kenntnisse mit der Programmierung, kann mir daher nicht erklären, wie ich den Fehler beseitigen könnte.
    Die Com 6 meldet :
    AT+CIPMUX=1
    AT+CIPSERVER=1,80
    AT+CIPSTO=0
    AT+CWMODE?
    AT+CWSAP?
    AT+CIFSR
    Es können keine AT-Kommandos gesendet werden.
    Für Hilfe danke im Voraus.
    Klaus

  6. Hallo Fans,
    Nachsatz zu:
    Ich bin dabei, den sketch von Hubert Baumgarten ESP8266_HTM_01 zum Laufen zu bringen
    #include Muß es heißen!!
    ^

    Für Hilfe danke im Voraus.
    Klaus

  7. Nochmal mit Zeichenerläuterung:
    Klaus Saare
    jetzt gerade
    Dein Kommentar wartet auf Freischaltung.

    Hallo Fans,
    Nachsatz zu:
    Ich bin dabei, den sketch von Hubert Baumgarten ESP8266_HTM_01 zum Laufen zu bringen
    #include (Kleiner als)functional (größer als) muß es heißen!!
    ^

    Für Hilfe danke im Voraus.
    Klaus

  8. Hallo Hubert – habe die Seite schon vor einer Weile entdeckt und habe jetzt aber erst mein WEMOS D1 min board zum Laufen gebracht. Damit laufen alle beiden HTML Beispiele auf Anhieb. Nun will ich versuchen, ein paar Radio-Buttons zu installieren, mit denen ich z.B. Relais ansteuern kann. Weiterhin will ich einen Analogwert abfragen und zum Client senden. Ich werde dann berichten, wie erfolgreich ich war, oder ggf. hier einen Hilferuf loslassen. Danke für die Veröffentlichung der sehr lehrreichen Beispiele.
    Gruß Peter

  9. Hallo Fans,
    Neulich hatte mir Hubert Baumgarten auf meine Frage geantwortet, ich habe alles neu eingerichtet und bekomme nun für eine andere Konfiguration den gleichen Fehler wie für ESP8266_HTML_Demo_01:

    1 x ESP-12F Doit-Modul
    1 x 0,93″ OLED-Bildschirm
    1 x 4-Relais-Modul

    die folgende Fehlerausschrift:
    Arduino: 1.8.4 (Windows 10), Board: „NodeMCU 1.0 (ESP-12E Module), 80 MHz, 115200, 4M (3M SPIFFS)“
    Build-Optionen wurden verändert, alles wird neu kompiliert
    Archiving built core (caching) in: C:\Users\UKSAA_~1\AppData\Local\Temp\arduino_cache_132476\core\core_esp8266_esp8266_nodemcuv2_CpuFrequency_80,UploadSpeed_115200,FlashSize_4M3M_ebf600f747ad56cd21b1798c997094c7.a
    Der Sketch verwendet 260403 Bytes (24%) des Programmspeicherplatzes. Das Maximum sind 1044464 Bytes.
    Globale Variablen verwenden 37672 Bytes (45%) des dynamischen Speichers, 44248 Bytes für lokale Variablen verbleiben. Das Maximum sind 81920 Bytes.
    warning: espcomm_sync failed
    error: espcomm_open failed
    error: espcomm_upload_mem failed
    error: espcomm_upload_mem failed
    Am sketch liegt es wohl nicht.
    Für jede Hilfe im Voraus herzlichen Dank.

  10. Hallo,

    Ich hätte mal eine Frage bezüglich der Bild datei im ersten beispiel code?
    wie hast du diese datei erstellt? gibt es dazu eine bestimmte Software die du nutzt? Ich finde nichts auf tante Google…

    • Der aktueller Wert des Slider wird in meinem Beispiel in die Variable Volume eingelesen. Diese ist als Byte definiert.Für Deine Anwendung empfehle ich die Definition als unsigned int.

  11. Hallo Hubert,
    Ich versuche seit tagen einen ds18b20 einzubinden. Die Auswertung des sensors ist kein Problem! Aber das einbringen in die html Seite scheitert? Kann es sein dass hier nicht mit float gearbeitet werden kann? Hat jemand vielleicht eine Idee wie soetwas realisiert werden könnte?

  12. Dieses Beispiel hat mir super geholfen, ich habe etwas CSS & JScript mit rein gebracht.
    Eigentlich würde es jetzt super funktionieren, nur leider sprenge ich jetzt wohl irgend eine Grenze einer Variablen.
    Ich hatte vermutet „HTML_String“ (char HTML_String[5000];) aber den Wert einfach höher setzen hat nicht gereicht, gibt es noch eine andere Stelle an der ich schrauben könnte?
    (alternative ist noch eben den Seiteninhalt zu unterteilen)
    Danke für diesen sehr hilfreichen Beitrag!

  13. Alles Super:
    Laenge Header : 85
    Laenge HTML : 4770
    Fehlerfall:
    Laenge Header : 85
    Laenge HTML : 4870
    als Nachtrag habe ich mal die Werte ermittelt ab wann es bei mir zum Fehler kommt, im Fehlerfall sendet der ESP8266(12) nach dem ersten Seitenaufruf weiter LeerZeilen bis in alle Ewigkeit.
    Eventuell kennt das ja schon jemand und weis was hier zu tun ist?

  14. Hallo Hubert,
    ansich funktioniert die Webseite ganz gut aber in der funktion
    void WiFi_Start_STA() {

    habe ich dann noch diese Zeile eingefügt:

    WiFi.begin(ssid_sta, password_sta);

    dann verbindet sich der ESP auch mit dem lokalen Router.
    Außerdem zeigen verschiedene Browser die Datum/Zeit Felder falsch an.
    Sonst Top!!!!!
    Gruß Günter

  15. Hi

    Besten Dank für diese Ausarbeitung!
    Ich versuche gerade, diverse Sensoren per ESP8266 anzeigen zu lassen (mitgeschnitten via CAN-Modul, wo einige Arduinos die Sensorwerte drauf geben) – Das klappt auch halbwegs, mit Aktualisierung, alle 3 Sekunden wird per JavaScript eine Unterseite aufgerufen, Die als Rückgabe ID=Wert Paare zurück gibt, Die vom JavaScript interpretiert in einer Tabelle aktuell gehalten werden.
    (Jedes relevante Tabellenfeld hat eine eindeutige ID, Welche auch in der Rückgabe bereits vom ESP generiert wird – der JavaScript muß ’nur noch‘ getElementByID=arg[0].value=arg[1] au7sführen)
    Nun wollte ich die Daten ‚graphisch‘ etwas hübscher darstellen – in HTML ASCII-Art – hier bekomme ich recht schnell, daß der ESP resettet – wohl, weil meine Datenmenge (übrigens per String zusammen geklebt) wohl die Innereien zerreißt.
    Hier der Ansatz: Char-Array … klar … logisch … und dann noch, Was ich in den Tiefen des WWW ebenfalls nicht finden konnte – Übertragen in ‚kleinen Happen‘.
    Junge – Du bist top!
    Ob Dein Sketch Heute noch lauffähig ist, werde ich gleich herausfinden – Deine Ideen/Umsetzungen werden auf jeden Fall in mein Projekt einfließen.

    MfG

    PS: Ich durfte auch über HTML5 Canvas stolpern – vll. gibt’s ja doch noch Grafik vom ESP 🙂

  16. Nachtrag: Der Sketch funktioniert Jan/2021 immer noch.
    Einzig eine unbenutzte Variable tmp wird in
    int Pick_N_Zahl(const char * tx, char separator, byte n)
    angemeckert – Die wird sonst zur Generierung der Rückgabe benutzt – hier nicht benötigt.
    Kostet aber auch Nichts – der Kompiler optimiert Die weg.

    Habe nur die SSID und das zugehörige PW in einen separaten TAB (pw.h) abgelegt (und Diesen per #include „pw.h“ includiert) – so ist mein PW nicht direkt beim Vorzeigen des Sketch überall bekannt.

    MfG

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

I accept that my given data and my IP address is sent to a server in the USA only for the purpose of spam prevention through the Akismet program.More information on Akismet and GDPR.

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.