ESP32 Asynchroner Webserver

Es kann oft notwendig sein, mit unserer Anwendung, die auf dem ESP32-Mikrocontroller läuft, über eine grafische Oberfläche zu kommunizieren. Ob Konfigurationsänderung oder Anzeige von Sensorwerten etc. Die naheliegende Lösung ist die Verwendung des Webbrowsers, da wir unser Smartphone fast immer zur Hand haben. Dabei hilft der asynchrone Webserver. In diesem Artikel möchte ich die Anwendung der ESPAsyncWebServer-Bibliothek auf einem ESP32-Modul anhand von Beispielen demonstrieren.

Inhalt:

  Einführung in die Verwendung der ESPAsyncWebServer-Bibliothek

  Senden von Formulardaten mit der POST-Methode

  Website mit passwortgeschützter Einstellungs-seite

Einführung in die Verwendung der ESPAsyncWebServer-Bibliothek

Um den asynchronen Webserver zu verwenden, muss die ESPAsyncWebServer-Bibliothek installiert sein. Die AsyncTCP-Bibliothek muss als Abhängigkeit von ESPAsyncWebServer installiert werden. Die Bibliotheken sind verfügbar, auf die unten stehenden Links.

ESPAsyncWebServer-Bibliothek

AsyncTCP-Bibliothek

Entpacken wir die heruntergeladenen Dateien, löschen wir die Silbe „-master“, die am Ende der Datei angehängt ist, und kopieren wir sie dann in den Ordner „…/Arduino/libraries/“.

Dann die Arduino IDE starten/neu starten.

Am Anfang unseres Codes importieren wir die WiFi.h-Bibliothek sowie die zuvor heruntergeladenen zwei Bibliotheken oben.

#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>

Danach deklarieren wir zwei Variablen für die Anmeldeinformationen des WiFi-Netzwerks.

const char* ssid = "wifi_ssid";
const char* password = "supergeheimes_passwort";

Erstellen wir eine Instanz des AsyncWebServer-Objekts und geben Sie den Port an, auf dem der Server lauschen soll. Port 80 ist der Standard-HTTP-Port.

AsyncWebServer server(80);

Wir starten die serielle Verbindung in der Funktion setup().

Serial.begin(115200);

Wir verbinden uns mit dem WLAN-Netzwerk und drucken die ESP32-IP-Adresse auf dem seriellen Monitor.

WiFi.begin(ssid, password);

