Introduction

In the previous parts of this series, we explored the benefits of using Rust for embedded systems development and highlighted key crates within the Embedded Rust ecosystem. Now, let's compare the time and effort required to set up a development environment and deploy a simple application to a STM32 Microcontroller using Rust versus the traditional C/C++ approach with vendor-specific software like STM32CubeMX/STM32CubeIDE. This comparison will illustrate the streamlined experience that Rust offers. While vendor-specific software often has advantages in terms of feature-completeness and being tailored to the target platform, this specific case highlight the stark contrast in user experience.

Setting Up and Deploying with Rust

1. Install Rust Toolchain

Time: around 30 s (depending on your internet connection)

The first step in setting up an Embedded Rust environment is to install the Rust toolchain. This process is remarkably straightforward thanks to rustup, Rust's toolchain installer and version manager.

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

This single command installs rustup, rustc (the Rust compiler), cargo (the Rust package manager) and other essential tools. Follow the instructions on the screen and don't forget to restart your shell or source the configuration to have your PATH updated.

2. Install probe-rs

Time: around 10 s (depending on your internet connection)

To copy the binary from the Host PC to the MCU, we will use probe-rs.

The latest installation instructions are found on the tool's website. At the time of writing, probe is installed like this:

curl --proto '=https' --tlsv1.2 -LsSf https://github.com/probe-rs/probe-rs/releases/latest/download/probe-rs-tools-installer.sh | sh

Caution: If you are on Linux you might need to set some udev rules (or use sudo) or you might get permission errors. See the the probe-rs documentation for details on how to do this.

3. Add Target for ARM Cortex-M (optional)

Time: around 30 s (depending on your internet connection)

If you want to go super manual mode and don't have a properly set up git repository, you might need to install the correct target. However, if you have access to a pre-configured project that includes a rust-toolchain.toml file, cargo will automatically make rustup download and install the correct toolchain for you. Easy. Then this step takes 0 seconds, but the download in the later step will take a few seconds depending on your internet connection.

Sometimes this is not the case, unfortunately. Then you need to install it manually, for example:

rustup target add thumbv7em-none-eabi

This command ensures that the Rust compiler can generate code for the ARM Cortex-M architecture.

4. Clone Example Repository

Time: around 5 s (depending on your internet connection)

Clone an example repository that contains the necessary setup and configurations for your microcontroller. For instance, a typical command might be:

git clone https://github.com/Systemscape/stm32f469-disco-blinky
cd stm32f469-disco-blinky

5. Build the Project

Time: around 30 to 90 s (depending on your internet connection, cpu speed and whether the target is already installed)

Navigate to the example directory and build the project using Cargo:

cargo build

# Or for release build (logging may not work as well)
cargo build --release

Cargo will automatically handle dependencies, configuration, and compilation.

6. Flash and Run the Firmware

Time: around 10 s

Finally, use probe-rs to flash the compiled firmware onto the microcontroller.

Using the pre-configured .cargo/config.toml with the custom "runner", we can run the firmware like a normal Rust binary (you could even skip the cargo build step before):

cargo run

# Or for release build (logging may not work as well)
cargo run --release

Again, if no config files are present or you want to go super manual:

probe-rs run target/thumbv7em-none-eabi/debug/stm32f469-disco-blinky --chip STM32F469NIHx

# Or for release build (logging may not work as well)
probe-rs run target/thumbv7em-none-eabi/release/stm32f469-disco-blinky --chip STM32F469NIHx

With these steps, you can quickly set up, build, and deploy an embedded application using Rust. The total time required for all steps was less than five minutes. And if you are onboarding a new developer to your team, this time is all that is required to get them to develop embedded software. From here on they can poke into the code, explore the architecture, make changes, try out stuff and open their first pull request on the new team.

Troubleshooting

Linker cc not found

You might be on a super-minimum OS (maybe Debian?) and not even have a C toolchain installed.

This is usually fixed by installing one:

