Aus Raspberry Pi Geek 10/2022

ESP32: Webserver zum Konfigurieren der Einstellungen aufsetzen (Seite 2)

TIPP

Den Schaltplan zum Projekt, den Code zum Löschen des Flash-Speichers sowie das Programm für den Konfigurationsserver finden Sie im Download-Bereich zu diesem Artikel.

Programm

Die Funktionsweise des Konfigurationsmodus (Listing 2) gestaltet sich recht einfach. Wir verwenden von den 4096 Byte Flash-Speicher des ESP32 lediglich 512 Byte. Die teilen wir in acht Speicherstellen zu je 64 Byte auf und nummerieren sie von 0 bis 7 durch. Mit den Funktionen saveToEEPROM() und readFromEEPROM() greift das Programm auf die Speicherstellen zu. Als Datentyp für die zu speichernden Werte dient String. Das erleichtert es, die Werte später im Webserver zu verwenden.

Mit der Funktion handleRoot() ab Zeile 42 erzeugen wir eine einfache HTML-Seite, die es erlaubt, die Werte aus dem Flash-Speicher zu bearbeiten. Die Funktion lädt die Daten zunächst einmal in das Array value. Zum Ansprechen der einzelnen Speicherstellen verwenden wir immer die vergebenen Nummern. Deswegen tragen die Namen der HTML-Input-Elemente auch solche. Letztendlich haben wir damit eine generische Lösung zur Adressierung der Speicherplätze. Den Inhalt bestimmt ausschließlich die Beschriftung im HTML. Abbildung 3 zeigt die Ausgabe des (ziemlich spartanischen) Webservers.

Abbildung 3: Die Weboberfläche, mit dem Smartphone aufgerufen.

Abbildung 3: Die Weboberfläche, mit dem Smartphone aufgerufen.

In der Funktion setup() ab Zeile 65 startet abhängig von der Schalterstellung entweder der Konfigurationswebserver oder das normale Programm in der loop()-Funktion. Arbeitet der ESP32 im Standardmodus, lassen sich die Parameter mit der Funktion readFromEEPROM() auslesen und verwenden.

Im Konfigurationsmodus fährt der ESP32 einen Access Point hoch. Das darauf laufende WLAN trägt den Namen ESP32_CONFIG_SERVER ohne Passwort. Um zum Webserver zu gelangen, geben Sie nach dem Verbinden mit dem Access Point im Webbrowser die IP-Adresse 192.168.123.1 ein.

Listing 2

Webserver