Serial.print("Verbindung zum WiFi-Netzwerk.");
while (WiFi.status() != WL_CONNECTED
{
  delay(500);
  Serial.print(".");
}
Serial.println();
Serial.println("Ist verbunden!");
Serial.print("IP-Adresse: ");
Serial.println(WiFi.localIP());

Der asynchrone Webserver kann mehrere Verbindungen gleichzeitig verarbeiten. Jeder verbundene Client ist einem AsyncWebServerRequest-Objekt zugeordnet. ESPAsyncWebServer läuft als Hintergrundprozess.

Durch den Aufruf der Methode server.on() legen wir im ersten Parameter die Route fest, auf der der Server auf eingehende HTTP-Anfragen lauscht, das zweite Argument enthält die Art der HTTP-Anfragen, in unserem Fall verwenden wir die GET-Methode,
und schließlich in der dritter Parameter geben wir eine Funktion an, die dann ausgeführt werden soll, wenn eine Anfrage auf der angegebenen Route eintrifft.

Wir können die HTTP-Antwort mit der Methode send von AsyncWebServerRequest bereitstellen. Das folgende Beispiel sendet eine einfache Zeichenkette, hier hat send drei Parameter. Das erste Argument ist der 200 HTTP-Antwortcode, das zweite Argument ist die Sendemethode, in unserem Fall „text/plain“, das letzte Argument ist die zu sendende Zeichenkette, „Hallo AsyncWebServer“.

Dann starten wir den Server mit die Funktion server.begin() aufrufen.

server.on("/hello_server", HTTP_GET, [](AsyncWebServerRequest *request)
{
  request->send(200, "text/plain", "Hallo AsyncWebServer!");
});
 
server.begin();

Der loop()-Funktion wird nichts hinzugefügt, da keine Client-Handler-Funktion aufgerufen werden muss, um AsyncWebServer zu verwenden.

Sehen wir uns den vollständigen Code an:

#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>

const char* ssid = "wifi_ssid";
const char* password = "supergeheimes_passwort";

AsyncWebServer server(80);

void setup()
{
  Serial.begin(115200);
 
  WiFi.begin(ssid, password);

  Serial.print("Verbindung zum WiFi-Netzwerk.");
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println();
  Serial.println("Ist verbunden!");
  Serial.print("Ip-Addresse: ");
  Serial.println(WiFi.localIP());
 
  server.on("/hello_server", HTTP_GET, [](AsyncWebServerRequest *request)
  {
    request->send(200, "text/plain", "Hallo AsyncWebServer!");
  });
 
  server.begin();
}
void loop(){}

Sobald dies geschehen ist, öffnen wir unsere bevorzugten Webbrowser und fügen wir die vom ArduinoIDE seriellen-Monitor kopierte IP-Adresse, in die Adresszeile des Browsers ein, zusammen mit dem Pfad, der in der Funktion server.on angegeben wurde. Zum Beispiel so.

http://192.168.1.20/hello_server

Das Ergebnis ist im Browserfenster zu sehen.

Senden von Formulardaten mit der POST-Methode

Im folgenden Beispiel übertragen wir Daten aus einem Texteingabefeld mit der POST-Methode und zeigen dann den eingegebenen Text an.

#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>

AsyncWebServer server(80);

const char* ssid = "wifi_ssid";
const char* password = "supergeheimes_passwort";

const char* PARAM_MESSAGE = "message";
String postMessage = "";

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML>
<html>
  <head>
    <title>ESP32 Asynchroner Webserver</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script>
      function submitData() 
      {
        setTimeout(function(){
          document.location.reload(false); 
        }, 1000);   
      }
    </script>  
  </head>
  <body>
    <center>
      <h2>ESP32 Asynchroner Webserver</h2>

      <div style="height:50px"></div>

      <p>HTTP POST Nachricht: %POST_MESSAGE%</p>
      
      <form action="/post" method="POST" target="self_page">
        <input type="text" name="message">
        <br><br>
        <input type="submit" value="Senden..." onclick="submitData()">
      </form>
    </center>
    <iframe style="display:none" name="self_page"></iframe>
  </body>
</html> )rawliteral";

void notFound(AsyncWebServerRequest *request) 
{
  request->send(404, "text/plain", "Die gesuchte Seite kann nicht gefunden werden");
}

String processor(const String& var)
{
  if(var == "POST_MESSAGE")
  {
    return postMessage;
  }
  return String();
}

void setup()
{
  Serial.begin(115200);
 
  WiFi.begin(ssid, password);

  Serial.print("Verbindung zum WiFi-Netzwerk.");
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println();
  Serial.println("Ist verbunden!");
  Serial.print("Ip-Addresse: ");
  Serial.println(WiFi.localIP());

  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
  {
    request->send_P(200, "text/html", index_html, processor);
  });

  server.on("/post", HTTP_POST, [](AsyncWebServerRequest *request)
  {
    if(request->hasParam(PARAM_MESSAGE, true))
    {
      postMessage = request->getParam(PARAM_MESSAGE, true)->value();
    }
    request->send(200);
  });

  server.onNotFound(notFound);

  server.begin();
}

void loop() {}

Der HTML-Code wird in das konstante Zeichenarray „index_html[]“ platziert. Das Schlüsselwort „PROGMEM“ ist ein Variablen-modifikator, der den Compiler anweist, den Inhalt der Variablen im Flash-Speicher zu speichern.

