Rust on embedded – running your code

All tooled up, raring to go? The next step for the embryonic Rustacian is the blinky….

The setup

Both the code and the tools used, largely depends on the choice of development board you have available. This page is using the following boards which are cheap, functional and very available:

  • as a loading and debugging tool: a STM32f103c8 with the Black Magic Debug Probe software loaded (Instructions for putting BMP firmware onto boards are in the readme in that git repository)
  • as a target board: the same type of STM32f103c8.

The STM32F103c8 comes on a variety of carrier boards, we’re using the Blue Pill board which is available from the usual online purveyors of tat.  If you start using the BMP debugger on a regular basis then you should consider buying one from the primary vendor, both to support the project and ‘cos it’ll be a hell of a lot more reliable than a bluepill board of dubious pedigree.

Anyway, once you’ve got through that bit, the bobbin tool takes away most, but not all, of the pain when dealing with embedded development in Rust.

Let’s get blinky up and running

The embedded version of “Hello, World” is blinky. Just blink a LED to show that your code runs.

Clone the repo

Using your favourite tool, clone or download the following repo:

git clone
Override the default compiler

Enter the stm32f103xx directory and enter: rustup override set nightly to make this directory use the nightly compiler.

Override the default scan method used by bobbin

By default bobbin selects JTAG as the default way to communicate with the development board. My setup uses the [tt]swdp_scan[/tt] method.

Switch to the ‘config-for-stm32-blackmagic-setup’ branch:

git checkout config-for-stm32-blackmagic-setup

Or  a file .bobbin/config with the following content:


