Close

BJR_LOG_17 Software Overview

A project log for BJR_019

The famous ball balancing platform reimagined

tamas-feherTamas Feher 08/22/2025 at 09:390 Comments

Running the BJR on Rust

Inside the BJR_019 lives an STM32F4 microcontroller, orchestrating motion control, sensing, and communication. Every line of firmware on that chip and the supporting software that interacts with it was written in Rust.

The decision was deliberate. I believe the Rust ecosystem is mature enough to get more widespread in the embedded community. With this project I wanted to test my assumptions.

Impressions of using Rust in embedded systems

Debugging bare-metal embedded systems is always harder. Crashes rarely announce themselves with helpful stack traces. Most often, things just stop working.

With Rust, many memory-related bugs are caught before they ever touch the hardware. The compiler enforces safety rules that can feel annoyingly strict at times. In retrospective those same rules have saved me from introducing hard-to-find bugs. 

Using a different language doesn't make bugs magically disappear, but even if it fails, debugging has been much simpler. 

Took a little while until I got used to the HAL's design patterns,  but once I got a hold of it, I could apply it to all the other projects in rust. It also gave birth to my own higher level abstraction (BAL, the Board Abstraction Layer) that I'll introduce in a later log. 

What's included?

The BJR_019 project isn’t just firmware. Alongside the embedded code, I wrote a telemetry visualization tool, also in Rust.

This small visualizer enabled me to chisel out control related issues with minimal data processing. I could see what's happening to the robot in real time.

Because both the firmware and the visualization software shared the same language, I could rapidly iterate on the communication protocol. Adding a new telemetry field was as simple as tweaking a few lines in one package That tight integration was a huge win for speed.

Programming and communicating with the BJR_019

One of the standout parts of this journey has been using probe-rs. an incredible toolkit for embedded Rust. With a single-line installation, I gained:

If you're unfamiliar with Segger's Real Time Transfer (RTT) feature, you're in for a treat! Check it out here.

Calling

cargo embed --bin bjr_app --release

 Will do a couple of things:

  1. Build and flash the specified program
  2. Opens 3 RTT channels:
    1. The log outputs from the device
    2. the terminal interface for sending commands
    3. a binary stream of telemetry data, piped into a web socket. the telemetry visualizer connects to this socket and displays the measurement data real time. 

To configure this, I added a simple Embed.toml file:

[default.general]
chip = "STM32F446RETx"

[default.rtt]
enabled = true
up_channels = [
    { channel = 0, mode = "NoBlockSkip", format = "String" },
    { channel = 1, mode = "NoBlockSkip", format = "String"},
    { channel = 2, mode = "NoBlockSkip", format = "BinaryLE", socket = "127.0.0.1:8080" },
]
tabs = [
    { up_channel = 0, name = "Log" },
    { up_channel = 1, name = "Terminal" },
]

You set it once and forget it.

Why This Matters

For me, rapid iteration is the name of the game. The faster I can flash, debug, and visualize, the faster I can tune the robot and push it forward.

Rust, combined with probe-rs, RTT and the visualizer, gave me a workflow that was fast, productive and genuinely enjoyable. It reduced friction, allowing me to focus less on plumbing and more on experimenting with the robot itself.

Discussions