ESP32 WebSocket Server: Find out how to Easily Make one Work and Create a WebSocket Server with this Complete Tutorial. Find out Exactly How to get Instant, Fully Bi-Directional Communications Running Easily.

ESP32 WebSocket Server: You're probably here because you tried to get your ESP32 to control some relays and return a temperature reading to the browser. The problem is that you have to refresh the page every time you want to see new readings.

  • Uses Modern Web Technology not the older AJAX technology.

  • Use it for instantaneous bi-directional communications - no poll or refresh.

  • They have minimal overhead for data transfer i.e. they are efficient.

  • Designed for low latency i.e. they are designed to be fast.

  • Simultaneous update all attached Clients(browsers) are updated togther.

When you initially start using IOT control, you setup the ESP32 as a webserver the problem is that a webserver only serves a page on request from the browser (using the HTTP_GET command). So every time you want an update from the ESP32 you have to click the browser reload button!

ESP32 Websocket Server : Client to Server, LED update, Server to Client

You can solve this problem using the newer WebSocket technology - there's an introduction in this link. Simply put a WebSocket allows you to establish bi-directional communication without refreshing the browser page.

Note: Websockets provide true bi-directional comms. without high overhead.
Note: Websockets are more difficult to use than just HTML.

This is exactly what you want for getting readings back from an ESP32. You can also use WebSockets to go the other way and control LEDs or relays on the ESP32.

The Webserver

You need a web server as this code reacts to browser requests and responds by returning a web page i.e. it serves up web page to the client (The ESP32 is the server).

The first thing you need to do is setup a webserver using an Arduino Library - in this case:

    ESPAsyncWebServer Library

You use the asynchronous webserver library because it is interrupt and event driven giving you better performance. In the previous link you can find a discussion and comparison of synchronous and asynchronous web servers.

Web Server example

First of all you need to serve HTML pages to the client (browser) from the server (ESP32) using a standard library, in this case, the ESPAsyncWebServer library. Program this example sketch into your ESP32:


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

const char* ssid = "your_SSID";
const char* password = "your_PASSWORD";

AsyncWebServer server(80);

void handleroot(AsyncWebServerRequest *request) {
  request->send(200, "text/html", "<html><body><h1>Hello, world!</h1></body></html>");
}

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

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

  Serial.println("Connected to WiFi");
  Serial.print("IP Address: ");
  Serial.println(WiFi.localIP());

  server.on("/", HTTP_GET, handleroot);

  server.begin();
}

void loop() {

}
Enter your router information into the code (password and ssid). Then observe the IP address shown in the serial monitor of the Arduino IDE. Type that IP address into the browser URL box.

For this example, when the browser requests a page from the ESP32 the following html text is returned...

    <html><body><h1>Hello, world!</h1></body></html>

... so the text...

    'Hello, world!'

...will be displayed in the browser window.

ESP32 Serves pages

So now the ESP32 can serve a page when it gets an HTTP_GET request from the browser.

The ESP32 code :

server.on("/", HTTP_GET, handleroot);

...starts the server, and when it receives an HTTP_GET for URL "/" (or index) from the client, it then calls the handleroot function:

void handleroot(AsyncWebServerRequest *request) {
  request->send(200, "text/html", "<html><body><h1>Hello, world!</h1></body></html>");
}

... which sends a the text/html item containing the HTML string enclosed in quotes. The number 200 is an error number that in this case says everything is fine.

Data From Client (Browser) to Server (ESP32)

Here is the very uninteresting Browser page. It is intentionally uninteresting as it shows the fundamentals and does not include fancy CSS styling - that makes the code longer.

ESP32 Websocket Server Client to Browser button

Set up websockets on the Server (ESP32)

This allows the client (browser) to send data to the server (ESP32).

To enable websockets you can use the websocket code already inside the ESPAsyncWebServer library; with the following code in addition to the original asynchronous server:

AsyncWebSocket webSocket("/ws");

After the server start code :

server.on("/", HTTP_GET, handleroot);

...add the following code:

server.addHandler(&webSocket);
webSocket.onEvent(handleWebSocketEvent);

This code starts monitoring for the websocket communication and calls handleWebSocketEvent when an event triggers the webscocket.

In the handleWebSocketEvent function that you write you can react to websocket events such as:

