It may often be necessary to communicate with our application running on the ESP32 microcontroller via a graphical interface. Whether changing configuration or displaying sensor values, etc. The obvious solution is to use the web browser, because we almost always have our smartphone at hand. The asynchronous web server helps with this. In this article, I would like to demonstrate the application of the ESPAsyncWebServer library on an ESP32 module with examples.
Content:
Introduction to using the ESPAsyncWebServer library
Sending form data using the POST method
Website with password protected settings page
SparkFun ESP32 WROOM with ESP32-D0WDQ6 chip
Xtensa dual-core 32-bit LX6 microprocessor Up to 240MHz clock frequency, Integrated 802.11 BGN WiFi, 21 GPIO 8-electrode capacitive touch support
advertising
Introduction to using the ESPAsyncWebServer library
To use the asynchronous web server, the ESPAsyncWebServer library must be installed. The AsyncTCP library must be installed as a dependency of ESPAsyncWebServer The libraries are available by clicking on the links below.
Download ESPAsyncWebServer library
Unpack the downloaded files, delete the “-master” syllable appended to the end of the file, and then copy it to the “…/Arduino/libraries/” folder.
Then start/restart the Arduino IDE.
At the beginning of our code, we import the WiFi.h library, as well as the previously downloaded two libraries above.
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
After that, we declare two variables for the credentials of the WiFi network.
const char* ssid = "wifi_ssid";
const char* password = "supersecret_password";
Let’s create an instance of the AsyncWebServer object, specify the port where the server will listen. Port 80 is the default HTTP port.
AsyncWebServer server(80);
We start the serial connection in the setup() function.
Serial.begin(115200);
We connect to the Wifi network and print the ESP32 IP address on the serial monitor.
WiFi.begin(ssid, password);
Serial.print("Connect to the WiFi network.");
while (WiFi.status() != WL_CONNECTED
{
delay(500);
Serial.print(".");
}
Serial.println();
Serial.println("Connected!");
Serial.print("Ip address: ");
Serial.println(WiFi.localIP());
An asynchronous web server can handle multiple connections at the same time. Each connected client is associated with an AsyncWebServerRequest object. ESPAsyncWebServer runs as a background process.
By calling the server.on() method, in the first parameter we set the route where the server listens for incoming HTTP requests, the second argument contains the type of HTTP requests, in our case we use the GET method, and finally in the third parameter we specify a function that will then to be executed when a request arrives on the specified route.
We can provide the HTTP response using the send method of AsyncWebServerRequest. In the following example we send a simple string, here send has three parameters. The first parameter is the 200 HTTP response code, the second parameter is the sending method, in our case “text/plain”, the third argument is the character string to be sent, “Hello AsyncWebServer”.
After that, start the server by calling the server.begin() function.
server.on("/hello_server", HTTP_GET, [](AsyncWebServerRequest *request)
{
request->send(200, "text/plain", "Hello AsyncWebServer!");
});
server.begin();
Nothing is added to the loop() function now, since there is no need to call a client handler function to use AsyncWebServer.
Let’s see the full code:
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
const char* ssid = "wifi_ssid";
const char* password = "supersecret_password";
AsyncWebServer server(80);
void setup()
{
Serial.begin(115200);
WiFi.begin(ssid, password);
Serial.print("Connect to the WiFi network.");
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println();
Serial.println("Connected!");
Serial.print("Ip address: ");
Serial.println(WiFi.localIP());
server.on("/hello_server", HTTP_GET, [](AsyncWebServerRequest *request)
{
request->send(200, "text/plain", "Hello AsyncWebServer!");
});
server.begin();
}
void loop(){}
Upload the above code to the ESP32 using ArduinoIDE. If you have this, open your favorite web browser and add the IP address copied from the ArduinoIDE serial monitor to the path specified in the server.on function, and enter it in the address bar of the browser. For example like this.
http://192.168.1.20/hello_server
The result can be seen in the browser window.
Smart Home Wi-Fi Outlet Works with Alexa, Echo, Google Home & IFTTT, No Hub Required, Remote Control
advertising
Sending form data using the POST method
In the following example, we transfer data from a text input field using the POST method, and then display the entered text.
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
AsyncWebServer server(80);
const char* ssid = "wifi_ssid";
const char* password = "supersecret_password";
const char* PARAM_MESSAGE = "message";
String postMessage = "";
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML>
<html>
<head>
<title>ESP32 Asynchronous 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 Asynchronous webserver</h2>
<div style="height:50px"></div>
<p>HTTP POST message: %POST_MESSAGE%</p>
<form action="/post" method="POST" target="self_page">
<input type="text" name="message">
<br><br>
<input type="submit" value="Send" onclick="submitData()">
</form>
</center>
<iframe style="display:none" name="self_page"></iframe>
</body>
</html> )rawliteral";
void notFound(AsyncWebServerRequest *request)
{
request->send(404, "text/plain", "The page not found");
}
String processor(const String& var)
{
if(var == "POST_MESSAGE")
{
return postMessage;
}
return String();
}
void setup()
{
Serial.begin(115200);
WiFi.begin(ssid, password);
Serial.print("Connect to the WiFi network.");
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println();
Serial.println("Connected!");
Serial.print("Ip address: ");
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() {}
The html code is placed in the “index_html[]” constant character array. The “PROGMEM” keyword is a variable modifier, it tells the compiler to save the contents of the variable to flash memory.
const char index_html[] PROGMEM = R"rawliteral(....)rawliteral";
“R” means treat everything between the delimiters as a raw string. So everything is as described between “rawliteral( and …. )rawliteral”. In the example, we use the “rawliteral” delimiter, but you can freely choose anything instead.
If something is typed in the address bar of the browser, the “notFound()” function handles the error. It sends the 404 HTML response code and displays the specified content, in this case it is “text/plain” text “The page not found”.
void notFound(AsyncWebServerRequest *request)
{
request->send(404, "text/plain", "The page not found");
}
We placed a placeholder in the html code. Placeholders are delimited by % symbols, in our example it is (%POST_MESSAGE%). When the website is loaded, the “processor()” function runs and wherever it finds the placeholder in the code, it replaces it with the appropriate String.
In this example, we modify the content of a paragraph, passing the value of the global variable “postMessage” to the %POST_MESSAGE% placeholder.
String postMessage = "";
<html>...
<p>HTTP POST message: %POST_MESSAGE%</p>
...</html>
String processor(const String& var)
{
if(var == "POST_MESSAGE")
{
return postMessage;
}
return String();
}
In our code, the first server.on() function serves to display the home page. The first parameter, the path, only contains the “/” character, so the ESP32 monitors requests to the IP address.
The send_P() function sends the 200 HTTP response code as a response, defines the content type, in this case “text/html”. The location of the html code to be displayed must be specified in the third parameter, in our example the string stored in the index_html array. Since we placed a placeholder in the html code, the processor function that handles it must be defined, this is the processor() function given in the fourth parameter.
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
{
request->send_P(200, "text/html", index_html, processor);
});
In the example code, the second server.on() function listens to the “/post” path and accepts HTTP_POST requests. Within the function, we check whether the given parameter contains data. If yes, we extract the content of the parameter for further processing.
In this case, this “PARAM_MESSAGE” carries the content of the text input field marked with the name “message”, the value of which is transferred to the global variable “postMessage”. Finally, it sends a 200 HTTP response code.
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);
});
The server.onNotFound(notFound) function monitors requests that are directed to non-existent pages, for which we add the “notFound()” function as a parameter, which handles these errors.
server.onNotFound(notFound);
Finally, we start the AsyncWebServer with “server.begin();” by calling a function.
server.begin();
Let’s upload the code to the ESP32 and see the output in the web browser.
ESP32 Development Board 2.4 GHz Dual Core WLAN WiFi + Bluetooth
advertising – amazon.com
Website with password protected settings page
In the third example, we create a website consisting of a main page and a settings page.
At the first boot, the ESP32 tries to connect to the wifi network, but it does not have the necessary credentials to connect to the wifi. It will automatically switch to AP mode after 15 seconds of unsuccessful attempts. In this case, you need to connect to the ESP32 wifi access point.
Default identifiers required for connection:
SSID: Esp_AP
Password: 12345678
Important! The Password must be at least 8 characters long, otherwise it will not be taken into account by the server.
If you have successfully connected to the ESP32 access point, open the IP address ‘ 192.168.7.22 ‘ in the web browser.
On the main page, under the title line, a text field displays the state of the LED, which can be turned on or off with the push button located below it. The led is connected to the GPIO2 pin of the ESP32.
Further down we find another button, by clicking on it we can go to the settings page.
The settings page is password protected, the credentials required for entry are:
default username: admin
default password: admin .
There are two forms on the settings page. In the first form, you can set/modify the login IDs required for the Wi-Fi connection and the access point (AP). By clicking the save button, the data is stored in a non-volatile memory area, SPIFFS (SPI Flash File Storage).
The ESP32 will then reboot.
In the second form, you can change the login credentials of the settings page. These identifiers are also stored in a non-volatile memory. ESP32 will not restart after saving.
Below the two forms we find two more buttons, I don’t think they need an explanation.
This example code can be a good starting point for projects where it is necessary to change the settings.
#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 ="The LED is off!";
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>Home page</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 Asynchronous web server</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';">Settings</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>Settings</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>Settings</h2>
<div style="height:50px"></div>
<fieldset style="width:300px">
<legend>Network settings:</legend>
<br><br>
<form action="/network" target="self_page">
AP SSID:<br>
<input type="text" value='%inputApSsid%' name="inputApSsid" size="20">
<br><br>
AP Password:<br>
<input type="text" value='%inputApPassword%' name="inputApPassword" size="20">
<br>( at least 8 characters! )
<br><br>
Wifi SSID:<br>
<input type="text" value='%inputWifiSsid%' name="inputWifiSsid" size="20">
<br><br>
Wifi Password:<br>
<input type="text" value='%inputWifiPassword%' name="inputWifiPassword" size="20">
<br><br>
<input type="submit" value="Save" 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">
User name:<br>
<input type="text" value='%inputHttpUsername%' name="inputHttpUsername" size="20">
<br><br>
Password:<br>
<input type="text" value='%inputHttpPassword%' name="inputHttpPassword" size="20">
<br><br><br>
<input type="submit" value="Save" onclick="submitData()">
</form>
<br><br>
</fieldset>
<div style="height:20px"></div>
<fieldset style="width:300px">
<legend>To home page</legend>
<br><br>
<button onclick="window.location.href='/';">Home page</button>
<br><br>
</fieldset>
<div style="height:20px"></div>
<fieldset style="width:300px">
<legend>Logout</legend>
<br><br>
<button onclick="logoutButton()">Logout</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 =" The LED is off!";
}
else if(ledValue == "false")
{
ledValue = "true";
digitalWrite(ledPin, HIGH);
ledState ="The LED is on!";
}
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", "The page not found");
}
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 address: ");
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("Connection to WiFi network.");
int i = 0;
while (true)
{
if(WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
i++;
if(i > 30)
{
wifiIsConnected = false;
Serial.println();
Serial.println("It cannot connect to the WiFi network.");
break;
}
}
else
{
wifiIsConnected = true;
Serial.println();
Serial.println("Connected!");
Serial.print("Ip address: ");
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(){}
Meross Dimmable Smart Table Lamp
WiFi Table Lamp, Support HomeKit (iOS13+), Alexa, Google Assistant and SmartThings, Tunable White and Multi-Color, Touch Control, Voice and APP Control, Schedule and Timer
advertising – amazon.com