Rust on a Beaglebone Blue
Beaglebone Blue is an all-in-one linux computer for robotics. Based on the Beaglebone Black with specific integrations for robotics its the perfect dev board. In this post we wrap the extensive C libraries available to make them callable from Rust.
#Generate C Bindings
We need to generate C bindings to librobotcontrol. Thankfully Rust has fantastic tooling to autogenerate FFI C bindings with rust-bindgen. However we are not going to use the library feature as that requires the C libraries to be present at build. Instead we will generate the binding file ahead of time an statically link against the compiled C lib.
Boot up the beaglebone blue and login to a shell (
Git clone librobotcontrol, running the following:
If successful we now have a new file
Now we have bindings lets create the library to link against. Compilation is easy and all setup with the default Beaglebone Blue distribution. Compile with
make. Then create an archive with
NB: librobotcontrol is already complied in your Beaglebone distribution
Copy these files back to your main computer and lets move on to compiling our Rust program against this.
To compile our C libraries in rust we will need a build script.
Placing the file
build.rs in the root of a package will cause Cargo to compile the script and execute it just before building.
With a file layout:
librobotcontrol.a were generated on the Beaglebone Blue.
The build script provides the static link:
main.rs we can now include our new bindings:
Our build target is the AM335x 1GHz ARM® Cortex-A8. To easily compile natively the Rust tools team provides cross. This encapsulates the required environment in docker making cross compilation easy. Install cross and build with:
#Calling back to Rust from C
A core part of the Robotics feature is the IMU_MPU wich includes accelerometers, gyros and barometers. The provided C library has a callback function with the following C signature:
In Rust, bingen translates this to:
To call back into Rust from C code we need to provide a function which compiles to C's ABI. Unfortunately closures aren't able to provide this without a trampoline function. However, the provided C API doesn't allow this trampolining as the arguments aren't passed in the callback. We therefore resort to using global state, breaking the Rust thread safety guarantees. Each function that touches this memory will need to be wrapped in unsafe.
Where the function can be registered with:
As a quick demonstration we can expose these metrics via Promethues. Create a GuageVec for acceleration. On each callback update the guage parameters for all 3 dimensions x, y and z:
NB: MPU acceleration values should be filtered.
We now have our Rust program wrapping an ARM C library enabling easy access to all Beaglebone Blues peripherals!