ESP32 Arduino async HTTP server: Serving a HTML page from PROGMEM

The objective of this post is to explain how to serve a simple HTML page from the ESP32, using the Arduino core. The tests of this ESP32 tutorial were performed using a DFRobot’s ESP-WROOM-32 device integrated in a ESP32 FireBeetle board.

 

Introduction

The objective of this post is to explain how to serve a simple HTML page from the ESP32, using the Arduino core. You can check on this previous post how to set the libraries needed for us to create a HTTP server.

Our web page will be a simple HTML form that we will serve from the asynchronous HTTP server. Please note that we will not create any CSS for styling our form, so it will be a pretty raw one, just to illustrate how to serve the HTML.

The tests of this ESP32 tutorial were performed using a DFRobot’s ESP-WROOM-32 device integrated in a ESP32 FireBeetle board.

 

The HTML code

We will start by designing the HTML code independently from the Arduino code. Then, we will use some tools to convert it to a compact string format we can use on the ESP32.

Explaining in detail the HTML code and how everything works is outside the scope of this post. So, basically, we are going to create a very simple form with two inputs and a submit button, as can be seen below.

<form onSubmit = "event.preventDefault()">
	<label class="label">Network Name</label>
	<input type = "text" name = "ssid"/>
	<br/>
	<label>Password</label>
	<input type = "text" name = "pass"/>
	<br/>
	<input type="submit" value="Submit">
</form>

We are labeling the inputs as “Network Name” and “Password”, so this could be a possible implementation of a form to input a network’s WiFi credentials.

Please note that in this tutorial we are only going to cover the part of displaying the HTML content, so we are not going to develop the endpoint to receive the data from the form. Thus, we do the event.preventDefault() call on the submit action so nothing actually happens when we click the button.

Since we are developing for a microcontroller, which is a resource constrained device, we can compress the previous HTML for a single line format, thus getting rid of all the unnecessary tabs and new lines.

Although this will make the code hard to read for a person, it makes no difference to the client which will interpret it and allows us to save some space on the ESP32.

To perform this operation, we can use this online tool, which will minify our HTML code. To prevent problems, when working with complex HTML pages, my recommendation is that you always try the code locally after compressing it, to confirm no problem has occurred in the process.

To do so, you can simply paste the minified code in a text file, save with with a .html extension and open it with a web browser. If everything is ok, then you can use it on the ESP32.

You can check below at figure 1 the result of minifying the code with the online tool mentioned.

ESP32 Arduino HTML code minifying.png

Figure 1 – Minification of the HTML code.

Nonetheless, we can not directly use this code as a string in Arduino because of the quotes it has. Thus, we will need to escape them.

Since HTML code typically has lots of quotes, the easiest way to get the escaped version is by using a tool such as this online one.

So, to escape the code, simply copy the previously minified version of the HTML and paste it on the tool, unchecking the “split output into multiple lines” option before starting the operation. You can check the expected result in figure 2.

ESP32 Arduino escaping minified HTML code.png

Figure 2 – Escaping the minified HTML code in an online tool.

Now that we have the HTML code as a single line escaped C string, we can move on to the Arduino code.


The code

The code for this tutorial is similar to this previous one, with the exception that we are now going to serve a more complex HTML code, which we will store in the PROGMEM.

Since the HTML content is static, putting it on the PROGMEM (Program Memory) avoids consuming RAM, leaving this resource available for other uses. This is useful specially when our program serves a lot of static pages, thus consuming a lot of RAM if not placed in the PROGMEM.

To start the code, we will write all the needed library includes. We will also declare as global variables the credentials needed for connecting to the WiFi network.

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

const char* ssid = "yourNetworkSSID";
const char* password =  "yourNetworkPass";

Next we will declare an object of class AsyncWebServer, which we will use to setup the server. Note that as input of the constructor we need to pass the port where the server will be listening for requests.

AsyncWebServer server(80);

To finalize the global declarations, we will create a variable in the PROGMEM that will contain the HTML code we have prepared.

const char HTML[] PROGMEM = "<form onSubmit=\"event.preventDefault()\"><label class=\"label\">Network Name</label><input type=\"text\" name=\"ssid\"/><br/><label>Password</label><input type=\"text\" name=\"pass\"/><br/><input type=\"submit\" value=\"Submit\"></form>";

Moving on to the setup function, we will start by opening a serial connection and then connect the ESP32 to the WiFi network. You can check in more detail here how to connect the ESP32 to a WiFi network.

Serial.begin(115200);

WiFi.begin(ssid, password);

while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
}

Serial.println(WiFi.localIP());

