ESP32 HTTP web server: Serving external JavaScript file

In this tutorial we will learn how to serve an external JavaScript file to be referenced by a HTML file, using the ESP32. The ESP32 will be running the Arduino core and the HTTP async web server libraries. The tests were performed using a DFRobot’s ESP32 module integrated in a ESP32 development board.

Introduction

In this tutorial we will learn how to serve an external JavaScript file to be referenced by a HTML file, using the ESP32. The ESP32 will be running the Arduino core and the HTTP async web server libraries.

Note that this post will be very similar to the previous one where we explained how to serve an external CSS file, to be used in a HTML page, both served by the ESP32. So, the same way it makes sense to separate CSS (appearance) and HTML (content), it also makes sense to separate JavaScript (behavior).

Furthermore, in more complex solutions, the same JavaScript functions may need to be reused across multiple HTML pages, which means that by including the same JavaScript file we avoid code duplication.

Since this tutorial is focused on the end-to-end system, both the JavaScript and HTML codes will be very simple.

Additionally, in order to simplify the process of serving the files, we will leverage the ESP32 SPIFFS file system. So, instead of having to define big hardcoded strings in the Arduino code with the JavaScript and HTML code, we will serve the content from the file system.

In order to upload the JavaScript and HTML files to the ESP32 SPIFFS file system we will use this Arduino IDE plugin. This tutorial explains how to get started with it.

In short, after installing the plugin, we need to go to the Arduino sketch folder where we are developing the code and create a folder named “data“. There, we should place both the HTML and JavaScript files (we will cover their content in the sections below). This is illustrated in figure 1.

Arduino Sketch folder with the HTML and JavaScript files to be uploaded to the ESP32

Figure 1 – Data folder with the files to upload to the ESP32.

After that, we simply need to go to the Arduino IDE and, under the Tools menu, click the “ESP32 Sketch Data Upload” option. Note that the files will be uploaded to the root of the ESP32 file system and will have the same name they had on the computer from where they were uploaded.

In my case, as shown before in figure 1, the files were named “test.html” and “test.js”. So, in the ESP32, their paths will be “/test.html” and “/test.js“, respectively.

The tests from this tutorial were performed using a DFRobot’s ESP32 module integrated in a ESP32 development board.

The JavaScript code

Since the objective of this tutorial is to explain how to serve external JavaScript files from the ESP32 and not to focus on JavaScript coding, our script will be really simple. So we will just define a function that will open an alert window.

We will call our function openAlert and in its implementation we will simply call the alert method on the window object, passing as input a string with the content to be displayed. In our case, it will be a “Hello World” message, just for demonstration purposes.

function openAlert() {
	window.alert("Hello World");
}

Note that this function should be available to be called in a HTML file that includes this JavaScript file.

The HTML code

We will also keep the HTML code very simple, and it will basically consist on a button that will invoke the previously defined JavaScript function when clicked.

So, in order to include the JavaScript file, we need to use a script tag and set its src attribute to point to our JavaScript file. Since the file will be retrieved from the same server that is serving the HTML code, then we should use a relative path, which will be the name of the JavaScript file.

Note that we will include this external JavaScript file inside the head section of the HTML document.

Next, in the body section, we will have a button element that, when clicked, will invoke the openAlert function defined on the JavaScript file. The full HTML code can be seen below.

<!DOCTYPE html>
<html>

    <head>
        <script src="test.js"></script>
    </head>

    <body>
        <button onclick="openAlert()">Click me</button>
    </body>

</html>

The Arduino code

We will start by importing all the libraries we need to develop our web server application. The first one is the WiFi.h, so we can connect the ESP32 to a WiFi network. The second one is the SPIFFS.h, which is needed in order to interact with the SPIFFS file system. Finally, we need the ESPAsyncWebServer.h library, which exposes all the functionality needed to configure the HTTP web server on the ESP32.

In order to be able to connect the ESP32 to the WiFi network, we will need to provide the credentials of that network, more specifically the network name (SSID) and the network password. We will store them in two global variables, so we can easily change them.

Finally, still as a global variable, we need to declare an object of class AsyncWebServer, since this class exposes the methods needed to setup the HTTP web server on the ESP32. As covered in previous tutorials, the constructor of this class receives as input the number of the port where the ESP32 will be listening to incoming requests.

#include "WiFi.h"
#include "SPIFFS.h"
#include "ESPAsyncWebServer.h"

const char* ssid = "yourNetworkName";
const char* password =  "yourNetworkPassword";

AsyncWebServer server(80);

After the library includes and global variables declaration, we will move on to the Setup function. As we usually do, we will start by opening a serial connection, to be able to print some content to the serial port.

Followed by that, we will mount the SPIFFS file system, so we can later access the files to be served to the HTTP clients. Note that we are not going to directly interact with the file system since the HTTP web server framework handles that for us, but we need to make sure to mount the file system before the framework starts using it.