const char index_html[] PROGMEM = R"rawliteral(....)rawliteral";

„R“ bedeutet, dass alles zwischen den Trennzeichen als Roh zeichenfolge behandelt wird. Also alles ist, wie es ist aufgeschrieben, zwischen „rawliteral( und …. )rawliteral“ beschrieben. Im Beispiel verwenden wir das Trennzeichen „rawliteral“, aber wir können stattdessen alles frei wählen.

Wenn in der Adressleiste des Browsers etwas falsch eingegeben wird, behandelt die Funktion „notFound()“ den Fehler. Es sendet einen 404-HTML-Antwortcode und zeigt den angegebenen Inhalt an, in diesem Fall einen „text/plain“-Type Text: „Die gesuchte Seite kann nicht gefunden werden“.

void notFound(AsyncWebServerRequest *request) 
{
  request->send(404, "text/plain", "Die gesuchte Seite kann nicht gefunden werden");
}

Wir haben einen Platzhalter in den HTML-Code eingefügt. Platzhalter werden durch %-Symbole getrennt, in unserem Beispiel ist es (%POST_MESSAGE%). Beim Laden der Website wird die Funktion „processor()“ ausgeführt und dort, wo sie den Platzhalter im Code findet, ersetzt sie ihn durch den passenden String.

In diesem Beispiel ändern wir den Inhalt eines Absatzes, indem wir den Wert der globalen Variablen „postMessage“ an den Platzhalter %POST_MESSAGE% übergeben.

String postMessage = "";

<html>...
  <p>HTTP POST Nachricht: %POST_MESSAGE%</p>
...</html>

String processor(const String& var)
{
  if(var == "POST_MESSAGE")
  {
    return postMessage;
  }
  return String();
}

In unserem Code dient die erste Funktion server.on() dazu, die Startseite anzuzeigen. Der erste Parameter, der Pfad, enthält nur das Zeichen „/“, damit der ESP32 Anfragen an die IP-Adresse überwacht.

Die Funktion send_P() sendet als Response den 200 HTTP Response Code, definiert den Inhaltstyp, in diesem Fall „text/html“. Im dritten Parameter müssen wir angeben, wo sich der anzuzeigende HTML-Code befindet, in unserem Beispiel der im index_html-Array gespeicherte String. Da wir einen Platzhalter in den HTML-Code eingefügt haben, muss die Prozessor-funktion definiert werden, die ihn verarbeitet, dies ist die im vierten Parameter angegebene Processor()-Funktion.

server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
{
  request->send_P(200, "text/html", index_html, processor);
});

Im Beispielcode lauscht die zweite server.on()-Funktion auf den „/post“-Pfad und akzeptiert HTTP_POST-Anforderungen. Innerhalb der Funktion prüfen wir, ob der angegebene Parameter Daten enthält. Wenn ja, extrahieren wir den Inhalt des Parameters zur weiteren Verarbeitung.

Diese „PARAM_MESSAGE“ trägt dabei den Inhalt des mit „message“ bezeichneten Texteingabefeldes, dessen Wert an die globale Variable „postMessage“ übergeben wird. Schließlich sendet es einen 200-HTTP-Antwortcode.

const char* PARAM_MESSAGE = "message";
String postMessage = "";

</html>...
  <input type="text" name="message">
...</html>

server.on("/post", HTTP_POST, [](AsyncWebServerRequest *request)
{
  if(request->hasParam(PARAM_MESSAGE, true))
  {
    postMessage = request->getParam(PARAM_MESSAGE, true)->value();
  }
  request->send(200);
});

Die Funktion server.onNotFound(notFound) überwacht Anfragen, die an nicht existierende Seiten gerichtet sind, für die wir die Funktion „notFound()“ als Parameter hinzufügen, die diese Fehler behandelt.

server.onNotFound(notFound);

