Detect and Disconnect Inactive Web Sockets

Constraining my Sawppy rover logic to only a single rover operator was good, but that code immediately exposed the next problem: a web socket on one side doesn’t always know when it has lost contact with the other end. When this happens to my ESP32 server on the rover, it means the rover doesn’t know its driver is gone. And thanks to the code I just added, it means nobody else can get into the driver’s seat, either.

This is a known failure mode for web sockets, and there’s a prescribed mechanism to deal with it: a web socket heart beat with the “ping” and “pong” control frames. Either end of a web socket can choose to send a ping. Upon receipt of this ping a web socket implementation is obligated to reply with a pong. Doing this on a regular basis lets us check to verify the connection is still alive.

I started writing code to send pings, but then I realized it’s not really necessary. The browser client is obligated to send steer and speed values on a regular basis, and that can serve as my heartbeat. I can set a timer each time the rover receives the steer and speed commands, and if it’s been too long since the last transmission, the rover can proactively terminate the web socket so another rover operator can assume command.

As usual I started with my Node.js server running on my desktop to explore the concept and get an idea of how it’s supposed to work. For JavaScript I start a timer with setTimeout() and every time I receive a client command I call refresh() on that timer to reset the clock. If the timer goes off, it’s been too long and I call terminate() on the web socket instance. Which I need to keep track of now, something I managed to avoid earlier.

Once I understood how it was supposed to work, I moved on to implementation on ESP32. For this task I chose to use a FreeRTOS software timer. With mostly the same semantics as in JavaScript. When a new web socket is accepted, I call xTimerStart(). Every time the rover receives a command, xTimerReset() is called. If a reset does not occur in time, I queue up a web socket control frame set to HTTPD_WS_TYPE_CLOSE to close up shop.

That code took care of the server side logic, but that left a problem on the client side: How can I make it obvious when the server has decided to quit listening to commands from a particular controller?

[Code for this project is publicly available on GitHub]

Leave a Reply

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

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

Facebook photo

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

Connecting to %s