ESP32 HTTP webserver: receiving textual data in websocket

In this tutorial we will check how to receive textual data from a client on a websocket, using the ESP32 and the Arduino core. The tests from this tutorial were performed using a DFRobot’s ESP32 module integrated in a ESP32 development board.


Introduction

In this tutorial we will check how to receive textual data from a client on a websocket, using the ESP32 and the Arduino core.

We will be using the Async HTTP web server libraries to host the websocket endpoint that will be contacted by the client. For an introduction on the async websockets plugin from these libraries, please check the previous post.

We will be implementing the websocket client in Python. All the code needed is also shown below and, as we will see, it will be very simple and short.

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


The Arduino code

Includes and global variables

As usual, we start by including the libraries needed. We will include both the WiFi.h, for connecting the ESP32 to a Wifi network, and the ESPAsyncWebServer.h, in order to setup the server.

Additionally, we will store the network credentials in two constant global variables. We will need both the network name and password.

We will also need an object of class AsyncWebServer, so we can setup the whole HTTP server. As input, the constructor of this class will receive the port where the server will be listening to requests.

Finally, as global variable, we will need an object of class AsyncWebSocket. This object will be used to setup the websocket endpoint and the corresponding handling function, which will execute whenever a websocket event occurs.

Note that, as input of the constructor, we need to pass the websocket endpoint. We will setup the “/test” endpoint.

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

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

AsyncWebServer server(80);
AsyncWebSocket ws("/test");


The setup

Moving on to the Arduino setup, the first thing we will do is opening a serial connection, so we can output some messages from our program. After that, we will connect the ESP32 to the WiFi network.

Note that, once the connection is established, we print the local IP assigned to the ESP32 on the WiFi network. This IP will be needed in the Python code, when specifying the server address.

Serial.begin(115200);

WiFi.begin(ssid, password);

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

Serial.println(WiFi.localIP());

The rest of the setup function will be very simple and consist on the web server configurations.

First, we need to bind a handling function to our websocket endpoint, in order for our code to run when a websocket event occurs. We do this by calling the onEvent method on our AsyncWebSocket object.

As input, this method receives the handling function to execute when such event occurs. This function needs to have the signature defined by the AwsEventHandler type, which can be seen here. We will specify the function later.

ws.onEvent(onWsEvent);

Now, we need to register the websocket object on the HTTP web server. This is done by calling the addHandler method of the AsyncWebServer object, passing as input the address of the AsyncWebSocket object.

server.addHandler(&ws);

Finally, in order for the web server to start listening to incoming requests, we need to call the begin method AsyncWebServer object.

server.begin();

The whole setup function can be seen below.

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

  WiFi.begin(ssid, password);

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

  Serial.println(WiFi.localIP());

  ws.onEvent(onWsEvent);
  server.addHandler(&ws);

  server.begin();
}


The handling function

To finalize the code, we need to define the web socket endpoint handling function which, as already mentioned, needs to have a signature like the one defined by the AwsEventHandler type.

void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){
// WS event handling code
}

In the previous post, we focused our attention in the second and third arguments. As seen there, the third argument corresponds to an enum that indicates the type of websocket event that occurred.

The two events covered in that post where the client connection and disconnection, which we will also use here for debugging purposes.

Additionally, we will also look for the enumerated value WS_EVT_DATA, which indicates that data was received from the client.

if(type == WS_EVT_CONNECT){
 Serial.println("Websocket client connection received");

} else if(type == WS_EVT_DISCONNECT){
  Serial.println("Client disconnected");

} else if(type == WS_EVT_DATA){
  // Data received handling
}

In order to check the received data, we will look to the fifth and sixth arguments of our function, more precisely the array of data bytes and the length of the data received, respectively.

Just as a note, for simplicity, we are assuming that all the data is sent in a single frame. If you need to handle multiple frames, please check the original example from the async HTTP library.

We are also assuming that the data is being sent in textual format, but take in consideration that websockets also support binary payloads (and this library can also deal with that format).

So, since we have the array with the data and we know its length, we can simply iterate the whole array and print it. Note the conversion of each byte to char, since we assumed we are dealing with textual data.

for(int i=0; i < len; i++) {
   Serial.print((char) data[i]);
}

You can check the complete function below.

void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){

  if(type == WS_EVT_CONNECT){

    Serial.println("Websocket client connection received");

  } else if(type == WS_EVT_DISCONNECT){
    Serial.println("Client disconnected");

  } else if(type == WS_EVT_DATA){

    Serial.println("Data received: ");

    for(int i=0; i < len; i++) {
          Serial.print((char) data[i]);
    }

    Serial.println();
  }
}


The final code

The final Arduino source code can be seen below.

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

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

AsyncWebServer server(80);
AsyncWebSocket ws("/test");

void onWsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len){

  if(type == WS_EVT_CONNECT){

    Serial.println("Websocket client connection received");

  } else if(type == WS_EVT_DISCONNECT){
    Serial.println("Client disconnected");

  } else if(type == WS_EVT_DATA){

    Serial.println("Data received: ");

    for(int i=0; i < len; i++) {
          Serial.print((char) data[i]);
    }

    Serial.println();
  }
}

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

  WiFi.begin(ssid, password);

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

  Serial.println(WiFi.localIP());

  ws.onEvent(onWsEvent);
  server.addHandler(&ws);

  server.begin();
}

void loop(){}


The Python code

As usual, the Python code will be very simple and short. We start by importing the websocket module, which will expose a very simple API for us to create our client. If you haven’t yet installed this module, please consult the previous post for a short explanation on how to do it.

import websocket

Next, we need to create an object of class WebSocket, exposed by the previous module. After that, we will connect to the server.

The websocket endpoint address follows the format below. You will need to change #ESP_IP# by the IP of your ESP32, which will be printed to the serial console once it connects to the WiFi network, and #websocket_endpoint# by “test”, since it was the path we have configured in the Arduino code.

ws://#ESP_IP#/#websocket_endpoint#

You can check below how it looks after changing the placeholders by real values. Note that the IP of your ESP32 will most likely differ from mine.

 
ws = websocket.WebSocket()
ws.connect("ws://192.168.1.78/test")

To send some textual content to the websocket, we simply call the send method on our WebSocket object, passing as input a string with the content.

ws.send("Test")

Finally, we close the connection by calling the close method on the same object. The final Python code an be seen below and already includes this method call.

import websocket

ws = websocket.WebSocket()
ws.connect("ws://192.168.1.78/test")

ws.send("Test")

ws.close()

 

Testing the code

To test the whole end to end system, the first thing we need to do is compiling and uploading the ESP32 code, using the Arduino IDE.

When the procedure is finished, open the Arduino IDE serial monitor and wait for the device to connect to the WiFi network. If the connection is successful, then it should output an IP, which is the one you need to use on the Python code, as already mentioned.

Note that this tutorial assumes that both the ESP32 and the computer that is running the Python code are connected to the same WiFi network.

After putting the correct IP on the Python code, simply run it. If you go back to the Arduino IDE serial monitor, the connection event should have been detected, then the data sent by the Python client should get printed and finally the websocket closing event should also have been detected, as shown in figure 1.

ESP32 HTTP async websocket server receive data.png

Figure 1 – Textual data received on the websocket endpoint on the ESP32.

Advertisements

One Reply to “ESP32 HTTP webserver: receiving textual data in websocket”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s