WS_EVT_DISCONNECT
WS_EVT_CONNECT
WS_EVT_DATA

So your code, in the ESP, can react to received websocket events that indicate either a new connection, a dis-connection or data reception.

Setup websockets on the client side

The next step is to create the html code that will send data to the server (ESP32). So lets create a button in the browser, that toggles the built in LED on an ESP32.

For this you need some JavaScript which is used to create the websocket functionality:
In this case we'll just create a button that sends a toggle command to the ESP32.

For this operation the following HTML and JavaScript is required:

<!DOCTYPE html>
<html>
<head>
</head>
<body>
  <h1>ESP32 Websocket Example - Client to Server</h1>
  <button onclick='toggleLED()'>Toggle LED</button>
  <br clear="all">

  <script>
    var webSocket;
    connectWebSocket();
    function connectWebSocket() {
      webSocket = new WebSocket('ws://' + window.location.hostname + '/ws');
      webSocket.onopen = function(event) {
        console.log('WebSocket connected');
      };
      webSocket.onerror = function(event) {
        console.error('WebSocket error:', event);
      };
    }
    function toggleLED() {
      webSocket.send('toggle');
    }

  </script>
</body>
</html>

Here the toggleLED() function sends the data string 'toggle' over the connected websocket when the button, labelled 'Toggle LED', in the HTML page is pressed.

In the ESP32 code, the handleWebSocketEvent() function will detect the WS_EVT_DATA event updating the LED state.

The following ESP32 code will update ring 'toggthe LED on receipt of a data payload from the webSocket matching the stle'.

void handleWebSocketEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
  switch (type) {
    case WS_EVT_DISCONNECT:
      Serial.printf("[u] Disconnected!\n", client->id());
      break;

    case WS_EVT_CONNECT:
      {
        IPAddress ip = client->remoteIP();
        Serial.printf("[u] Connected from %d.%d.%d.%d", client->id(), ip[0], ip[1], ip[2], ip[3]);
      }
      break;

    case WS_EVT_DATA:
      {
        AwsFrameInfo *info = (AwsFrameInfo*)arg;
        if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
          data[len] = '\0';
          String payload = String((char*)data);

          Serial.printf("[u] Received: %s\n", client->id(), payload.c_str());
          if (payload == "toggle") {
            ledState = !ledState;
            // digitalWrite(LED_PIN, ledState);
            digitalWrite(LED_PIN, !ledState); // For AI thinker LED control is inverted (pin33)
          }
        }
      }
      break;
  }
}

Example 1 code Download

You can download the first example code here - change the file extension to .ino and label the whole lot as :

    websockets_CtoS_toggle_led.ino

Save the file in a directory labelled websockets_simple_CLI_TO_SER_button_toggle_led the same a any other Arduino code. Don't forget to enter your own SSID and router password. Program your ESP32 - an example of programming for the AI thinker board is shown here, for different boards just select your board from the drop down list..

As usual copy the IP address shown in the monitor and paste that into your browser address bar.

This is a no-frills program so observe the serial monitor output to get the status of the link. You should see something along these lines:
Connecting to WiFi...
Connecting to WiFi...
Connecting to WiFi...
Connected to WiFi
IP Address: 192.168.0.62
Client to Server WebSockets with ESPAsyncWebServer.h
[1] Connected from 192.168.0.51
[1] Received: toggle
[1] Received: toggle
[1] Received: toggle
[1] Received: toggle
The ESP32 connects to the WiFi and returns its IP address. When you type the IP address into the address bar you get the message similar to:

    [1] Connected from 192.168.0.51

... and when you hit the button on the web page the websocket sends data from the browser to the ESP32 to control the LED:

    [1] Received: toggle

ESP32 WebSocket Server: Multiple WebSockets

The concept of having multiple open websockets gives a very different slant to how you control an ESP32 compared to just using GET and POST protocols with traditional HTML (really just point to point control). If you alter data from one Client connection through a websocket, all the other client connections should get updated.

Warning: You can have more than one active WebSocket!
You see that '[1]' in the above serial output - that indicates that websocket 1 is active. Open another browser page (even open a different browser), type in the same IP address (the 1st one). Then the serial output will be something like:

[2] Connected from 192.168.0.51
[2] Received: toggle
[2] Received: toggle

The '[1]' has become a '[2]' showing that the second websocket is now active and a toggle signal was received from websocket 2. You can toggle the LED from any browser window that has a connected websocket.

