I wanted to make a Bluetooth widget and I wanted to learn more Rust, so I went about looking what options there are for enabling BLE in an Embedded Rust project.
First off, I could probably have done this on a Raspberry Pi and saved myself a few headaches, but I specifically want to get into “bare-metal embedded”, not “embedded-Linux embedded”.
Hardware-wise, the most well-supported option in the community at the time of writing (fall 2021) seems to be the Nordic nRF52. I already got the nRF52840 devkit to work through the Knurling Sessions, so that’s what I was going to use.
The way Nordic’s BLE chips work is they have a binary blob called the SoftDevice which implements the higher layers of the BLE stack and interfaces to the hardware. It is (presumably) well integrated in the Nordic C SDK, but in order to use it from Rust, we need an interface. After some research, I found three good options and a few worse ones:
The best option that I have found is nrf-softdevice by the Embassy project. Embassy is an async executor for embedded Rust, what that means will have to be the topic of a separate post. What they have done is generate Rust bindings for the Softdevice binary. The project is work-in-progress and consists of several crates in the one git repository linked above, no packets available on crates.io. Being related to Embassy, nrf-softdevice relies on a lot of async/await. If this is not what you want, this is not the solution for you.
Rubble is an entire BLE stack built in Rust, meaning it replaces the Nordic Softdevice entirely. It is being developed on the nRF52 and seems to have an impressive feature set for a community project. Some features are still missing however, and it is not certified for use in commercial products. If the BLE widget I’m building ever turns into something that I would like to sell, I might have to re-write all of the code or pay for having Rubble certified..
3. Nordic SDK
A third option mentioned a lot is to flip things around and include a Rust application binary into a C project using Nordic’s official SDK. One will need to implement whatever glue-code is needed to wire in the Softdevice to the application, but then the rest can be done in Rust. While not as exciting as the other two options, it seems like this is what experienced professional embedded developers are doing to bring Rust into production projects today.
If none of the solutions above sound interesting, one might also interface the Softdevice directly. For this, get a hold of the Nordic SDK and dig in to find the relevant memory locations to call for the various APIs.
Another option that might be worth mentioning is to get a complete BLE module that speaks some form of AT commands over UART. These exist from many different vendors and pushes all the BLE logic out of the application. The downside is one instead has to deal with a lot of string manipulation to handle the AT interface.
Did I miss any?