Skip to content

Embedded Rust - Beginner Core Concepts

Overview

Embedded Rust is a safe, efficient, and modern systems programming language designed for resource-constrained embedded systems. It leverages Rust’s memory safety guarantees, zero-cost abstractions, and strong type system to prevent common errors like null dereferencing and buffer overflows.

This guide covers beginner Embedded Rust concepts, including:
βœ… Why Rust for Embedded Systems?
βœ… Setting up an Embedded Rust Environment
βœ… Basic Program Structure
βœ… Memory Safety & Ownership in Embedded Rust
βœ… GPIO & Hardware Abstraction Layer (HAL)
βœ… Basic Concurrency with RTIC (Real-Time Interrupt-driven Concurrency)


Table of Contents

  1. Introduction to Embedded Rust
  2. Why Use Rust for Embedded Development?
  3. Setting Up the Development Environment
  4. Basic Program Structure in Embedded Rust
  5. Understanding Memory Safety & Ownership
  6. Working with GPIO & Peripherals
  7. Basic Concurrency in Embedded Rust
  8. Tools & Learning Resources

1. Introduction to Embedded Rust

What is Embedded Rust?

πŸ”Ή Embedded Rust is a subset of the Rust language that runs on bare-metal microcontrollers (MCUs) and other embedded devices.
πŸ”Ή It enables memory safety without garbage collection, making it ideal for real-time and safety-critical applications.
πŸ”Ή Rust's strong type system helps catch bugs at compile time, reducing runtime errors.

Common Use Cases

βœ… IoT Devices
βœ… Industrial Automation
βœ… Aerospace & Automotive Systems
βœ… Robotics & Drones


2. Why Use Rust for Embedded Development?

Feature Benefit for Embedded Systems
Memory Safety No null pointer dereferencing, no buffer overflows
Zero-Cost Abstractions No runtime overhead from high-level constructs
Concurrency without Data Races Safe multi-threading and interrupts
No Standard Library (#![no_std]) Runs on bare-metal devices without OS dependencies
Performance Comparable to C Direct control over hardware like C, but safer

3. Setting Up the Development Environment

Required Tools

πŸ”Ή Rust Toolchain (rustup, cargo, rustc) – Install Rust:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
πŸ”Ή cargo-generate – For setting up embedded projects:
cargo install cargo-generate
πŸ”Ή probe-rs – For flashing and debugging:
cargo install probe-rs
πŸ”Ή Target Support for Embedded MCUs – Example for ARM Cortex-M:
rustup target add thumbv7em-none-eabihf


4. Basic Program Structure in Embedded Rust

A minimal "Blinky LED" program using the embedded-hal crate:

#![no_std]
#![no_main]

use cortex_m_rt::entry;
use embedded_hal::digital::v2::OutputPin;
use panic_halt as _; // Panic handler

#[entry]
fn main() -> ! {
    let mut led = ... // Configure GPIO as output
    loop {
        led.set_high().unwrap(); // Turn LED ON
        cortex_m::asm::delay(10_000_000);
        led.set_low().unwrap(); // Turn LED OFF
        cortex_m::asm::delay(10_000_000);
    }
}

βœ” #![no_std] – No standard library, required for bare-metal applications.
βœ” #![no_main] – Disables default main function (MCUs have custom boot logic).
βœ” cortex_m_rt::entry – Marks entry point for embedded Rust programs.


5. Understanding Memory Safety & Ownership

Rust prevents common memory bugs found in C and C++:
βœ… No Null Pointers – Rust enforces explicit handling of Option<T>.
βœ… No Buffer Overflows – Rust enforces safe array indexing.
βœ… No Data Races – Rust’s ownership system prevents concurrent memory corruption.

Ownership Example in Embedded Context

fn configure_led(mut led: gpio::Pin<Output>) {
    led.set_high().unwrap(); // LED ON
} // `led` is dropped here, preventing accidental reuse
βœ” Prevents use-after-free and double free errors.


6. Working with GPIO & Peripherals

use embedded_hal::digital::v2::OutputPin;

fn blink_led(mut led: impl OutputPin) {
    led.set_high().unwrap();
    cortex_m::asm::delay(10_000_000);
    led.set_low().unwrap();
}
βœ” Uses HAL traits for portability across different microcontrollers.

Reading a Button Input

use embedded_hal::digital::v2::InputPin;

fn read_button(button: impl InputPin) -> bool {
    button.is_high().unwrap()
}
βœ” Abstracts hardware details for code reusability.


7. Basic Concurrency in Embedded Rust

Using RTIC (Real-Time Interrupt-driven Concurrency)

RTIC helps manage tasks, interrupts, and resource sharing safely.

Example: LED toggling with an interrupt-driven timer

#[rtic::app(device = stm32f4)]
mod app {
    use rtic::cyccnt::U32Ext;

    #[resources]
    struct Resources {
        led: gpio::Pin<Output>,
    }

    #[task(binds = TIM2, resources = [led])]
    fn timer_interrupt(ctx: timer_interrupt::Context) {
        ctx.resources.led.toggle().unwrap();
    }
}
βœ” RTIC prevents race conditions through compile-time checks.


8. Tools & Learning Resources

Essential Tools for Embedded Rust

πŸ”Ή probe-rs – Flash and debug firmware
πŸ”Ή cargo-embed – Easy embedded development
πŸ”Ή defmt – Efficient logging for microcontrollers

Learning Resources

πŸ“˜ The Embedded Rust Book
πŸ“˜ Rust Embedded HAL Documentation
πŸ“˜ RTIC (Real-Time Interrupt-driven Concurrency)
πŸ“˜ Writing Embedded Rust for ARM Cortex-M


Conclusion

🎯 Key Takeaways
βœ… Rust offers memory safety, concurrency, and high performance for embedded systems.
βœ… #![no_std] enables Rust to run on bare-metal microcontrollers.
βœ… Embedded HAL abstracts hardware, enabling portability across MCUs.
βœ… RTIC provides safe task scheduling and interrupt management.

πŸš€ Next Steps πŸ”Ή Try blinking an LED on an STM32 or ESP32 board. πŸ”Ή Explore embedded Rust projects like drone firmware or sensor integration. πŸ”Ή Learn real-time operating systems (RTOS) in Rust.