ESP32 Arduino async HTTP server: Serving a HTML page from FLASH memory

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 FLASH memory.

Since the HTML content is static, putting it on the FLASH 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 FLASH.

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.

Update: As explained in this blog post, in the ESP32 constant data is automatically stored in FLASH memory and can be accessed directly from FLASH memory without previously copying it to RAM. So, there is no need to use the PROGMEM keyword below and it is only defined due to compatibility with other platforms. You can check the PROGMEM define on this file. This forum post also contains more information about this subject. Thanks to MOAM Industries and Russell P Hintze for pointing this. The PROGMEM keyword was removed from the final code below, but you can test with it to confirm that it is indeed defined and doesn’t cause any compilation problem.

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[] = "<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

 

22 thoughts on “ESP32 Arduino async HTTP server: Serving a HTML page from FLASH memory”

  1. Pingback: ESP32 Async HTTP web server: websockets introduction | techtutorialsx

  2. Pingback: ESP32 Async HTTP web server: websockets introduction | techtutorialsx

  3. Pingback: ESP32 Arduino web server: Receiving data from JavaScript websocket client – techtutorialsx

  4. Pingback: ESP32 Arduino web server: Receiving data from JavaScript websocket client – techtutorialsx

Leave a Reply

Discover more from techtutorialsx

Subscribe now to keep reading and get access to the full archive.

Continue reading