Schließlich starten wir AsyncWebServer, indem wir die Funktion „server.begin()“ aufrufen.

server.begin();

Laden wir den Code auf den ESP32 hoch, und die Ausgabe im Webbrowser anzeigen.

ESP32 asynchrone Webserver-Method POST
ESP32 asynchrone Webserver-Method POST

Website mit passwortgeschützter Einstellungsseite

Im dritten Beispiel erstellen wir eine Website, die aus einer Hauptseite und einer Einstellungs-seite besteht.

Beim ersten Start versucht der ESP32, sich mit dem WLAN-Netzwerk zu verbinden, verfügt jedoch nicht über die erforderlichen Anmeldeinformationen, um sich mit dem WLAN zu verbinden. Nach 15 Sekunden erfolgloser Versuche wechselt es automatisch in den AP-Modus. Verbinden wir in diesem Fall zu dem ESP32 WLAN Access Point.

Für die Verbindung erforderliche Standardkennungen:
  SSID: Esp_AP
  Passwort: 12345678

Wichtig! Das Passwort muss mindestens 8 Zeichen lang sein, sonst wird es vom Server nicht berücksichtigt.

Wenn wir sind erfolgreich mit dem ESP32 Access Point verbunden haben, öffnen wir die IP-Adresse ‚192.168.7.22‘ in unser Webbrowser.

Auf der Hauptseite zeigt unter der Titelzeile ein Textfeld den Zustand der LED an, die mit dem darunter liegenden Schaltfläche ein- oder ausgeschaltet werden kann. Die LED ist mit dem GPIO2-Pin des ESP32 verbunden.

Weiter unten finden wir eine weitere Schaltfläche, durch Klicken darauf können wir zur Einstellungsseite gehen.

ESP32 asynchrone Webserver-Startseite
ESP32 asynchrone Webserver-Startseite

Die Einstellungsseite ist passwortgeschützt, die für den Zugang erforderlichen Anmeldeinformationen sind:
  Standard benutzername: admin
  Standard passwort: admin .

ESP32 asynchrone Webserver-Anmeldung
ESP32 asynchrone Webserver-Anmeldung

Es gibt zwei Formulare auf der Einstellungsseite. Im ersten Formular können wir für die Wi-Fi-Verbindung und den Access Point (AP) erforderlichen Anmelde-IDs festlegen/ändern. Durch Klicken auf die Schaltfläche „Speichern“ werden die Daten in einem nichtflüchtigen Speicherbereich, SPIFFS (SPI Flash File Storage), gespeichert.

Der ESP32 wird dann neu gestartet.

ESP32 asynchrone Webserver-Netzwerkeinstellungen
ESP32 asynchrone Webserver-Netzwerkeinstellungen

Im zweiten Formular können wir die Anmeldeinformationen der Einstellungsseite ändern. Auch diese Kennungen werden in einem nichtflüchtigen Speicher gespeichert. ESP32 wird nach dem Speichern nicht neu gestartet.

ESP32 asynchroner Webserver Anmeldedaten änderung
ESP32 asynchroner Webserver Anmeldedaten änderung

Unterhalb der beiden Formulare finden wir zwei weitere Schaltflächen, die meiner Meinung nach nicht erklärt werden müssen.

ESP32 asynchroner Webserver-Schaltflächen
ESP32 asynchroner Webserver-Schaltflächen

Dieser Beispielcode kann ein guter Ausgangspunkt für Projekte sein, bei denen es notwendig ist, die Einstellungen zu ändern.

