Language: EN

comunicar-una-pagina-web-con-un-esp8266-con-peticiones-ajax

Communicating a web page with an ESP8266 or ESP32 using AJAX requests

We continue with the tutorials of the ESP8266 and the ESP32. Now, we get to the most interesting aspects of this interesting MCU. This time we will see how to make AJAX requests.

We will refer to the ESP8266, but the same code is compatible for the ESP32, adjusting the name of the libraries. At the end, you have the code for both the ESP8266 and the ESP32.

We have had several entries dedicated to solving how to communicate a web page served to the client with the ESP8266 acting as a backend.

So, we started by seeing various examples about web forms, as a simple way to send data from the client to the ESP8266. This has allowed us to present concepts such as endpoint, backend, response, or the use of Javascript. However, we already anticipated that it was not the best available option.

The main problem with web forms is that, after submission, the client has to refresh the web page. Which was fine 20 years ago, but in this era of web applications, it is not very tolerable.

We have many technologies available to solve this situation. The first one we are going to see is AJAX (Asynchronous JavaScript And XML) a mechanism that allows making http calls in the “background” of a web page, without needing to reload the page.

In reality, AJAX is a set of components, the most important being the ‘XMLHttpRequest’ object.

During an AJAX request, the client sends a request to the server sending (optionally) a series of data. Then, the client receives a response from the server that contains (optionally) another series of data.

To illustrate the operation of AJAX we are going to do a simple example in which the served web page will display the value of ‘millis()’ received from the ESP8266. Of course, in a real project the ESP8266 would do something with the received data (turn on a light, actuate a motor) or the client with the received data (the value of an input, the measurement of a sensor).

With our simple example of receiving ‘millis()’ is enough to illustrate the mechanism of AJAX requests, which is what interests us, without complicating it with additional things that would only serve to distract.

To make our AJAX requests we are going to need JavaScript (did you see that coming? that’s why it’s called asynchronous JavaScript). We are going to use a bit of Vanilla JavaScript, that is, JavaScript ‘bare’, without additional libraries or frameworks.

Sounds good, right? Let’s get to work!

On the one hand, the main program of the program remains exactly the same as in the previous examples.

#include <ESP8266WiFi.h>
#include <ESPAsyncWebServer.h>
#include <FS.h>

#include "config.h"  // Replace with your network data
#include "Server.hpp"
#include "ESP8266_Utils.hpp"

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

  InitServer();
}

void loop(void)
{
}

On the other hand, we have the ‘Server.hpp’ file, where we have defined an endpoint for /GetMillis under the GET method, which has the function GetMillis() as a callback. This function simply returns the value of millis() as a text string.

AsyncWebServer server(80);

String GetMillis()
{
  return String(millis(), DEC);
}

void InitServer()
{
  server.serveStatic("/", SPIFFS, "/").setDefaultFile("index.html");

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

  server.onNotFound([](AsyncWebServerRequest *request) {
    request->send(400, "text/plain", "Not found");
  });

  server.begin();
    Serial.println("HTTP server started");
}

It is worth noting that, up to this point, all the server code is almost the same as we would have done for a web form. The server exposes certain endpoints

On the other hand, we have our web page ‘index.html’ inside the ‘data’ folder

A very simple web page where, basically, we only have a ‘div’ with id ‘counterDiv’, which we will use to display the received ‘millis()’ value.

<!DOCTYPE html>
<html class="no-js" lang="">
   <head>
      <meta charset="utf-8">
      <meta http-equiv="x-ua-compatible" content="ie=edge">
      <title>ESP8266 Ajax</title>
      <meta name="description" content="">
      <meta name="viewport" content="width=device-width, initial-scale=1">
   </head>
 
   <body>
    <h1>Millis</h1>
        <div id="counterDiv">---</div>
    </body>
  
    <script type="text/javascript" src="./js/main.js"></script>
</html>

In addition, we have included the reference to our Javascript file ‘main.js’, inside the ‘js’ folder. In this ‘main.js’ file is where the “magic” of our Ajax resides.

var counterDiv = document.getElementById('counterDiv');

function updateCounterUI(counter)
{
  counterDiv.innerHTML = counter; 
}

function ajaxCall() {
    var xmlhttp = new XMLHttpRequest();

    xmlhttp.onreadystatechange = function() {
        if (xmlhttp.readyState == XMLHttpRequest.DONE) {
           if (xmlhttp.status == 200) {
              updateCounterUI(xmlhttp.responseText);
           }
           else {
              console.log('error', xmlhttp);
           }
        }
    };

    xmlhttp.open("GET", "GetMillis", true);
    xmlhttp.send();
}

(function scheduleAjax() {
    ajaxCall();
    setTimeout(scheduleAjax, 1000);
})();

Let’s analyze this JavaScript file. From bottom to top, at the end we have the ‘scheduleAjax()’ function. The purpose of this function is simply to call the ‘ajaxCall()’ function every second. In a “normal” project, we wouldn’t necessarily have this component (or maybe we would, it depends). The AJAX call could be triggered by a user action (such as pressing a button).

The AJAX request itself is made within this ‘ajaxCall()’ function. On the one hand, we instantiate an ‘XMLHttpRequest’ object. Then, we associate the callbacks to execute in case of success (updateCounterUI) and in case of error (display the error in the console).

Next, we execute the AJAX call with the ‘Open’ method to the ‘/GetMillis’ endpoint under the GET method, and we execute the ‘Send()’ function. In this example we are not sending information, but we could send a String in the ‘Send()’ method.

Finally, we have the ‘updateCounterUI’ function, which is executed if the AJAX request has been successful, and to which we pass the content of the response ‘xmlhttp.responseText’. This function simply takes the received text (the value of ‘Millis()’) and replaces the content of ‘counterDiv’ with the received response.

We have associated ‘counterDiv’ with the corresponding ‘div’ of the DOM (the representation of the loaded web page) with the function ‘document.getElementById(‘counterDiv’)’

Now, if we load the web page, we will see that the value displays the Millis() counter refreshing every second.

esp8266-ajax-resultado

Let’s review

Let’s review what is happening here:

  • We have served a web page, which contains a JavaScript
  • This JavaScript makes an AJAX call to a server endpoint every second
  • The server is responding with the content of Millis()
  • The JavaScript receives the response, and dynamically changes the DOM to show the received value

In summary, in an AJAX request the client initiates the communication and launches an HTTP request “in the background”, sending a series of information, and receiving another, without the need to update the page.

What if we wanted the server to initiate the communication? Well, basically, we couldn’t do it with AJAX. We would have to go to other technologies like Websocket or MQTT, which we will see in upcoming entries.

In reality, there are techniques to do something similar with AJAX, such as Comets/Long polling, but they are not suitable for use in an ESP8266.

However, the fact that there are other technologies like Websocket or MQTT does not mean that they are better or replace AJAX. They are complementary technologies and each one has its use. For example, both alternatives involve a greater use of resources (we can serve fewer clients).

In many projects (most of them) the server offers a series of endpoints under certain types of request configuring an API. It is only necessary to receive requests (usually AJAX) to these endpoints in order to either obtain information or perform actions.

Where it would not be suitable to use AJAX is where we need high response speeds. That is, when we need to react quickly to changes in the backend (e.g. detect a button press) or a high transmission speed (e.g. LED animation effects)

But we will delve into all this in the next entry on the ESP8266, where we will see the use of Websockets. See you soon!

Download the code

All the code from this post is available for download on Github.

github-full

Version for the ESP8266: https://github.com/luisllamasbinaburo/ESP8266-Examples

Version for the ESP32: https://github.com/luisllamasbinaburo/ESP32-Examples