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

*Info: In diesem Beitrag verweisen orangefarbende Links auf Affiliates.