ESP32 Websocket: Control ESP32 Pins [PlatformIO]

This tutorial covers how to implement an ESP32 Websocket server to control ESP32 GPIO Pins. In more detail, we will describe how to build a web page (web socket client) that controls ESP32 Pins using Websocket. To do it, we will use a RGB strip LED whose color can be changed remotely. The ESP32 Websocket server will be developed using PlatformIO. Anyway, you can use another IDE and import the code described below. You can buy the ESP32 at DigitSpace. If you have an ESP8266 you can read how to control GPIO PINS with ESP8266.

What is the Websocket protocol

Before diving into the project details, it is useful to describe briefly what is the Websocket protocol and how we can use it. The Websocket protocol is described in RFC 6645. It is a two-way protocol over a TCP channel. The Websocket protocol enables the interaction between a client (the web browser) and a web server (in our case an ESP32 Websocket server). The interesting aspect of this protocol is that it enables real-time data exchange between the web client and the web server. We will use this feature to exchange data between a web page and the ESP32 Webscoket server so that it receives the inputs from the web page and then it controls its output PINS accordingly.

Project overview: ESP32 Web socket server and Websocket client

To describe how to enable a real-time data transfer from the web page (the Websocket client) to the ESP32 Websocket server, we will suppose that we want to create a web page that we use to pick a color. The color codes are passed through the WebSocket protocol to the ESP32 that uses it to control RGB Led strips. We have used this example in several posts and you can read them if you want to know the different approaches we can use when we have to send data from a client to a server:

The image below describes the project we are going go build:

Websocket protocol:
ESP32 Websocket server and Webscoket client

If you don’t need a two-way data exchange you can consider ESP32 Server Sent Events.

Setting up the Project

Let’s create a new PlatformIO project setting as development board ESP32 Dev and add the following libraries in the platformio.ini file:

lib_deps =
   ESPAsyncTCP 
   ESP Async WebServer
   adafruit/Adafruit NeoPixel @ ^1.7.0

This ESP32 Websocket project uses the followng libraries:

While the first two are mandatory to make the project working the last one is optional. If you don’ have a Neopixel LED strip you can use a simle RGB LED and therefore you don’t need this library. If you prefer you can test the project without using the LEDS.

ESP32 Websocket Server source code

To implement a Websocket Server using ESP32 we need the following source code:

#include <Arduino.h>
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <Adafruit_NeoPixel.h>
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">  <title>ESP8266 Websocket</title>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js">
  <script src="https://cdn.jsdelivr.net/npm/spectrum-colorpicker2/dist/spectrum.min.js"></script>
  
  <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/spectrum-colorpicker2/dist/spectrum.min.css">
  <script language="javascript">
  
  
    var gwUrl = "ws://192.168.1.58/ws";
    var webSocket = new WebSocket(gwUrl);
    webSocket.onopen = function(e) {
        console.log("open");
    }
    webSocket.onclose = function(e) {
        console.log("close");
    }
  
   webSocket.onmessage = function(e) {
        console.log("message");
    }
    function handleColor() {
      var val = document.getElementById('type-color-on-page').value;
      webSocket.send(val.substring(1));         
    }
  </script>
  
  <style>
    h2 {background: #3285DC;
        color: #FFFFFF;
        align:center;
    }
  
    .content {
        border: 1px solid #164372;
        padding: 5px;
    }
    
    .button {
       background-color: #00b300; 
       border: none;
       color: white;
       padding: 8px 10px;
       text-align: center;
       text-decoration: none;
       display: inline-block;
       font-size: 14px;
  }
  </style>
</head>
<body>
  <h2>ESP32 Websocket</h2>
  <div class="content">
  <p>Pick a color</p>
  <div id="qunit"></div>
  
  <input type="color" id="type-color-on-page"  />
   <p>
     <input type="button" class="button" value="Send to ESP8266" id="btn" onclick="handleColor()" />
   </p>
  
  </div>
</body>
</html>
)rawliteral";  

// WiFi config
const char *SSID = "your_ssid";
const char *PWD = "your_wifi_password";
// Web server running on port 80
AsyncWebServer server(80);
// Web socket
AsyncWebSocket ws("/ws");
// Neopixel
Adafruit_NeoPixel strip = Adafruit_NeoPixel(16, 15, NEO_GRB + NEO_KHZ800);
void connectToWiFi() {
  Serial.print("Connecting to ");
  Serial.println(SSID);
  
  WiFi.begin(SSID, PWD);
  
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
    // we can even make the ESP32 to sleep
  }
 
  Serial.print("Connected. IP: ");
  Serial.println(WiFi.localIP());
}

void handlingIncomingData(void *arg, uint8_t *data, size_t len) {
  AwsFrameInfo *info = (AwsFrameInfo*)arg;
  
  if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
    String hexColor = "";
    for (int i=0; i < len; i++)
      hexColor += ((char) data[i]);
    Serial.println("Hex Color: " + hexColor);
    
    long n = strtol(&hexColor[0], NULL, 16);
    Serial.println(n);
    strip.fill(n);
    strip.show();
  }
}
// Callback for incoming event
void onEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, 
             void * arg, uint8_t *data, size_t len){
   switch(type) {
      case WS_EVT_CONNECT:
        Serial.printf("Client connected: \n\tClient id:%u\n\tClient IP:%s\n", 
             client->id(), client->remoteIP().toString().c_str());
        break;
      case WS_EVT_DISCONNECT:
         Serial.printf("Client disconnected:\n\tClient id:%u\n", client->id());
         break;
      case WS_EVT_DATA:
         handlingIncomingData(arg, data, len);
         break;
      case WS_EVT_PONG:
          Serial.printf("Pong:\n\tClient id:%u\n", client->id());
          break;
      case WS_EVT_ERROR:
          Serial.printf("Error:\n\tClient id:%u\n", client->id());
          break;     
   }
  
}
void setup() {
  Serial.begin(9600);
  strip.begin();
  strip.setBrightness(255);
  strip.fill(strip.Color(200,0,0));
  strip.show();
  connectToWiFi();
  ws.onEvent(onEvent);
  server.addHandler(&ws);
 
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", index_html, NULL);
  });
   server.begin();
}
void loop() {
  ws.cleanupClients();
}Code language: C++ (cpp)