After that, we need to connect the ESP32 to the WiFi network. If you need a more detailed tutorial on how to connect the ESP32 to a WiFi network, please check here. After the connection is established, we print the local IP assigned to the ESP32 on the network, so we can reach it later with a HTTP client.

Serial.begin(115200);

if(!SPIFFS.begin()){
     Serial.println("An Error has occurred while mounting SPIFFS");
     return;
}

WiFi.begin(ssid, password);

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

Serial.println(WiFi.localIP());

Now we need to configure the routes of the server. The first one will be responsible for serving the HTML page and, for simplicity, we will call it “/html”.

Since this is a route that the client is going to use to fetch the HTML content, it makes sense to only listen to HTTP GET requests.

The route handing function implementation will be very simple and it basically consists on returning back to the client the HTML file that we have previously uploaded to the ESP32 file system.

To do it, we just need to call the send method on the AsyncWebServerRequest object pointer that is passed to the handling function by the HTTP web server framework. Since we receive a pointer and not the actual object, we need to use the -> operator to call the mentioned method.

As first input, we will pass the SPIFFS object, so the framework is able to interact with the file system. As second input, we need to pass the full path to the file we want to serve (remember from the introductory section that the file will be on the “/test.html” path on the SPIFFS file system).

Finally, as third argument, we pass a string with the content-type, which will be used by the client to know how to interpret the content returned by the server. In our case, since we are serving a HTML file, the content-type is “text/html“.

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

Now, we will need an additional route that will be responsible for serving the JavaScript file. The route name should match the name of the file, accordingly to what we have specified in the HTML code. So, we will call it “/test.js“.

Like the previous one, this route will only listen to HTTP GET requests, since the client will only fetch the JavaScript file and should not be able to perform any other action.

In the handling function implementation we will simply return back to the client the JavaScript file, again using the send method of the AsyncWebServerRequest object pointer.

In this case, the full path of the file is “/test.js” and the content-type is “text/javascript“, since we are serving a file that only contains JavaScript code. This is different from serving a HTML file that contains JavaScript functions defined in a script tag and, if that was the case, then we would use “text/html”.

server.on("/test.js", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/test.js", "text/javascript");
});

After configuring all the routes, we need to call the begin method on the server object, so it starts listening to incoming HTTP requests from clients. The final code can be seen below and already includes the call to this method, and also the empty Arduino loop function, since we don’t have any additional logic to implement.

#include "WiFi.h"
#include "SPIFFS.h"
#include "ESPAsyncWebServer.h"

const char* ssid = "yourNetworkName";
const char* password =  "yourNetworkPassword";

AsyncWebServer server(80);

void setup(){
  Serial.begin(115200);

  if(!SPIFFS.begin()){
     Serial.println("An Error has occurred while mounting SPIFFS");
     return;
  }

  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(SPIFFS, "/test.html", "text/html");
  });

  server.on("/test.js", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/test.js", "text/javascript");
  });

  server.begin();
}

void loop(){}

Testing the code

To test the code, simply compile it and upload it to your device using the Arduino IDE, with support for the ESP32 Arduino core. Once the procedure finishes, open the Arduino IDE serial monitor and copy the IP address that will be printed when the connection to the WiFi network is established.

Then, open a web browser of your choice and type the following on the address bar, changing #yourIp# by the IP you have just copied.

http://#yourIp#/html

You should get an output similar to figure 2. As can be seen by the developer’s console (I’m using Google Chrome on the example below), after accessing the HTML page, an additional request is made to the server in order to fetch the JavaScript file that is needed. The server then returns the file correctly, as can be seen by the 200 HTTP status code of the response, which means success.

Accessing the HTML page served by the ESP32 with the developer's console opened

Figure 2 – HTML page served by the ESP32.

After that, simply click the button of the web page. You should get an output like the one shown below at figure 3, which shows the alert box that was defined in the JavaScript file.

Alert box rendered by JavaScript code after clicking the HTML button

Figure 3 – Alert box rendered after clicking the button.

