← Back to Kevin's newslettersPublished: 2020 Feb 11

Robot update

If I have one skill, it’s starting a whimsical project and then immediately getting side-tracked by the dozens of yak shaves it ends up spawning.

Anyway, last time I promised to talk about my highly advanced robot:

Two wheels and motors bolted to a piece of OSB, held up by a rag, with a circuit board attached via rubber band and wires extending off frame

However, I’ve since realized there are way too many topics here, so instead let’s play “choose your own adventure” — email me if you want more details on any of the following and I’ll write a blog post or reply with a pile of notes:

Of course, as soon as I got all of this working I decided that the Raspberry Pi was cliché and things like “wifi”, “ssh”, and “an operating system” were making me soft, so I immediately started over with a microcontroller:

That’s the gist, write me if you want more details.

Thoughts on embedded Rust

After a few months, I have some thoughts about all this:

RTFM is an amazing framework that makes concurrency safe, fast, and easy (compared to C, anyway); it leverages hardware priority levels to prove (at compile-time) that resources can be shared without runtime overhead (i.e., mutexes), includes a low-overhead task scheduler, and eliminates the hard-to-debug concurrency/interrupt memory corruption nonsense that would absolutely be plaguing me if I were coding C.

Cargo is great: It’s easy to split code up in packages, run on both desktop (for debug/testing) and microcontroller, and also grab handy open source packages.

Shout-out in particular to bitvec, a package for easy manipulation of bits. Seriously, I’m decoding one-bit-at-a-time from timed flashes of light, and nowhere does my code include inscrutable bitwise shifts or boolean operations; this is amazing for code readability (and my sanity).

Let’s talk about abstraction. The lowest-level interfaces (short of typing in the memory offsets by hand) are the device-specific “peripheral access crates”, which are automatically generated from manufacturer-provided documentation and expose all device registers in a uniform way.

For example, the code:

device.GPIOA.moder.modify(|_, w| w.moder6().bits(0b10));

means “write binary value 10 to moder6 field of GPIOA’s moder register”. What the heck does that mean? It puts pin A6 into “alternate function” mode, though the crate documentation won’t tell you that (it’s autogenerated).

The upside of this PAC API is that, after you read and understand the 2,000+ page microcontroller reference manual, you fully understand the API.

On the downside, well, aside from being a bit verbose, the PAC API doesn’t prevent mistakes like writing the wrong bit value (0b01, say) or accidentally trying to later use pin A6 for some other purpose.

So the wise Rust folks have written “HAL” (hardware abstraction layer) crates on top of the PACs. Not only does the HAL reify values as enums (Mode::AlternateFunction instead of 0b10), it also leverages Rust’s super-fancy type system to catch mistakes at compile-time. E.g., the function call that sets A6 to alternate function mode, might “consume” (take ownership of) the pin, which means the compiler will error out if you try to use that pin elsewhere.

A lot of this is quite clever and powerful, check out this blog post overview or the embedded rust book.

There are still a few rough edges / things that tripped me up though:

All-in-all, though, I’m still pretty jazzed about the possibilities from using Rust on embedded hardware.

Yak shaves

I keep a running list of yak shaves on my website; the following are just the latest, robot-inspired ones:

Misc. stuff

Until next time my friends!