Tag Archives: probe run

Embedded Rust Toolchains

I recently started learning Embedded Rust. As I mentioned at the top of the last post, there are a couple of toolchain options:

  • OpenOCD + GDB
  • probe-rs + cargo-embed

Turns out there is also a third one that I just discovered:

  • probe-rs + probe-run

This was a little confusing at first, I was unsure what was the better option and how these projects all connect to each other. Embedded Rust seems to be moving fast, so this might get outdated but here is a basic summary if you are just getting into Embedded Rust as well.

The Embedded Rust way

Rust-embedded is a working group in the official Rust organization. Among other things, they maintain The Embedded Rust Book, which you may have come across. In the book, they describe what I would call the “official” toolchain, using OpenOCD and ARM-compatible GDB (gdb-multiarch). This is probably the way to go if you need to do serious development today. OpenOCD and GDB are stable and mature projects and for a Rust programmer, it is the most fully-featured and reliable option right now.

Enter probe-rs

As an alternative to those external (non-Rust) dependencies, a team has formed an ambitious project around replacing it all with software written in Rust – probe-rs. Here is an illustration (borrowed from the video below):

This illustration shows nicely what probe-rs tries to be.

Here is a very informative talk by one of the people behind probe-rs:

My major learning from this talk was that probe-rs is really a library. Other projects, like cargo-embed and probe-run are built on top.


The probe-rs team built cargo-embed to show off the capabilities of probe-rs. As such, it was the first tool I came across when I found the official probe-rs website. One might imagine that being built by the same team, cargo-embed will stay closer to the latest features of probe-rs and have a shorter path to get new features in. But this is just speculation.

To build and upload programs, you simply run cargo embed --release (see my last post about why --release is important for timing). It possible to do logging with rtt, a debugger-based thing that uses an internal buffer that gets read out by the debugger instead of, for example, printing over UART. Debugging is also supported (but not at the same time as rtt currently) from the command line, or visually in something like Visual Studio Code, by hooking into the GDB stubs that probe-rs provides. This is an interface that (to the best of my understanding) mimics GDB’s, but actually goes directly to probe-rs.

Configuration is done in a new file called Embed.toml. There you configure what chip you are using and whether to use rtt or GDB debugging, or set up separate profiles for each.

The vision of probe-rs is to offer a full development environment for embedded Rust, so they are also working on a VSCode plugin. It is still in alpha, and I have not tried it yet.


Ferrous Systems is a company that pops up everywhere in embedded Rust. They are a consultancy specializing in Rust for embedded applications and are also very active in open source development for embedded Rust. They started a project called Knurling dedicated to improving the experience working with embedded Rust.

Knurling has many sub-projects and probe-run is one of them. Built on top of probe-rs, it gives you the same features as cargo-embed, but in a slightly different packaging. The philosophy is that embedded development should work the same way as native development, so instead of introducing a new cargo command, probe-run is a so called Cargo runner. This means you configure the “usual” cargo run command to use probe-run under the hood. And there is no new configuration file to keep track of, just the regular Cargo.toml and .cargo/config.toml. Does it matter? Up to you.

Knurling also has an interesting logging framework; defmt. Instead of doing string formatting on the embedded device, it relies on a tool on the host side and simply generates a list of strings at compile time that is kept on the host. The embedded device then simply sends the index into that list (using rtt), causing much less overhead.

I do like the idea of keeping the main Rust interface unchanged, which speaks in favor of probe-run, but I’m not sure about plans for integrating probe-run with VSCode, or debugging with breakpoints. As I learn more, I hope to find a favorite and maybe also start contributing myself.