19 Replies to “ESP32 HTTP web server: Serving external JavaScript file”

  1. Hello there,
    all of your tutorials are marvelous with easy understandable content

    but i need one help i have html,css,javascript file in SPIFFS, i had successfully hosted those file to make webpage on server,
    consider one scenario this webpage have two input files for SSID and PASSWORD if we give parameters and press SUBMIT i want to receive those parameters on ESP32 hardware so i can change some configuration.

    can you guide me for this,
    what is the best way to send and receive data to and from webpage ?

    thanks,

    1. Hi!

      Thank you very much for the feedback, I’m very happy to know the content is easy to understand 🙂

      Well, there are multiple ways of achieving that. One suggestion can be the following:

      In the ESP32 side, you will need to have a server route to receive those credentials. To make things clean, you should submit them in a POST request, so your route should be listening to a post request.

      On the HTML page side, I would probably bind a JavaScript function to the submit event of the button.

      Then, I would get the data from the form, put it in JSON format and submit a POST request to the server.

      Note that if you use JSON format, you will have to parse this on the ESP32 side. Tutorial here:
      https://techtutorialsx.com/2017/04/26/esp32-parsing-json/

      Once you have the data on the ESP32, it’s just a matter of changing the connection to the new network, using the received credentials.

      You need to take a closer look at the transitory behavior when changing the networks.

      I’m not sure if you need to first explicitly disconnect from the current network and connect to the new one, or if you just connect to the new one and the rest is handled under the hood. I did not make tests on this behavior yet.

      Also, I’m not sure if the ESP32 server will keep working transparently when you switch networks or if you have to somehow explicitly restart it.

      Nonetheless, keep in mind that in a end to end perspective, you will still have a problem with the first WiFi connection you will do.

      In order to server your HTML page the first time, you will need to either connect to a “default” wifi network that you know will exist, or you will have to setup the ESP32 to act as a soft AP (thus hosting its own network to which other devices can connect).

      By doing this, you will have other use cases to worry about. Depending if you want to do this by yourself or not, you can also take a look at some implementations of WiFi managers for the ESP32 that basically implement that functionality, you just need to import a lib:
      https://github.com/zhouhan0126/WIFIMANAGER-ESP32

      If you want to do it by yourself, you can still take a look at their code, which can be a good starting point 🙂

      Hope this helps!

      Best regards,
      Nuno Santos

  2. Hello there,
    all of your tutorials are marvelous with easy understandable content
    but i need one help i have html,css,javascript file in SPIFFS, i had successfully hosted those file to make webpage on server,
    consider one scenario this webpage have two input files for SSID and PASSWORD if we give parameters and press SUBMIT i want to receive those parameters on ESP32 hardware so i can change some configuration.
    can you guide me for this,
    what is the best way to send and receive data to and from webpage ?
    thanks,

    1. Hi!
      Thank you very much for the feedback, I’m very happy to know the content is easy to understand 🙂
      Well, there are multiple ways of achieving that. One suggestion can be the following:
      In the ESP32 side, you will need to have a server route to receive those credentials. To make things clean, you should submit them in a POST request, so your route should be listening to a post request.
      On the HTML page side, I would probably bind a JavaScript function to the submit event of the button.
      Then, I would get the data from the form, put it in JSON format and submit a POST request to the server.
      Note that if you use JSON format, you will have to parse this on the ESP32 side. Tutorial here:
      https://techtutorialsx.com/2017/04/26/esp32-parsing-json/
      Once you have the data on the ESP32, it’s just a matter of changing the connection to the new network, using the received credentials.
      You need to take a closer look at the transitory behavior when changing the networks.
      I’m not sure if you need to first explicitly disconnect from the current network and connect to the new one, or if you just connect to the new one and the rest is handled under the hood. I did not make tests on this behavior yet.
      Also, I’m not sure if the ESP32 server will keep working transparently when you switch networks or if you have to somehow explicitly restart it.
      Nonetheless, keep in mind that in a end to end perspective, you will still have a problem with the first WiFi connection you will do.
      In order to server your HTML page the first time, you will need to either connect to a “default” wifi network that you know will exist, or you will have to setup the ESP32 to act as a soft AP (thus hosting its own network to which other devices can connect).
      By doing this, you will have other use cases to worry about. Depending if you want to do this by yourself or not, you can also take a look at some implementations of WiFi managers for the ESP32 that basically implement that functionality, you just need to import a lib:
      https://github.com/zhouhan0126/WIFIMANAGER-ESP32
      If you want to do it by yourself, you can still take a look at their code, which can be a good starting point 🙂
      Hope this helps!
      Best regards,
      Nuno Santos

  3. Hi!

    Like futechiot said, all your tutorials are really good and easy to understand, I’m not an english speaker, but spanish speaker, and even with my english level, it is really easy to learn with your work, so thank you very much!

    If you don’t mind, I have a question about the ESP Async Web Server, do you know if it’s possible to use jquery ajax method with ESP Async Web Server to update values from analog/digital readings?

    Sorry if my english is bad, and thank you again.

    Regards.

    Ivan Pelcastre.

    1. Hi!

      Thank you very much for the feedback, I’m very happy to know that you are finding the tutorials easy to understand 🙂

      I’m also not a native English speaker myself, I’m Portuguese. And don’t worry, your English is perfectly understandable 🙂

      Yes you should be able to use Ajax to send a request periodically to the ESP32 and get new values.

      Are you thinking about periodically updating the values (which means polling the ESP32, which is not very efficient) or updating them after an action (ex: a button click)?

      In either cases, here is an example on how to serve jQuery from the ESP32 async web server:
      https://techtutorialsx.com/2018/09/11/esp32-arduino-web-server-serving-jquery/

      If you want something “real time” without polling, the best approach is to resort to websockets:
      https://techtutorialsx.com/2018/09/11/esp32-arduino-web-server-sending-data-to-javascript-client-via-websocket/

      Hope this helps 🙂

      Best regards,
      Nuno Santos

      1. Hi!

        Thank you for your help, i’m gonna try with Websockets, it seems, like you said, the best aproach for what I’m trying to do. But I have another question, reading the post of sending data to javascript client via websocket, what I have to do to send, for example, if i have two data (Data1 and Data2), and I want display in the HTML on hypotheticals elements with ID Display1 and Display2?

        Sorry beforehand if my english is a little hard to undestand and thanks again.

        Regards.

        Ivan Pelcastre.

        1. Hi,

          You’we welcome 🙂

          What you need to send is really a specific decision of your application.

          With websockets, you can send data in the format you want, so it’s up for your application to decide how to format the messages to suit your needs.

          You can, for example, send a json message that has an attribute called “display” that specifies which display you want to use, and another attribute called “data” that has the actual data to display.

          Then, your “message received” function simply looks into that json and decides what to do.

          This is one of the many possible approaches that you can follow 🙂

          As a basic example on how to send data to a JavaScript client, you can check this post:
          https://techtutorialsx.com/2018/09/11/esp32-arduino-web-server-sending-data-to-javascript-client-via-websocket/

          Hope it helps 🙂

          Best regards,
          Nuno Santos

  4. Hi!
    Like futechiot said, all your tutorials are really good and easy to understand, I’m not an english speaker, but spanish speaker, and even with my english level, it is really easy to learn with your work, so thank you very much!
    If you don’t mind, I have a question about the ESP Async Web Server, do you know if it’s possible to use jquery ajax method with ESP Async Web Server to update values from analog/digital readings?
    Sorry if my english is bad, and thank you again.
    Regards.
    Ivan Pelcastre.

    1. Hi!
      Thank you very much for the feedback, I’m very happy to know that you are finding the tutorials easy to understand 🙂
      I’m also not a native English speaker myself, I’m Portuguese. And don’t worry, your English is perfectly understandable 🙂
      Yes you should be able to use Ajax to send a request periodically to the ESP32 and get new values.
      Are you thinking about periodically updating the values (which means polling the ESP32, which is not very efficient) or updating them after an action (ex: a button click)?
      In either cases, here is an example on how to serve jQuery from the ESP32 async web server:
      https://techtutorialsx.com/2018/09/11/esp32-arduino-web-server-serving-jquery/
      If you want something “real time” without polling, the best approach is to resort to websockets:
      https://techtutorialsx.com/2018/09/11/esp32-arduino-web-server-sending-data-to-javascript-client-via-websocket/
      Hope this helps 🙂
      Best regards,
      Nuno Santos

      1. Hi!
        Thank you for your help, i’m gonna try with Websockets, it seems, like you said, the best aproach for what I’m trying to do. But I have another question, reading the post of sending data to javascript client via websocket, what I have to do to send, for example, if i have two data (Data1 and Data2), and I want display in the HTML on hypotheticals elements with ID Display1 and Display2?
        Sorry beforehand if my english is a little hard to undestand and thanks again.
        Regards.
        Ivan Pelcastre.

        1. Hi,
          You’we welcome 🙂
          What you need to send is really a specific decision of your application.
          With websockets, you can send data in the format you want, so it’s up for your application to decide how to format the messages to suit your needs.
          You can, for example, send a json message that has an attribute called “display” that specifies which display you want to use, and another attribute called “data” that has the actual data to display.
          Then, your “message received” function simply looks into that json and decides what to do.
          This is one of the many possible approaches that you can follow 🙂
          As a basic example on how to send data to a JavaScript client, you can check this post:
          https://techtutorialsx.com/2018/09/11/esp32-arduino-web-server-sending-data-to-javascript-client-via-websocket/
          Hope it helps 🙂
          Best regards,
          Nuno Santos

  5. Hi Nuno, thank you for your very informative series on ESP32 webservers. I have learned a huge amount from you. I would like to host a webpage with an embedded timer that has minimum blocking or delay on the ESP32 main loop. I tried easytimer.js but cannot get it working with the SPIFFS and AsyncWebServer methods that you have kindly explained.

    Do you have any alternative suggestions of different ways to display a timer (approximate – real timings will be on the ESP32 and displayed separately once timing has finished) on the webpage without blocking? Happy to try javascript but need more experienced advice on the right strategy!

    Equally, if you can see a good way of getting https://github.com/albert-gonzalez/easytimer.js working on the ESP32, that would be amazing!
    Simon

Leave a Reply