or you can download the source code:

Below the code explanation.

Configuring the AsyncWebServer and the AsyncWebSocket in the ESP32

The ESP32 has to create two different servers:

  • AsyncWebServer to respond on port 80 and provides the Web page so that the user can pick the color
  • AsyncWebSocket to handle the Websocket protocol between the web browser and the ESP32 server
// Web server running on port 80
AsyncWebServer server(80);
// Web socket
AsyncWebSocket ws("/ws");Code language: C++ (cpp)

In this way the ESP32 handles the websocket channel on the path /ws.

Handling Websocket events on the ESP32

Once the connection is established between the ESP32 and the browser, it is necessary to handle a set of Websocket events such as:

  • connection
  • disconnection

and so on. The code is shown below:

// Callback for incoming event
void onEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, 
             void * arg, uint8_t *data, size_t len){
   switch(type) {
      case WS_EVT_CONNECT:
        Serial.printf("Client connected: \n\tClient id:%u\n\tClient IP:%s\n", 
             client->id(), client->remoteIP().toString().c_str());
        break;
      case WS_EVT_DISCONNECT:
         Serial.printf("Client disconnected:\n\tClient id:%u\n", client->id());
         break;
      case WS_EVT_DATA:
         handlingIncomingData(arg, data, len);
         break;
      case WS_EVT_PONG:
          Serial.printf("Pong:\n\tClient id:%u\n", client->id());
          break;
      case WS_EVT_ERROR:
          Serial.printf("Error:\n\tClient id:%u\n", client->id());
          break;     
   }
  
}Code language: C++ (cpp)

As you can notice, we simply log the events without taking any actions except when a new data packet arrives. This data packet, sent by the broswer, holds the RGB color information. We handle this event in the handleIncomingData(..) Here we will set the LED strip color:

void handlingIncomingData(void *arg, uint8_t *data, size_t len) {
  AwsFrameInfo *info = (AwsFrameInfo*)arg;
  
  if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
    String hexColor = "";
    for (int i=0; i < len; i++)
      hexColor += ((char) data[i]);
    Serial.println("Hex Color: " + hexColor);
    
    long n = strtol(&hexColor[0], NULL, 16);
    Serial.println(n);
    strip.fill(n);
    strip.show();
  }
}Code language: C++ (cpp)

We don’t want to focus too much on how to set the Neopixel color because it is out of the scope of this article. Instead, focus your attention on the way used to read the data.

How to create a Web socket client: the Web page

The ESP32 has to provide the Web page to the browser that will have all the logic in javascript to handle the web socket. As you remember, the ESP32 behaves not only as a Websocket server but also as a Web server listening on port 80:

server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", index_html, NULL);
 });Code language: C++ (cpp)

Setup() method in the ESP32 Websocket server

The last part is glueing all the pieces so that the Webserver and the Websocket server starts correctly and the ESP32 connects to the WiFi:

void setup() {
  Serial.begin(9600);
  strip.begin();
  strip.setBrightness(255);
  strip.fill(strip.Color(200,0,0));
  strip.show();
  connectToWiFi();
  ws.onEvent(onEvent);
  server.addHandler(&ws);
 
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", index_html, NULL);
  });
   server.begin();
}
void loop() {
  ws.cleanupClients();
}Code language: C++ (cpp)

This piece of code is very simple.

ESP32 Javascript Websocket to handle events client-side

The ESP32 provides a web page that is loaded by the browser as it connects to the Web server. This page implements the logic to handle the Websocket channel:

Websocket client

If we inspect the HTML page content, we can notice an interesting piece of code:

 <script language="javascript">
    var gwUrl = "ws://" + location.name + "/ws";
    var webSocket = new WebSocket(gwUrl);
    webSocket.onopen = function(e) {
        console.log("open");
    }
    webSocket.onclose = function(e) {
        console.log("close");
    }
  
   webSocket.onmessage = function(e) {
        console.log("message");
    }
    function handleColor() {
      var val = document.getElementById('type-color-on-page').value;
      webSocket.send(val.substring(1));         
    }
  </script>Code language: JavaScript (javascript)

This the code that handles the websocket protocol between the web browser and the ESP32 server. Notice that we use the path ws:// to connect to the server. Moreover, in this piece of code, we handle several events as we did previously in the ESP32 WebSocket server code. At the end, the code sends the color to the server:

webSocket.send(val.substring(1));Code language: JavaScript (javascript)

This is how the web page looks in your smartphone:

Websocket to send RGB color to ESP32

The final result is shown below:

Wrapping up

At the end of this tutorial, you have learned how to implement ESP32 Websocket server and how to create a Websocket client using Javascript and ESP32. We have discovered how to send real-time data from a web browser to the ESP32 using WebSocket protocol. Now you can develop a more complex use case using WebSocket protocol to exchange data from the web browser to the ESP32 WebSocket server.

Tags:
    1. Philip Chisholm April 3, 2021

    Add Your Comment