Steering control precision was something I found lacking in my SGVHAK rover software project. This is my second effort at browser-based rover control and I added code to vary steering rate as a function of speed. Over the next few weeks (or more) I will see if it’s an overall improvement and see if it’s worth keeping. The next problem I wanted to solve with browser-based rover driving is that HTTP was designed to be completely stateless, and a mechanism to serve many clients. This doesn’t work so well for driving a vehicle, where we want to have only one driver at the wheel.
I didn’t know how to solve this problem with SGVHAK rover. Once I had an HTTP web server up and running, it would happily serve rover control UI to any number of clients. And it would happily accept and process HTTP POST submissions from any and all of those clients. In practice this means we can have multiple touchscreen phones all trying to drive the rover, and the rover ends up being very confused with conflicting messages coming in interleaved with each other. Steering servos would rapidly flick between multiple positions, and driving motors would rapidly change speeds. This causes hardware damage.
Switching from stateless HTTP POST to web sockets gave me a tool to solve this problem. Now the server side code can keep a reference to a specific web socket, and any additional attempts to set up a rover driving web socket can be rejected. This allows me to keep the number of rover drivers to at most one.
For my Node.js server, I didn’t even need to keep a global reference. The web socket server class maintains a list of clients, and I can check the number of clients. The trickier part for me was figuring out how to reject additional sockets. I looked fruitlessly in the web socket server for a while, because the answer is actually a little bit upstream: The HTTP server has an “upgrade” event that I can subscribe to, and it is called whenever a web socket client request upgrading from HTTP GET to websocket. This is the location where I can reject the connection if there was already an existing client. With the Node.js server configured to test the scenario on my development desktop, I found a few bugs in my client-side browser code. Once it worked I could continue to my ESP32 code.
For my ESP32 server, it means tracking two things: an identifier for the HTTP server (
httpd_handle_t) and a socket descriptor. Together those two values uniquely identify a websocket. The URI handler I registered to handle websocket upgrade requests is given an instance of
httpd_req_t. Using that, I can obtain both parts of an unique identifier and compare them against future calls into the URI handler. I process requests if the server handle and socket descriptor matches, and reject them if they don’t. With this code in place, only a single driver is commanding a rover at any given time. But this code also immediately exposed another problem: how to detect if that single driver is gone?
[Code for this project is publicly available on GitHub.]