For this simple code controlling the LED on the ESP32 from either instance does work. But consider this:

    What if you reported the state of the LED in the HTML page?

Toggling the LED in one browser window would update that window, but not the other one. In fact when your ESP32 code toggles the LED it should also output a broadcast websocket message to all websockets. The broadcast message says "this is the new value of the LED".

In this way all browser pages can be kept in step.

Note: WebSockets are designed for multiple instances - you have to code for that.

ESP32 WebSocket Server: Multi Broadcast LED status

Lets update the code to show the state of the LED in each browser instance.

Here's the screen shot of the browser:


ESP32 Websocket Server : Client to Server, LED update

There are two problems:

  1. On websocket connection, the browser does not know the current LED state.
  2. Other instances of the websocket (in different browser pages) need LED state updates.

So how do you code this action?

LED status

HTML to show websocket LED status

First lets add an LED status message to the current HTML code:


 <p>LED Status: <span id='ledStatus'></span></p>

This gives a place holder for the LED status message that will be updated by the javascript on receipt of a websocket LED status message.

Javascript to show websocket LED status

Now add in the Javascript that reacts to a websocket message:


webSocket.onmessage = function(event) {
        var data = JSON.parse(event.data);
        if (data.label === 'ledStatus') {
          document.getElementById('ledStatus').innerHTML = data.value;
        }
      };

When the Browser/Javascript recives a properly formatted JSON string it will display the received message data value in the element that has id='ledStatus'.

In this case the messages are either LED ON, and LED OFF

{
  "label": "ledStatus",
  "value": "LED ON"
}

Alternatively for OFF we have:

{
  "label": "ledStatus",
  "value": "LED OFF"
}

It may seem like a lot of trouble to use JSON formatted strings, but when there is lots of data you can access the information in a scalable way.

ESP32 code to broadcast LED status

in the handleWebSocket code we had for the WS_EVT_DATA case statement:

case WS_EVT_DATA:
  {
  ...

  if (payload == "toggle") {
        ledState = !ledState;
        // digitalWrite(LED_PIN, ledState);
        digitalWrite(LED_PIN, !ledState); // For AI thinker LED control is inverted (pin33)
      }

  }

Now all you do is add in code that generates a JSON formatted string (in one line), that depends on the ledState variable and broadcast that to all sockets:

    case WS_EVT_DATA:
      {
      ...

          if (payload == "toggle") {
            ledState = !ledState;
            // digitalWrite(LED_PIN, ledState);
            digitalWrite(LED_PIN, !ledState); // For AI thinker LED control is inverted (pin33)

            String ledUpdate = "{\"label\":\"ledStatus\",\"value\":\"" + String(ledState ? "LED ON" : "LED OFF") + "\"}";
            webSocket.textAll(ledUpdate);
          }

     }

Here the JSON string value is changed depending on the ledState to either "LED ON" or "LED OFF" and is then sent as a broadcast message to all connected websockets.

So, whenever the server (ESP32) receives a websocket 'toggle' (LED) message, as a data event, the ESP32 will broadcast the value to all connected websockets.

ESP32 WebSocket Server: A few Extras

Initialise Browser on websocket connect

The LED status information (sent to the browser) is not automatically updated on a websocket browser connect event so you have to code it into the ESP32 code. This is separate to the when the clicking the button.

Just to complete this part, set up the initialisation of the LED state using the WS_EVT_CNCT, again in the handleWebSocket function:

case WS_EVT_CONNECT:
      {
        IPAddress ip = client->remoteIP();
        Serial.printf("[u] Connected from %d.%d.%d.%d\n", client->id(), ip[0], ip[1], ip[2], ip[3]);
      }
      break;

Adding in the initialisation code:

case WS_EVT_CONNECT:
      {
        IPAddress ip = client->remoteIP();
        Serial.printf("[u] Connected from %d.%d.%d.%d\n", client->id(), ip[0], ip[1], ip[2], ip[3]);

        // Initialise the browser entry box with the current LED state.
        // Create a JSON message containing the LED status
        String ledStatusUpdate = "{\"label\":\"ledStatus\",\"value\":\"" + String(ledState ? "LED ON" : "LED OFF") + "\"}";

        // Send the LED status to the client
        client->text(ledStatusUpdate);
      }