After the connection to the WiFi network, we will take care of the webserver setup. So, we will need to bind a server route to a handling function that will return the HTML we defined when a client makes a request. We will use the “/html” route.

If you need a more detailed explanation on the functions signatures and how the binding works, please consult this previous post.

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

Note that we are specifying the return content-type as “text/html” so the client (in our case it will be a web browser) knows that it must interpret the received content as such.

Also, take in consideration that the last parameter of the send function is the content that we are actually returning to the client and, in our case, it is the HTML variable we defined early.

Now that we have handled the configurations of the server, we simply need to call the begin function on the server object, so it starts listening to incoming HTTP requests. Since the server works asynchronously, we don’t need to perform any call on the main loop, which may be left empty.

You can check the full source code below.

#include <WiFi.h>
#include <FS.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
 
const char* ssid = "yourNetworkSSID";
const char* password =  "yourNetworkPassword";
 
AsyncWebServer server(80);

const char HTML[] PROGMEM = "<form onSubmit=\"event.preventDefault()\"><label class=\"label\">Network Name</label><input type=\"text\" name=\"ssid\"/><br/><label>Password</label><input type=\"text\" name=\"pass\"/><br/><input type=\"submit\" value=\"Submit\"></form>";

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("/html", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(200, "text/html", HTML);
  });
 
  server.begin();
}
 
void loop(){
}


Testing the code

To test the code, you simply need to compile it and upload it to your device using the Arduino IDE. Once it is ready, just open the serial monitor and wait for the connection to the WiFi network.

When it finishes, the local IP of the ESP32 on your Wireless network should get printed to the serial monitor. Copy that IP, since we will need it to connect to the server.

Then, open a web browser of you choice and write the following URL on the address bar, changing {yourEspIp} by the IP you have just obtained.

http://{yourEspIp}/html

You should get an output similar to figure 3, which shows the HTML page being served and rendered by the browser.

ESP32 Arduino Async HTTP server web form.png

Figure 3 – HTML form rendered in the browser.


Related posts

Advertisements
This entry was posted in ESP32 and tagged , , , , , , . Bookmark the permalink.

14 Responses to ESP32 Arduino async HTTP server: Serving a HTML page from PROGMEM

  1. yuulye says:

    Is this microcontroller? I always wanted to learn robot

    Liked by 1 person

  2. Pingback: ESP32 Arduino HTTP server: Getting query parameters | techtutorialsx

  3. Pingback: ESP32 Arduino HTTP server: Running multiple server instances | techtutorialsx

  4. Pingback: ESP8266 Arduino: Asynchronous HTTP web server | techtutorialsx

  5. Pingback: ESP32 Arduino HTTP Server: Serving HTML and JavaScript | techtutorialsx

  6. Pingback: ESP32 Arduino: HTTP server over soft AP | techtutorialsx

  7. Pingback: ESP32 Arduino HTTP server: external and internal redirects | techtutorialsx

  8. Pingback: ESP32 Arduino HTTP server: route not found handling | techtutorialsx

  9. Could you tell me how to server multiple web pages this way? For example, assuming I have 3 web pages (index, page1, page2) and index is loaded when server starts. How to serve page1 and page2 when user clients on the link on the index page that link to the corresponding page?

    Liked by 1 person

    • Sorry. This is to correct some typos in my question above.

      Could you tell me how to serve multiple web pages this way? For example, assuming I have 3 web pages (index, page1, page2) and index is loaded when server starts. How to serve page1 and page2 when user clicks on the link on the index page that links to the corresponding page?

      Liked by 1 person

    • antepher says:

      Hi! The simplest way is having multiple routes in your server serving different HTML pages.

      You can declare more routes using the on method.

      As long as the links in your index point to your other webpages, the client (if it is a browser) should automatically request the other pages from the server when the user clicks the link.

      Since you are going to be handling navigation inside your own server, you can use relative links (check the difference in the article below):
      https://www.coffeecup.com/help/articles/absolute-vs-relative-pathslinks/

      Note however that what you mention about loading the index when the server starts is not how it will work.

      Basically, when the server starts, all the 3 routes will be available and if a client makes a request to them, the HTML content will be returned (unless you implement some kind of logic in your handling function).

      So what typically happens is that the client know the address of the index page of a website a puts it on the URL bar of a browser to access it, and then he navigates inside that website by clicking in links (as you mentioned).

      Nonetheless, the index page is not loaded when the server starts but rather is typically the first page someone accesses.

      Hope this helped clarifying 🙂

      Like

  10. Pingback: ESP32 Arduino async server: Controlling HTTP methods allowed | techtutorialsx

  11. Pingback: ESP32 Arduino web server: getting client IP | techtutorialsx

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 )

w

Connecting to %s