And then run bobbin run --bin stm32f103xx to compile, link, load and run the program.

 Compiling stm32f103xx v0.1.0 (file:///home/erwin/dev/github/bobbin-blinky/stm32f103xx)
 Finished dev [optimized + debuginfo] target(s) in 0.13 secs
 text data bss dec hex filename
 162 0 4 166 a6 target/thumbv7m-none-eabi/debug/stm32f103xx
 Loading target/thumbv7m-none-eabi/debug/stm32f103xx
 Complete Successfully flashed device
 Loader Load Complete
 Console Opening Console

To debug use bobbin gdb --bin stm32f103xx.

Smile, and bask in the reflected glory of your red flashing LED (or possibly green, consistency doesn’t seem to be a strong point on these Bluepill boards).

Rust on embedded – getting the tools

Rust has at least an evens chance of being the future programming language of small embedded systems. If it’s in your future, shouldn’t you at least know about it?

This series of blog posts will take you through the steps needed to install a toolset for embedded development on Cortex-M boards and create a simple application using Rust. But first let’s look at the basic question: why?

Rust and its ecosystem

Rust as a programming language makes claims that are unheard of in our industry. Combining native compiled code with strong claims about memory safety and preventing data races is unique. But that is not enough to make a programming language productive for software development in general and embedded development in particular. To be productive you need a toolset that makes the entire edit-compile-run/debug cycle efficient.

Nowadays that means:

  • a compiler with meaningful error messages,
  • an IDE with syntax-highlighting, completion, re-factoring and integrated debugging,
  • a built tool that knows how to build the mostly used targets by default,
  • support for unit-testing, continuous integration and benchmarking,
  • a dependency manager that makes it easy to integrate existing libraries into your code base
  • and an ecosystem with a huge amount of open source libraries.

Today (November 2017) Rust can check 5 of the 6 bullets points above. For people coming from a background in programming languages like Java and C# the IDE support will still feel like we are living in the early 90s. Today, IDE’s like Visual Studio Code and IntelliJ IDEA (community edition) provide Rust plugins with basic completion, re-factoring and debugging support. The Rust community has recognized that ‘great IDE support’ is necessary for adoption of the language and is tracking its progress on

Setting up the tools

This post is going to walk you through the necessary steps to get all the tools up and running. This post assumes a real operating system like Linux or OS X.

Step 1: Getting stuff to compile

In the edit-compile-run/debug cycle we will first focus on ‘compile’.


First: the rust toolchain manager, rustup. Go to and install rustup. This will install a series of tools:

  • rustup, the toolchain manager,
  • the default stable compiler for your platform,
  • the built-tool cargo,
  • the API documentation and
  • the sources for the standard libraries

Check your installation with the rustup show command. My output is:

Default host: x86_64-apple-darwin

stable-x86_64-apple-darwin (default)
rustc 1.22.1 (05e2e1c41 2017-11-22)

The nightly compiler and Rust sources

For embedded development, you currently need features that are not yet stabilised and are therefore not available in the stable compiler. Execute rustup toolchain add nightly to add the nightly compiler and rustup component add rust-src to add the Rust sources. Again, use rustup show to check your installation:

Default host: x86_64-apple-darwin

installed toolchains


active toolchain

stable-x86_64-apple-darwin (default)
rustc 1.22.1 (05e2e1c41 2017-11-22)

and rustup component list to check the installed components:

>>SNIP<< rust-docs-x86_64-apple-darwin (default) rust-src (installed) >>SNIP<<

Staying up to date

Keeping your compilers up to date is simple: rustup update and wait. You can pick (and pin) specific versions of the compiler using rustup. Example: rustup toolchain add 1.20.

Installing the ARM gcc toolchain

Rust’s compiler will use the platform native linker to link to final binaries. For ARM on embedded that is arm-none-eabi-ld. Head to and install the latest ARM toolchain for your platform. Making the binaries available in your $PATH makes life a lot easier.

Cross-compiling using Xargo

Xargo is a cross-compiling tool for Rust. Xargo is a wrapper around cargo that handles compiling Rust’s core libraries for your target. Installing Xargo is as simple as executing cargo install xargo in your shell.

Step 2: Running your code on the board

Getting your code to run on the board, means getting it on the board first. This post assumes that you are using the BlackMagicProbe/Bluepill to connect to your target board. If that is the case you need to tell the debugger, arm-none-eabi-gdb to use the BlackMagicProbe using the USB-serial connection. That means, entering this at the GDB prompt:

target extended-remote /dev/ENTER_YOUR_DEVICE_HERE


There is a tool available that scans your system for available debuggers. It is called bobbin-cli and can simply be installed using Rust’s build tool cargo:
cargo install bobbin-cli. This will download, compile and install bobbin-cli using your default compiler.

Bobbin needs dfu-util to work, so on Linux do sudo apt install dfu-util (or equivalent), on a Mac do: brew install dfu-util or equivalent.  Run bobbin check to see what tools it finds. My output:

 Bobbin 0.8.0
 Rust 1.23.0-nightly (246a6d19c 2017-11-23)
 Cargo 0.24.0-nightly (abd137ad1 2017-11-12)
 Xargo 0.3.8
 GCC 6.3.1 20170215 (release) [ARM/embedded-6-branch revision 245512]
 OpenOCD Not Found
 JLink Not Found
 Bossa Not Found
 Teensy Not Found
 dfu-util 0.9

Finding your board

Now connect your debugger board and execute: bobbin list. On my system this shows:

ID        VID:PID  Vendor / Product                         Serial Number           
2700e45b 1d50:6018 Black Sphere Technologies / Black Magic Probe (Bluepill), (Firmware v1.6-rc0-338-g798d883) C4D6A8F0

bobbin info shows on what device the debugger ports are available:

ID               2700e45bdf2fa10f6dbe1cb90d3894c8679762f2
Vendor ID        1d50
Product ID       6018
Vendor           Black Sphere Technologies
Product          Black Magic Probe (Bluepill), (Firmware v1.6-rc0-338-g798d883)
Serial Number    C4D6A8F0
Type             BlackMagicProbe
Loader Type      blackmagic
Debugger Type    blackmagic
CDC Device       /dev/cu.usbmodemC4D6A8F3
GDB Device       /dev/cu.usbmodemC4D6A8F1

Unsurprisingly, to debug we use the GDB Device.

In the next post we will be creating some example code and running it.