WebSockets-powered robot with a Raspberry Pi and an Arduino

Hey all; I'm fairly new to Rust (seven months give or take; JavaScript is my daily driver) and I've been working on a custom-built, 3D-printed robot car https://github.com/botinaboxer/codercar for the better part of the last year and a half.

Introduction

I've tried a lot of different avenues - NODE-RED, Python with MQTT and PiGPIO, Johnny-Five.js and Cylon.js, and a couple of others. Honestly I've had a blast learning how to interface with hardware using high-level languages, but, well...

It got to the point where (either for my lack of pre-planning, premature excitement, or whatever) I just couldn't take it - I needed a better way of coding everything up.

And so I turned to Rust, after a bit of soul searching and a lot of helpful advice. It's great, really. I love the ecosystem (Cargo, Rustup, rustc, crates - the whole shebang). And I'm finally getting somewhere with it.

Using the RPPAL library from golemparts, I was able to control the GPIO pins on my Raspberry Pi Zero W using Rust. That allowed me to use the attached L9110s motor controllers to get my CodeRcar moving. But I needed a front-end, since I didn't want to be using a CLI program for this, and I am more versed in front-end web development than in CLI/TUI interface development.

So I created codercar-controller, a Vue front-end application (built in Nuxt) with USB controller support. It is not finished, and I'm currently re-writing it using Quasar, but that's a discussion for another place.

Basically, it communicates with the Raspberry Pi Zero W via WebSockets, and I made codercar-rust as the back-end running on the Pi.

It uses tokio-tungstenite at the moment for managing the websockets, and serde to serialize and parse the JSON object representing the current status of a game controller plugged into the computer running the front-end (which incidentally, could be the Pi itself, but let's say I'm using my laptop).

And now to the present

So here's the deal. It works, and it works pretty well in my opinion. It can move forwards, backwards, left, and right, and the input lag is basically non-existent (at least for a little robot; I never noticed anywhere near this performance with NODE-RED and/or Python...)

However, now I'm looking to introduce an Arduino into the mix. Connected via USB to the Pi, the Arduino has an HC-SR04 ultrasonic distance sensor, a 8x8 LED matrix, and potentially other screens/sensors connected to it.

Maybe I'm barking up the wrong tree or unintentionally limiting myself here, but here's my dilemma.

I'd like to be able to have an async event loop running (probably ever 100 milliseconds, since I've tested that to be a good compromise between speed and freedom to do/wait for a bunch of robot stuff).

That loop should be able to do the following, based on my preliminary idea:

  • Read incoming data from the Arduino (Nano) over USB
    • That data would primarily be sensor data like from an ultrasonic sensor or maybe an accelerometer/gyroscope like an MPU6050
  • Read incoming data from the front-end (via websockets; mostly game controller state)
  • Make sense of the current situation
  • Perform appropriate commands (send data back to the Arduino, move motors/toggle GPIO pins, etc.)
  • Send data back to the front-end (like the sensor data so it can be graphed realtime - incidentally I'm still trying to figure that out, but that's a way easier problem to solve)

So, yeah...

Thing is, I really don't know how to code that all up in Rust. https://github.com/sdroege/async-tungstenite/blob/master/examples/interval-server.rs is an example that looks promising if you know what I mean :wink:, using async-tungstenite (which is a bit different from tokio-tungstenite which I'm using right now, but not much so. I'd rather use the async variant personally).

I'd like to be able to use something like that for the event loop, then find libraries for stuff like serial communication (RPPAL might be useful for that though, since it has UART support). In theory it doesn't seem too difficult. In practice, I'm rather overwhelmed.

I haven't found a good solution to this problem, and I've been searching on-and-off for the better part of a year. Maybe I'm just not familiar enough with Rust, but I can't seem to wrap my mind around what I need to do here.

It's just, I'm kinda at an impass here. Maybe I should just use Python, but frankly I don't like it. JavaScript is too heavy for this type of thing, at least according to my tests. C is, well... C, and any other alternative is either Haskell (which I honestly wouldn't mind using for something, just not this) or something far worse. I really want Rust for this - it's the theoretically-perfect solution.

My Verdict

I think I'm on the right track here. I don't think I'm completely crazy, and I'll probably figure it out eventually. It's just going to take a lot more work, and that's fine by me. As much as I might seem (and am) frustrated by all this, I chose to take on this project, and I've had a ton of fun/learned a bunch in the process.

I just feel like I'm getting to a turning point here. I'm close; I can feel it.

Anyway, if you'd like more details about the CodeRcar project, want to talk about Rust/Raspberry Pis/Arduinos/robotics in general, that's cool.

If you have any tips or pointers, or want more information pursuant to what my actual problem is here, that's cool too.

Thanks for taking the time to read this post; hopefully it was helpful/informative/intriguing in some way, and I hope you have a great day or night or whatever.

1 Like

Welcome to Rust world. You seem to have accomplished a lot there in a short time and that sounds like an interesting project.

I'm pretty new to the Rust world as well but I have been using tokio to do most of what is on your list. Some of which is ultimately run in a Raspberry Pi. All of which does run on a Raspberry Pi even if it will end up in a cloud or other server somewhere.

To your points, without going into specifics:

  • I use tokio-tunstestenite for web socket connections to my browser, and other, clients. That can handle game controller input and such easily.

  • See later...

  • I use tokio-serial for communication between a Raspberry Pi and lesser micro-controllers over serial links. So sending commands and getting status/responses back from an Arduino should be easy.

  • One can just send data back to the front-end client(s) over the same web socket connection used to get the game controller inputs.

As for the "making sense of the current situation". That is rather open ended and depends on what you want to do.

I imagine you will want a main control loop running on a regular interval, 10 or 100 times per second that is easy enough to set up in tokio.

You could just keep updates from your Arduino in some data structure that is shared with the control loop with an Arc.

You could set up mpsc channels to communicate incoming sensor/control inputs into that control loop and send control commands out again. Tokio has a select! that allows such a loop to respond to events on channels like that easily.

2 Likes

Very cool!

Thanks for your insight; it sure seems like tokio is the way to go what with all the modules, sub-crates, and derivatives. I'll at least check out tokio-serial for sure, and that whole MPSC paradigm looks useful.

Especially since it'd be kinda silly to expect only one reading per loop from the Arduino when there's no real-time clock syncing going on, and more successive sensor readings in a given period generally mean there's more data to work with (especially if I end up adding an accelerometer into the mix).

Yeah, a 100 millisecond control loop would probably be best (and yeah open-ended is the idea; at some point I wouldn't mind adding more sensors/screens/maybe a RPi camera or other stuff, so I'd need a reasonable time interval to do an arbitrarily high number of operations). I guess the challenge is mostly in setting up that data structure for the updates from the Arduino and setting up that control loop. But it certainly seems doable, and if not optimal than at least rather efficient. Cool.

At any rate, you've given me something to look into and work on, so :+1:

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.