Language: EN

comunicar-una-pagina-web-con-websockets-en-el-esp8266

Communicating a web page with websockets in the ESP8266 or ESP32

We continue with the entries of the ESP8266 and the ESP32 seeing the widely heard, loved, and sometimes poorly understood Websockets.

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.

In the latest entries of this series of tutorials dedicated to the ESP8266, we have seen how to communicate a web page with the ESP8266 as a backend. We started by looking at web forms as a simple way. But we have already said that they are an outdated mechanism and that forces the client to refresh the web page when sending information.

In the previous entry we saw a much more convenient way with Ajax requests. Ajax requests allow processing a request to the server without reloading the page. But they have the disadvantage that the server does not have a mechanism to call the client and, in addition, they are relatively slow because they have to establish a connection in each request.

In this post we are going to see Websockets, another communication mechanism between the client and the server, which allows bidirectional communication and low lag.

Websockets are a “modern version” of traditional sockets that work over HTTP, and are designed to work in web applications. In a websocket a connection is created between the client and the server that remains open. For this reason they have much less lag, and communication can be done in both directions.

However, it should be noted that Websockets are not the definitive solution or that it replaces Ajax, but they are compatible technologies. It is even possible to have hybrid applications, which use both solutions simultaneously.

Websockets are suitable for applications that require a high refresh rate (real-time data capture, control of lighting systems, control of a motor, etc.) and where fast communication from the server to the client is necessary.

However, keeping the connection open involves resource consumption, which translates into a smaller number of clients that we can handle compared to a solution based on an API served through Ajax.

Code examples

Enough introduction. We will delve into an example of Websockets in the ESP8266. We are going to do the same example that we did in the entry about Ajax, simply updating a number on the web page with the value of ‘millis()’ obtained from the server. A minimal example that illustrates communication without distracting with additional elements.

In fact, we are going to see two examples based on the same code (the first of them is commented out). In both cases the client will make a websocket connection, and the difference is:

  • Example 1: The client will send data periodically and receives ‘millis()’ as a response
  • Example 2: The server uses a broadcast to inform clients of the value of ‘millis()’

Example 1 is commented out in the code. As it is, the code executes example 2, which uses broadcast.

Let’s see how our code looks.

In our main program, we have added the additional dependencies to the ‘WebSocketsServer.h’ library and references to ‘WebSockets.hpp’ and ‘ESP8266_Utils_WS.hpp’ which will contain repetitive code related to the use of Websockets.

On the other hand, we can see that in the loop we call ‘websocket.loop()’, which manages the Websockets process. And we also have part of the code from example 2, where the server uses the ‘broadcastTXT(…);’ function to inform all clients of the value of ‘millis()‘.

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

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

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

void loop(void)
{
  webSocket.loop();
  
  // Example 2, server call
  String message = GetMillis();
  webSocket.broadcastTXT(message);
}

Our ‘server.hpp’ file is very simplified. We do not have endpoints, and simply serve the content from the SPIFFS.

AsyncWebServer server(80);

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

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

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

Regarding the ‘ESP8266_Utils_WS.hpp’ file, it contains code that we can reuse for use with Websockets.

In this file, we first define a new Websocket associated with port 81. At the end, we have the ‘InitWebSockets()’ function, which initializes the Websocket and associates the callback function ‘webSocketEvent(…)’ to the Websockets events.

In the callback function, we discriminate the type of event received. In case it is a received text, we generate a response with ‘ProcessRequest()’ and send it to the client.

WebSocketsServer webSocket = WebSocketsServer(81);

void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) 
{
  switch(type) {
  case WStype_DISCONNECTED:
    break;
  case WStype_CONNECTED:
    //IPAddress ip = webSocket.remoteIP(num);
    //webSocket.sendTXT(num, "Connected");
    break;
  case WStype_TEXT:
    String response = ProcessRequest();
    webSocket.sendTXT(num, response);
    break;
  }
}

void InitWebSockets() {
  webSocket.begin();
  webSocket.onEvent(webSocketEvent);
  Serial.println("WebSocket server started");
}

Finally, we have the ‘WebSockets.hpp’ file, which contains the definition of our websocket ‘API’. Here we define the ‘ProcessRequest()’ function, which we used in the previous file. In this example, we simply return the value of ‘millis()’ encoded as a string.

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

String ProcessRequest()
{
  return GetMillis();
}

In example 2, where the server broadcasts to all clients, no additional code is necessary.

In example 1, where the client makes periodic requests to the server, we have the code commented out in the ‘onopen’ event. Here, we define a timer every 100ms, to send a text (in this case, an empty text) to the server and “provoke” it to send us the response.

Result

We upload everything to the ESP8266 and access the web page to see that, indeed, the value of ‘millis()’ is updated correctly. Just like in the case of Ajax but much faster.

esp8266-ajax-resultado

We’re done! We have seen how to make web forms, how to use Ajax requests, and how to use Websockets, as ways to communicate the frontend with the ESP8266 as a backend.

In the next entry we will see how to make asynchronous Websockets, and in the following one UDP connections. 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