The only difference between this code and the previous is that the specific newly connected websocket is updated (it's not a broadcast command).

Reconnect when connection lost

Another useful thing to add to the Javascript code is an automatic re-connection operation so that if the WiFi is lost for some reason the code will try and re-connect the websocket. It means you don't have to refresh the browser, it's all automatic.

You can do this by adding the following code:

webSocket.onclose = function(event) {
console.log('WebSocket disconnected');
        var messageElement = document.getElementById('message');
        messageElement.innerHTML ='WebSocket disconnected. Reconnecting...';
        setTimeout(connectWebSocket, 2000);
        setTimeout(function() { messageElement.innerHTML = ''; }, 3000);
      };

Also, add into the body html, an 'id' for the status message.

<p><span id='message'></span></p>

Example 2 code Download

You can download the second example code here - change the file extension to .ino and label the whole lot as :

    websockets_CtoS_toggle_led_stat_upd.ino

Test Example 2

To test out this functionality, use your mobile as a hotspot (or another ESP32), get the new IP address (it will be different). Reprogram the ESP32 with your hotspot name and password. Set your PC to use the hotspot. Use the IP address in the browser. Turn off the hotspot. Turn it on again, and you'll see the re-connection message as coded above.

ESP32 WebSocket Server: Test LED Status Update


To test the code open two browser pages and type in the original IP address into both. When you click the toggle LED button in one, the message will be updated in both.

Click one toggle button to turn on the LED. Close the second browser page. Open a new browser page as before, observe the LED status message - it should be LED ON - indicating that the initialisation code is operating correctly.

Data From Server (ESP32) to Client (Browser)

This allows the server (ESP32) to send data to the client (browser).

In this example, we'll send an update every second and every second, a count value will be increased and transmitted through a websocket.

Here's the screen shot of the browser:

ESP32 Websocket Server : Client to Server, LED update, Server to Client

ESP32 WebSocket Server: Generate ESP32 counter update code

To generate the count value you use the millis() timer in non blocking mode to generate a 1 second count update in the loop function:

int countTimeWas = millis();
long countVal = 0;

void loop() {
  // Send data to browser, increasing number every second.
  if (millis() - countTimeWas >= 1000) {
    countTimeWas = millis();

    String countUpdate = "{\"label\":\"count\",\"value\":" + String(countVal++) + "}";
    webSocket.textAll(countUpdate);
}
}

ESP32 WebSocket Server: HTML counter placeholder

For the HTML code you need a place holder to show the count value

<p>Count: <span id='countData'></span></p>

ESP32 WebSocket Server: Javascript websocket counter recevier

...and some javascript to react to the count update received via the websocket adding the 'count' data.label comparison:


webSocket.onmessage = function(event) {
       var data = JSON.parse(event.data);
       if (data.label === 'ledStatus') {
         document.getElementById('ledStatus').innerHTML = data.value;
       } else
if (data.label === 'count') {
         document.getElementById('countData').innerHTML = data.value;
       }
     };

Example 3 - Full code Download

This code is a full example of using websockets in your program.

It incorporates:

  1. Data sent from Client to Server (ESP32) - press a button in the HTML page.
  2. WebSocket broadcast update - a consequence of multi-instance WebSockets.
  3. Data sent from Server to Client (Browser) - every second a count update.

You can download the third full code example code here - change the file extension to .ino and label the whole lot as:

    websockets_CtoS_toggle_led_stat_upd_StoC_count.ino.

Conclusions

This page shows you exactly how to implement an Ardino WebSocket Server, showing you the programming methods to allow bi-directional communication.

First a Webserver is example presented, followed by a Client to Server (ESP32) example. This is expanded from a simple HTML button to more complex status update, which also explains the need for multi-socket broadcast operations. The final example completes the communication path by adding data from Server to Client (Browser).

For the Client to Server (ESP32) programming you need some Javascript and HTML While for the Server to Client (Browser) you need an Arduino WebSocket library.


Written by John Main who has a degree in Electronic Engineering.

Note: Parts of this page were written using chatgpt as a research assistant.





Comments

Have your say about what you just read! Leave me a comment in the box below.

Don’t see the comments box? Log in to your Facebook account, give Facebook consent, then return to this page and refresh it.



Privacy Policy | Contact | About Me

Site Map | Terms of Use