sudo apt update
sudo apt install build-essential

Flashing fails SwdDpWait

WARN probe_rs::probe::stlink: send_jtag_command 242 failed: SwdDpWait
WARN probe_rs::probe::stlink: got SwdDpWait/SwdApWait, retrying.

Try passing --connect-under-reset to probe-rs or press the Reset button on your board. Sometimes the board's integrated debugger gets stuck...

probe-rs run [...] --connect-under-reset
# or with cargo (the additional `--` passes the command from cargo on to probe-rs)
cargo run -- --connect-under-reset

Setting Up and Deploying with STM32CubeMX

1. Download and Install STM32CubeMX, then download necessary software packages

Time: around 7 min including account creation

First, download STM32CubeMX (about 600 MB) from the STMicroelectronics website and install it.

Well, yeah... no. First create an account and accepted the terms and conditions. Then you download. You can also download as a guest, but this also means entering your name, company and mail address and then getting a download link via mail. Since we need to download multiple software packages, it is actually faster to create an account in this case, as we need to open our mailbox anyway, either for the download or the account confirmation. Also, there has been an obnoxious bug on the ST.com website that causes the donwload button to disappear once you are logged in and it only reappears after 15-30 seconds of inactivity.

Then download the needed software packages for your application/board in STM32CubeMX.

2. Download and install STM32CubeIDE

Time: around 5 min including account creation

Then download STM32CubeIDE as we will need it later (about 1 GB), unpack and install it.

3. Set Up Project in STM32CubeMX

Time: around 6 min

Launch STM32CubeMX and create a new project. Select the target microcontroller (e.g., STM32F469NI), or target board (e.g. DISCO-F469NI), configure the necessary peripherals, and generate the initialization code. This process involves several steps in the graphical interface:

To speed up the process, we will leave all peripherals in their default mode, since we downloaded an example program above as well.

4. Import into IDE

Time: around 6 min when opening for the first time

Import the generated project into an Integrated Development Environment (IDE) such as STM32CubeIDE or an alternative like Keil or IAR. This step may involve:

5. Write Application Code

Time: not considered

Edit the generated code to implement the desired functionality, such as blinking an LED. This involves:

Since we downloaded an example in the above case for the Rust toolchain, we won't consider this step. There are some STM32CubeMX-Compatible examples available in the project configurator, so we assume that one of those was suitable. If we want to add a blinking LED to our project, we can do that, for example like so in the main loop:

HAL_GPIO_TogglePin(LED1_Port, LED1_Pin);   // toggle LED1
HAL_Delay (500);                           // wait 500 ms for each state

We have already explained in other blog posts that application code can be developed faster and better in rust than in C. This blog post is just about comparing the tooling and setup times.

6. Build and Flash

Time: around 3 min when configuring for a new project

Build the project using the IDE's build tools and flash the firmware onto the microcontroller using a compatible programmer/debugger (e.g., ST-Link). This step may involve:

In total, all these steps combined took about 27 minutes, just shy of half an hour, but this a developer with experience with the software that knew which buttons to press. It is easy to see that a less experienced developer could easily waste several hours just setting up the software.

Comparison of Rust and STM32CubeMX

Toolchain Integration

Ease of Setup

Consistency

Developer Experience

Conclusion

The comparison highlights the streamlined and efficient nature of the Rust ecosystem for embedded development. Rust's integrated toolchain, consistency, and modern language features offer a significant advantage over the traditional C/C++ approach with STM32CubeMX. While both approaches can achieve the same end result, Rust simplifies the setup and development process, making it an attractive option for embedded systems engineers.

In this series, we've seen how Rust's safety, concurrency, and performance benefits, combined with a robust ecosystem and efficient setup process, position it as a powerful choice for embedded systems development. As the Embedded Rust community continues to grow and improve, the language's adoption in the embedded domain is likely to increase, bringing modern software engineering practices to a field that demands reliability and efficiency.