WebSockets on the ESP32

Sending data between an embedded device and something like an PC sometime can be frustrating. Usually communication standards like UART/RS232 are used to establish an easy to use connection, while other standards like USB are difficult to handle and tend to be very complicated. I was playing with the ESP32 and wrote a basic WebSocket server. The advantage of websockets is the flexibility, combined with high data rates, low latency and the availability of webSocket client modules as well in modern browsers but also in .net or java.

This software is a PROTOTYPE version and is not designed or intended for use in production, especially not for safety-critical applications! The user represents and warrants that it will NOT use or redistribute the Software for such purposes. This prototype is for research purposes only. This software is provided "AS IS," without a warranty of any kind.

WebSocket?

WebSockets are similar to HTTP connections. When you request a webpage from a server, a TCP connection is established and closed as soon as the content has been transferred from the server to the client (e.g. browser). The difference between HTTP and websockets is that a websocket connection remains established and bidirectional communication becomes possible. The advantage over e.g. AJAX is, that there is no overhead for the handshake, as the connection is already open and thus the latency is lower.

Learn more about WebSockets:

WebSocket Handshake on the ESP32

The first thing we need is a WebSocket Task. It is very similar to a HTTP Server but might listen to another port. However, you also can listen to port 80 for websocket connections but then need to distinguish between HTTP and websocket request. I choose to listen to a dedicated port, in order to reduce complexity.

This snippet creates a new TCP listener at port 9998. If we receive a connection request,  ws_server_netconn_serve is called.

The WebSocket Handshake is a little tricky 🙁 .The client (e.g. a browser) sends a request which looks like this:

The server has to read Sec-WebSocket-Key, concatinate the magic string "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" to it, take the SHA1 of it, and return the base64 encoded result to the client:

Yep... I know... but no worries, we will go trough it step by step. Maybe its now time to get a coffee or a bottle of wine 😉

We need the following strings:

First, there is the Argument we are looking for, our magic string and finally the handshake response with a wildcard for our calculated hash.

Next we need to allocate some memory for the SHA1 input and the SHA1 result

We copy the static "private" key into the SHA1 input and try to get the parameter start from the request:

If we have found the Sec-Key, we load in into the SHA1 input, get the hash from the ESP32 SHA1 engine and base64 encoding:

Now that we have the SHA1 result, we can send the handshake response:

The connection is now open and we can wait for incoming messages:

 

Receive WebSocket frames

The format of Websocket frames can be found here. I created a structure to help "parsing" WebSocket frames:

For my application, a frame size with 2^7 bytes is sufficient, thus only frames with a length <126 are handled.

The received framed will be casted to the frame header structure. We check if the client wants to close the connection and then we extract the payload mask and unmask the payload. Once we have the payload, I call a function  WS_process_in_data , which (in this example) will loop back the Frame

Send WebSocket frames

Once the connection is established, I save the connection reference in a static variable

In order to send WebSocket Frames, we simply need to send a WebSocket Header, followed by the (luckily no masking etc here) payload. Again, this demo is limited to 2^7 bytes, but can be easily extended.

An example project with a WebSocket receive task can be found here:

ESP32_WebSocket_demo

Leave a Reply

Thomas

About Thomas Barth

Thomas Barth, born 1986, is a german teaching fellow and Ph.D. student. He studied electrical engineering in Darmstadt, Frankfurt and Helsinki and worked 7 years in industry automation before he switched to embedded systems and microelectronics. To read more about him, click here.