ESP32 Arduino HTTP server: Template processing

In this tutorial we will check how to use the template processing features of the async HTTP webserver libraries for the ESP32. The tests were performed using a DFRobot’s ESP32 module integrated in a ESP32 development board.

Introduction

In this 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:

The setup code

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();

The processor function

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 code

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(){}

Testing the code

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.

ESP32 Arduino Async HTTP web server template processing.png

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.

ESP32 Arduino HTTP web server template processor value

Figure 2 – Value received by the placeholder processing function.


References

[1] https://esp32.com/viewtopic.php?t=1995https://esp32.com/viewtopic.php?t=1995

[2] https://github.com/me-no-dev/ESPAsyncWebServer#template-processing

Advertisements

12 Replies to “ESP32 Arduino HTTP server: Template processing”

  1. So how would I do this with a file supplied by SPIFFS? I’m using an HTML page in the SPIFFS file system which is served to the client on request, but I need to set/modify some values in the HTML before it’s sent?

    Liked by 1 person

        1. You’re welcome 🙂

          May I ask if you are serving the jQuery from the ESP32 or fetching it from an external source?

          I’ve recently done a proof of concept serving jQuery from the ESP32 file system, for those cases where we need to serve a webpage but we don’t have Internet access (ex: ESP32 working as soft AP).

          Also, if you need to update a webpage regularly, it can be a good option to use websockets instead of polling the server with ajax.

          Best regards,
          Nuno Santos

          Liked by 1 person

          1. I’m serving the JS script via the ESP32’s file system. 🙂 It works fine for the test page I’ve got set up (all it does it turn some lights on/off and set a PWM output) but instead of loading the page again with every button press, I just update the DOM with JS and data returned from the ESP32 via AJAX.

            For what I’m developing the ESP32 for, though, I will be moving over to websockets fairly soon. I’m going to want to expose the ESP32’s serial console over the web server, so push/pull is important.

            Liked by 1 person

            1. Very nice project 🙂

              It seems a good approach to only update what you need in the web page in that case.

              For a functionality such as exposing the serial console you are absolutely right, the best approach should be using something as websockets or, eventually, even raw sockets.

              It probably depends on the client side interface and what additional features you want to offer.

              Wish you the best luck with your project 🙂

              Best regards,
              Nuno Santos

              Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s