#include <WiFi.h>
#include <WiFiClient.h>
#include <WiFiAP.h>
#include <WebServer.h>
#include <EEPROM.h>
#define LED 17
#define MODE_SWITCH 12
#define EEPROM_SIZE 512
#define MAX_HTTP_ARGUMENTS 3
const char *CONFIG_SERVER_SSID = "ESP32_CONFIG_SERVER";
const char *CONFIG_SERVER_PASS = "";
WebServer configServer(80);
IPAddress AP_LOCAL_IP(192, 168, 123, 1);
IPAddress AP_GATEWAY_IP(192, 168, 123, 1);
IPAddress AP_NETWORK_MASK(255, 255, 255, 0);
char buffer[64];
void saveToEEPROM(int storage, String configValue) {
  configValue.toCharArray(buffer, 64);
  int i = 0;
  for (int address = (storage * 64); address < (storage * 64) + 64; address++) {
    EEPROM.write(address, buffer[i] );
    i++;
    if (buffer[i] == 0)break;
  }
  EEPROM.commit();
}
String readFromEEPROM(int storage) {
  String configValue = "";
  for (int address = (storage * 64); address < (storage * 64) + 64; address++) {
    if (EEPROM.read(address) == 0)break;
    configValue += char(EEPROM.read(address));
  }
  return configValue;
}
void handleRoot() {
  String values[MAX_HTTP_ARGUMENTS];
  if (configServer.arg("SEND") == "Submit") {
    for (int httpArgument = 0; httpArgument < MAX_HTTP_ARGUMENTS; httpArgument++) {
      saveToEEPROM(httpArgument, configServer.arg(String(httpArgument)));
    }//for
  }//if
  for (int httpArgument = 0; httpArgument < MAX_HTTP_ARGUMENTS; httpArgument++) {
    values[httpArgument] = readFromEEPROM(httpArgument);
  }//for
  String message = "<HTML><BODY><H1>Configuration</H1><BR>";
  message += "<TABLE><FORM ACTION='/' METHOD='post'>";
  message += "<TR><TD>SSID:</TD><TD><INPUT TYPE='text' NAME='0' VALUE='" + values[0] + "'>&nbsp;</TD></TR>";
  message += "<TR><TD>PASS:</TD><TD><INPUT TYPE='text' NAME='1' VALUE='" + values[1] + "'>&nbsp;</TD></TR>";
  message += "<TR><TD>HOST:</TD><TD><INPUT TYPE='text' NAME='2' VALUE='" + values[2] + "'>&nbsp;</TD></TR>";
  message += "<TR><TD>&nbsp;</TD><TD><INPUT TYPE='submit' VALUE='Submit' NAME='SEND'></INPUT></TD></TR>";
  message += "</FORM></TABLE>";
  message += "</BODY></HTML>";
  configServer.send(200, "text/html", message);
}
void setup() {
  pinMode(LED, OUTPUT);
  pinMode(MODE_SWITCH, INPUT_PULLUP);
  Serial.begin(115200);
  EEPROM.begin(EEPROM_SIZE);
  if (digitalRead(MODE_SWITCH)) {
    WiFi.softAPConfig(AP_LOCAL_IP, AP_GATEWAY_IP, AP_NETWORK_MASK);
    WiFi.softAP(CONFIG_SERVER_SSID, CONFIG_SERVER_PASS);
    configServer.on("/", handleRoot);
    configServer.begin();
    while (true) {
      yield();
      configServer.handleClient();
    }//while
  }//if
  char buffer_ssid[64];
  readFromEEPROM(0).toCharArray(buffer_ssid, 64);
  char *ssid = buffer;
  char buffer_pass[64];
  readFromEEPROM(1).toCharArray(buffer_pass, 64);
  char *pass = buffer1;
  Serial.print("SSID: "); Serial.println(ssid);
  Serial.print("PASS: "); Serial.println(pass);
  Serial.print("HOST: "); Serial.println(readFromEEPROM(2));
  WiFi.begin(ssid,pass );
  while (WiFi.status() != WL_CONNECTED) {
    delay(250);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
}//setup
void loop() {
  digitalWrite(LED, !digitalRead(LED));
  delay(500);
}

Bibliothek erzeugen

Das hier gezeigte Programm eignet sich gut dazu, um es in eine Bibliothek umzuwandeln. In einer solchen Library legen Sie Programmteile ab, die Sie in anderen Projekten wiederverwenden möchten. Um eine Bibliothek zu erzeugen, die sich in der Arduino IDE einbinden lässt, müssen Sie zunächst einen neuen Ordner im Verzeichnis libraries/ der Arduino IDE anlegen.

Innerhalb des neu erstellten Ordners erzeugen Sie nun zwei neue Dateien, die den exakt selben Namen wie das Verzeichnis tragen, allerdings jeweils eine andere Dateiendung haben. Achten Sie dabei auf die Groß- und Kleinschreibung. Eine der Dateien bekommt .h als Endung, die zweite .cpp.

Bei der Datei mit der Extension .h handelt es sich um eine sogenannte Header-Datei. Darin deklarieren Sie alle nötigen Variablen, Definitionen und Funktionsnamen. Der eigentliche Code der Funktionen steht in der Datei mit der Endung .cpp. Nach dem Erstellen der Dateien lassen sich diese als Bibliothek in andere Programme mit dem Kommando #include einbinden. Bei der Arbeit mit der Arduino IDE brauchen Sie Bibliotheken nicht vorab zu kompilieren, die IDE übernimmt das für Sie.

Um eine Idee zu vermitteln, wie die einzelnen Dateien aussehen, zeigen Listing 3 und Listing 4 die Bestandteile einer sehr einfachen Bibliothek namens mlib, Listing 5 demonstriert das Einbinden der Library in ein Programm. Es empfiehlt sich, zu verhindern, dass ein Programm eine Bibliothek mehrfach lädt. Das erledigen Sie mit den Präprozessorkommandos in den ersten beiden Zeilen von Listing 3.

Falls Sie tiefer in das Thema Bibliotheken für die Arduino IDE einsteigen möchten, finden Sie auf der englischsprachigen Homepage [3] des Arduino-Projekts reichlich Lesestoff dazu.

Listing 3

mlib.h

#ifndef mlib_h
  #define mlib_h
  int addOne(int a);
#endif

Listing 4

mlib.cpp

#include "mlib.h"
int addOne(int a) {
  return a+1;
}

Listing 5

Bibliothek einbinden

#include <mlib.h>
void setup() {
  Serial.begin(115200);
  // put your setup code
  //  here, to run once.
}
int i=1;
void loop() {
  Serial.println(i=addOne(i));
  delay(1000);
}
DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 5 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
RASPBERRY PI GEEK KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Raspberry Pi Geek bei Google Play Readly Logo
Nach oben