In this esp32 tutorial we will check how to use the template processing features of the async HTTP webserver libraries for the ESP32. You can follow this previous tutorial to check how to configure the HTTP async web server libraries for the Arduino core, running on the ESP32.
In some use cases, we may want to return to the client, for example, a HTML page that contains values that are only known at run time. One such example could be retrieving a page that shows the current temperature and humidity in a room.
One quick way to solve the problem could be declaring the HTML pieces that compose the final HTML page and then concatenate the strings with the temperature and humidity obtained at runtime.
Nonetheless, the code would be harder to understand and maintain, and if we wanted to add more sensor variables it would start to become unpractical.
So, a tool that many web server frameworks offer to tackle this problem is a template engine that allows to specify the base HTML and then placeholders for text replacement, conditional evaluation and looping expressions, amongst many other features.
These allow to combine the base template with the run time data to produce the final HTML document, making programming much easier and maintainable.
The HTTP ESP32 async web server offers a very simple template processing engine that allows to replace placeholders with values determined at runtime. These placeholders are included in the HTML code between percentage signs “%”.
The tests were performed using a DFRobot’s ESP32 module integrated in a ESP32 development board.
If you prefer a video version of this tutorial, please check my YouTube channel below:
As usual, we start by including the libraries needed. We will need the WiFi.h library, so the ESP32 can connect to a Wireless network, and the ESPAsyncWebServer.h, so we can setup the async HTTP web server.
#include "WiFi.h" #include "ESPAsyncWebServer.h"
Naturally, we will also need the credentials of the WiFi network, which we will store in two global variables.
const char* ssid = "yourNetworkName; const char* password = "yourNetworkPassword";
We will also declare our HTML code as a string. We will make it constant so it is stored in FLASH memory [1]. Remember that we should specify our placeholder between percentage signs “%“.
Our HTML code will be really simple and it will basically consist on a paragraph. The content of the paragraph is what will be replaced at runtime.
const char * html = "<p>%PLACEHOLDER%</p>";
Finally, we need an object of class AsyncWebServer, which is used to configure the routes of the server. Remember from previous posts that the constructor for this class receives the number of the port where the server will be listening. We will use port 80 since it is the default HTTP port.
AsyncWebServer server(80);
Next, we will move on to the setup function. As usual, we first take care of opening a serial port to output the results of our program, and also to connect the ESP32 to a WiFi network, using the previously declared credentials.
Note that we will print the local IP assigned to the ESP32 once the connection is established, so we know which IP address to use when making the request to the server.
Serial.begin(115200); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(1000); Serial.println("Connecting to WiFi.."); } Serial.println(WiFi.localIP());
Now we will configure the routes where our server will be listening. For this particular example, we will only need a single route. We will call it “/hello” and it will only listen to HTTP GET requests.
server.on("/hello", HTTP_GET, [](AsyncWebServerRequest *request){ // Route handling function });
In our handling function, we will return the answer to the client using the send_P method of the request object. As explained in the library documentation, this function is used to send large pages from PROGMEM.
Note: Although it is not clear in the documentation if this send_P method was designed to work with constant data stored in FLASH memory on the ESP32, it seems to be the only option that allows to specify the return status code, the content-type, the content and the template processor. You can check the list of variants of the send method here. For the use cases I’ve tested, it seems to be working fine.
So as first input of the send_P method we pass the status code to return, as second the content-type, as third the actual content and as fourth and final, the template processor function.
We will return a OK status code (200), the content-type will be “text/html” and the content will be the const string we previously declared. As template processor we will use a function called processor which we will define below.
server.on("/hello", HTTP_GET, [](AsyncWebServerRequest *request){ request->send_P(200, "text/html", html, processor); });
To finalize the setup, we need to call the begin method on our AsyncWebServer object, so the server begins listening to incoming HTTP requests.
server.begin();
Now we will define the template processing function, which needs to follow the signature defined here. This template processing function is a user defined function that will receive the template value and should specify which value to be used instead of the template.
String processor(const String& var) { // processor implementation }
Note that the template value is passed to the function without the percentage signs enclosing it. In our case, since we defined %PLACEHOLDER% as our placeholder in the HTML string, then this processor function will receive as input the value PLACEHOLDER.
We will first print the received value to the serial port, to confirm that it matches what we are expecting, and then we will define what to return to be used in place of the placeholder.
Serial.println(var);
Then, we check if the received value matches the placeholder we are expecting, and in case it is, we return the string to be used instead of it.
if(var == "PLACEHOLDER")return "Hello world from placeholder!";
The full placeholder function is shown below. Note that we return an empty string in case our condition is not met, just as a safeguard. Nonetheless, this may be more useful in more complex scenarios with multiple placeholders.
String processor(const String& var) { Serial.println(var); if(var == "PLACEHOLDER")return "Hello world from placeholder!"; return String(); }
The final source code can be seen below. Note that the Arduino main loop can be left empty since the asynchronous HTTP web server doesn’t require any periodic call of a function to handle the clients, like other older frameworks do.
#include "WiFi.h" #include "ESPAsyncWebServer.h" const char* ssid = "yourNetworkName"; const char* password = "yourNetworkPassword"; const char * html = "<p>%PLACEHOLDER%</p>"; AsyncWebServer server(80); String processor(const String& var) { Serial.println(var); if(var == "PLACEHOLDER") return "Hello world from placeholder!"; return String(); } void setup(){ Serial.begin(115200); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(1000); Serial.println("Connecting to WiFi.."); } Serial.println(WiFi.localIP()); server.on("/hello", HTTP_GET, [](AsyncWebServerRequest *request){ request->send_P(200, "text/html", html, processor); }); server.begin(); } void loop(){}
To test the code, simply compile it and upload it to your device. When the procedure finishes, open the Arduino IDE serial monitor. As soon as the ESP32 finishes the connection to the WiFi network, an IP address should get printed to the serial port. Copy that IP, since we will need it to reach the server.
Then, open a web browser of your choice and type the following in the address bar, changing #yourIP# by the IP you have just copied from the serial monitor.
http://#yourIP#/hello
You should get the result shown in figure 1. As can be seen, the placeholder was substituted by the string we have defined in the processor function.
Figure 1 – Resulting HTML, after the template substitution.
If you go back to the Arduino IDE serial monitor, the value received by the template processor function should be printed, as shown in figure 2.
Figure 2 – Value received by the placeholder processing function.
DFRobot supply lots of esp32 arduino tutorials and esp32 projects for makers to learn.