#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <AsyncTCP.h>
#include <Hash.h>
#include "FS.h"
#include "SPIFFS.h"
#define FORMAT_SPIFFS_IF_FAILED true
const char ledPin = 2;
String httpUsername = "admin";
String httpPassword = "admin";
String _AP_ssid = "Esp_AP";
String _AP_password = "12345678";
IPAddress local_IP(192,168,7,22);
IPAddress gateway(192,168,7,1);
IPAddress subnet(255,255,255,0);
AsyncWebServer server(80);
bool wifiIsConnected = false;
String ledValue ="false";
String ledState ="Die LED ist aus!";
const char* param_ledvalue = "ledvalue";
const char* param_ap_ssid = "inputApSsid";
const char* param_ap_password = "inputApPassword";
const char* param_wifi_ssid = "inputWifiSsid";
const char* param_wifi_password = "inputWifiPassword";
const char* param_httpUsername = "inputHttpUsername";
const char* param_httpPassword = "inputHttpPassword";
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML>
<html>
<head>
<title>Startseite</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script>
function submitData() 
{
setTimeout(function(){
document.location.reload(false); 
}, 1000);   
}
</script>
</head>
<body>
<center>
<h2>ESP32 Asynchroner Webserver</h2>
<div style="height:50px"></div>
<p>%LED_STATE%</p>
<form action="/ledSwitch" target="self_page">
<input type="hidden" name="ledvalue" value="%LED_VALUE%">
<input type="submit" value="LED" onclick="submitData()">
</form>
<br><br>
<button onclick="window.location.href='/settings';">Einstellungen</button>
<iframe style="display:none" name="self_page"></iframe>
</center>
</body>
</html> )rawliteral";
const char settings_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML>
<html>
<head>
<title>Einstellungsseite</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script>
function submitData() 
{
setTimeout(function(){
document.location.reload(false); 
}, 1000);   
}
function logoutButton() 
{
var xmlHttpRequest = new XMLHttpRequest();
xmlHttpRequest.open("GET", "/logout", true);
xmlHttpRequest.send();
setTimeout(function() {
window.open("/","/"); 
}, 1000);
}
</script>
</head>
<body>
<center>
<h2>Einstellungen</h2>
<div style="height:50px"></div>
<fieldset style="width:300px">
<legend>Netzwerkeinstellungen:</legend>
<br><br>
<form action="/network" target="self_page">
AP SSID:<br>
<input type="text" value='%inputApSsid%' name="inputApSsid" size="20">
<br><br>
AP Passwort:<br>
<input type="text" value='%inputApPassword%' name="inputApPassword" size="20">
<br>( Legalább 8 karakter! )
<br><br>
Wifi SSID:<br>
<input type="text" value='%inputWifiSsid%' name="inputWifiSsid" size="20">
<br><br>
Wifi Passwort:<br>
<input type="text" value='%inputWifiPassword%' name="inputWifiPassword" size="20">
<br><br>
<input type="submit" value="Speichern" onclick="submitData()">
</form>
<br><br>
</fieldset>
<div style="height:20px"></div>
<fieldset style="width:300px">
<legend>Login-Daten:</legend>
<br><br>
<form action="/http_login_set" target="self_page">
Benutzername:<br>
<input type="text" value='%inputHttpUsername%'  name="inputHttpUsername" size="20">
<br><br>
Passwort:<br>
<input type="text" value='%inputHttpPassword%'  name="inputHttpPassword" size="20">
<br><br><br>
<input type="submit" value="Speichern" onclick="submitData()">
</form>
<br><br>
</fieldset>
<div style="height:20px"></div>
<fieldset style="width:300px">
<legend>Zur Startseite</legend>
<br><br>
<button onclick="window.location.href='/';">Startseite</button>
<br><br>
</fieldset>
<div style="height:20px"></div>
<fieldset style="width:300px">
<legend>Abmeldung</legend>
<br><br>
<button onclick="logoutButton()">Abmeldung</button>
<br><br>
</fieldset>
<div style="height:50px;"></div>
<iframe style="display:none" name="self_page"></iframe>
</center>
</body>
</html> )rawliteral";
String inputValueAdapter(const String& inputValueSet)
{
if(inputValueSet == "LED_STATE")
{
return ledState;
}
if(inputValueSet == "LED_VALUE")
{
return ledValue;
}
if(inputValueSet == "inputApSsid")
{
return readFile(SPIFFS, "/apSsid.txt");
}
if(inputValueSet == "inputApPassword")
{
return readFile(SPIFFS, "/apPassword.txt");
}
if(inputValueSet == "inputWifiSsid")
{
return readFile(SPIFFS, "/wifiSsid.txt");
}
if(inputValueSet == "inputWifiPassword")
{
return readFile(SPIFFS, "/wifiPassword.txt");
}
if(inputValueSet == "inputHttpUsername")
{
return readFile(SPIFFS, "/httpUsername.txt");
}
if(inputValueSet == "inputHttpPassword")
{
return readFile(SPIFFS, "/httpPassword.txt");
}
return String();
}
void setupAsyncServer()
{
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
{
request->send_P(200, "text/html", index_html, inputValueAdapter);
});
server.on("/ledSwitch", HTTP_GET, [](AsyncWebServerRequest *request)
{
ledValue = request->getParam(param_ledvalue)->value();
if(ledValue == "true")
{
ledValue = "false";
digitalWrite(ledPin, LOW);
ledState ="Die LED ist aus!";
}
else if(ledValue == "false")
{
ledValue = "true";
digitalWrite(ledPin, HIGH);
ledState ="Die LED ist eingeschaltet!";
}
request->send(200, "text/text", ledValue);
});
server.on("/settings", HTTP_GET, [](AsyncWebServerRequest *request)
{
String temp_httpUsername = readFile(SPIFFS, "/httpUsername.txt");
if(temp_httpUsername == "")
{
temp_httpUsername = httpUsername;
}
const char* http_username = temp_httpUsername.c_str();
String temp_httpPassword = readFile(SPIFFS, "/httpPassword.txt");
if(temp_httpPassword == "")
{
temp_httpPassword = httpPassword;
}
const char* http_password = temp_httpPassword.c_str();
if(!request->authenticate(http_username, http_password))
{
return request->requestAuthentication();
}
request->send_P(200, "text/html", settings_html, inputValueAdapter);
});
server.on("/network", HTTP_GET, [](AsyncWebServerRequest *request)
{
String temp_httpUsername = readFile(SPIFFS, "/httpUsername.txt");
if(temp_httpUsername == "")
{
temp_httpUsername = httpUsername;
}
const char* http_username = temp_httpUsername.c_str();
String temp_httpPassword = readFile(SPIFFS, "/httpPassword.txt");
if(temp_httpPassword == "")
{
temp_httpPassword = httpPassword;
}
const char* http_password = temp_httpPassword.c_str();
if(!request->authenticate(http_username, http_password))
{
return request->requestAuthentication();
}
String inputMessage;
if (request->hasParam(param_ap_ssid)) 
{
inputMessage = request->getParam(param_ap_ssid)->value();
writeFile(SPIFFS, "/apSsid.txt", inputMessage.c_str());
}
if (request->hasParam(param_ap_password))
{
inputMessage = request->getParam(param_ap_password)->value();
writeFile(SPIFFS, "/apPassword.txt", inputMessage.c_str());
}
if (request->hasParam(param_wifi_ssid)) 
{
inputMessage = request->getParam(param_wifi_ssid)->value();
writeFile(SPIFFS, "/wifiSsid.txt", inputMessage.c_str());
}     
if (request->hasParam(param_wifi_password)) 
{
inputMessage = request->getParam(param_wifi_password)->value();
writeFile(SPIFFS, "/wifiPassword.txt", inputMessage.c_str());
}
request->send(200, "text/text", inputMessage);
Serial.println("ESP Restart...");
ESP.restart();
});
server.on("/http_login_set", HTTP_GET, [](AsyncWebServerRequest *request)
{
String temp_httpUsername = readFile(SPIFFS, "/httpUsername.txt");
if(temp_httpUsername == "")
{
temp_httpUsername = httpUsername;
}
const char* http_username = temp_httpUsername.c_str();
String temp_httpPassword = readFile(SPIFFS, "/httpPassword.txt");
if(temp_httpPassword == "")
{
temp_httpPassword = httpPassword;
}
const char* http_password = temp_httpPassword.c_str();
if(!request->authenticate(http_username, http_password))
{
return request->requestAuthentication();
}
String inputMessage;                                
if (request->hasParam(param_httpUsername))
{
inputMessage = request->getParam(param_httpUsername)->value();
writeFile(SPIFFS, "/httpUsername.txt", inputMessage.c_str());
}
if (request->hasParam(param_httpPassword))
{
inputMessage = request->getParam(param_httpPassword)->value();
writeFile(SPIFFS, "/httpPassword.txt", inputMessage.c_str());
}
request->send(200, "text/text", inputMessage);
});
server.on("/logout", HTTP_GET, [](AsyncWebServerRequest *request)
{
request->send(401);
});
server.onNotFound(notFound);
server.begin();
}
void notFound(AsyncWebServerRequest *request) 
{
request->send(404, "text/plain", "Die gesuchte Seite kann nicht gefunden werden");
}
String readFile(fs::FS &fs, const char * path)
{
String fileContent = "";
File file = fs.open(path, "r");
if(!file || file.isDirectory())
{
return fileContent;
}
while(file.available())
{
fileContent+=String((char)file.read());
}
file.close();
return fileContent;
}
void writeFile(fs::FS &fs, const char * path, const char * message)
{
File file = fs.open(path, "w");
if(!file)
{
return;
}
file.print(message);
file.close();
}
void setup_ap()
{
String temp_pass = readFile(SPIFFS, "/apPassword.txt");
if(temp_pass == "")
{
temp_pass = _AP_password;
}
String temp_ssid = readFile(SPIFFS, "/apSsid.txt");
if(temp_ssid == "")
{
temp_ssid = _AP_ssid;
}
const char* AP_ssid = temp_ssid.c_str();
const char* AP_password = temp_pass.c_str();
WiFi.softAPConfig(local_IP, gateway, subnet);
WiFi.softAP(AP_ssid, AP_password);
IPAddress Ap_Ip = WiFi.softAPIP();
Serial.print("Access Point IP Addresse: ");
Serial.println(Ap_Ip);
Serial.println();
}
void setup_wifi() 
{
String temp_pass = readFile(SPIFFS, "/wifiPassword.txt");
char passbuff[temp_pass.length() + 1];
temp_pass.toCharArray(passbuff, temp_pass.length() + 1);
const char* password = passbuff;
String temp_ssid = readFile(SPIFFS, "/wifiSsid.txt");
char ssidbuff[temp_ssid.length() + 1];
temp_ssid.toCharArray(ssidbuff, temp_ssid.length() + 1);
const char* ssid = ssidbuff;
WiFi.begin(ssid, password);
Serial.print("Verbindung zum WiFi-Netzwerk.");
int i = 0;
while (true) 
{
if(WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
i++;
if(i > 30)
{
wifiIsConnected = false;
Serial.println();
Serial.println("Es kann keine Verbindung zum WLAN-Netzwerk.");
break;
}
}
else
{
wifiIsConnected = true;
Serial.println();
Serial.println("Ist verbunden!!");
Serial.print("Ip Addresse: ");
Serial.println(WiFi.localIP());
Serial.println();
break;
}
}
}
void setup()
{
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, HIGH);
delay(2000);
digitalWrite(ledPin, LOW);
Serial.begin(115200);
SPIFFS.begin();
WiFi.mode(WIFI_STA);
setup_wifi();
if(!wifiIsConnected)
{
WiFi.mode(WIFI_AP);
setup_ap();    
}
setupAsyncServer();
}
void loop(){}

werbung – amazon.de