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/)?utm_source=rss&utm_medium=rss 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/?utm_source=rss&utm_medium=rss
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?utm_source=rss&utm_medium=rss 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.
Der Beitrag HTML Kochbuch mit ESP8266 und Arduino IDE erschien zuerst auf Arduino-Hannover.