
Kick off your embedded Rust journey with a quick Rust fundamentals intro, covering variables, statics, arrays, references and ownership, match statements, strings, enums, options, and result types.
Install and configure the Rust host toolchain with rustup, including rustc, cargo, and clippy, then set up a hello world project in VSCode and manage toolchains with rustup commands.
Install the default toolchain on windows and review rustc, cargo, std, and clippy, then understand the target triple and tier one targets for native and embedded ARM workloads.
Install VS Code as the main IDE for this course, and use the online Rust Playground for small exercises. Then apply the recommended extensions to write and compile Rust code.
Install the Rust toolchain, create a hello world project with cargo, and set up VS Code with Rust Analyzer to build, run, and understand binary crates.
Discover how rustfmt formats code to a consistent style, use clippy for static analysis and idiomatic improvements, and apply fixes with cargo fix to upgrade code.
Learn how Rust ensures memory safety unlike C/C++, using ownership, the borrow checker, and the option type to prevent null pointers and use-after-free.
Learn how to declare variables in Rust with let and mut, create local and static variables, rely on type inference and snake_case, and perform explicit casting with as.
Understand byte literals in Rust by initializing a u8 with ascii codes using the b prefix, and cast to cat or char to view the corresponding character.
Explore the char data type in Rust and Unicode scalar values. Create chars with the slash u syntax and convert u32 to char using from_u32; some values are invalid.
Learn how Rust uses static for immutable and mutable globals, why mutating statics requires unsafe blocks, and that immutable statics live in ROM while mutable ones in RAM.
Define compile-time constants with the const keyword in Rust, distinguish them from static variables, and learn how inlining uses constants for GPIO and peripheral addresses in embedded programming.
Explore how Rust prevents buffer overflows and ensures memory safety through runtime bounds checks, panic handling, and automatic memory management with ownership, drop, and smart pointers.
Master Rust arrays, fixed-size collections with optional type inference and explicit sizing. Use initialization, including value-and-repeat syntax, indexing, mutability, and for loops to sum elements, plus printing with debug formatting.
Explore Rust references, including ampersand forms like ref and ref mut, and how the borrow checker enforces one mutable or multiple immutable references, with lifetimes in safe Rust.
Explore the borrow operation in Rust, distinguishing immutable borrows from mutable borrows, using ampersand and ampersand mut, and why only one mutable borrow is allowed at a time.
Explore the slice data type in Rust as a borrowed, contiguous view into arrays, vectors, or strings, created with range expressions and a fat pointer.
Master Rust decision making with if, else, and match, using if blocks as expressions to return values, and distinguishing statements from expressions for clean, expressive code.
Explore the match statement in Rust, including exhaustive patterns, arms, and defaults, with examples on tuples and u8 values, and learn the matches macro for boolean results.
Use if let as a pattern match, pattern = value, with tuples and range checks via the at operator; ignore components with underscores and chain else if let.
Discover how Rust enforces memory safety by preventing data races with ownership and the borrow checker, avoiding dangling references, and enforcing explicit type checks over implicit casts.
Compare values using Rust's equality, inequality, and ordering operators to produce boolean results. Learn partial eq and partial ordering govern operators, and how logical not, and, or work on booleans.
Explore bitwise operators in Rust, including not, and, or, and left and right shifts. Understand unsigned versus signed shifts and how traits enable bitwise operations on custom types.
Explore the string data type in Rust, comparing string literals and heap-allocated strings, and learn how ownership, mutability, and common methods like new, from, and clone work.
Explore how Rust stores strings as utf eight encoded data by default, enabling Unicode text and emoji, while ASCII characters use one byte and non-ascii characters may take multiple bytes.
Explore slices of arrays and strings, learn the difference between string slices (&str) and slice types, and how utf-8 encoding impacts mutability and indexing.
Learn to define rust functions with fn, declare explicit parameter types in snake_case, and return values using return or expression, while exploring module visibility and no overloading.
Learn to create a custom data type in Rust with structs, including named, tuple, and unit structs, by explicitly annotating each member field and initializing instances.
Learn to print a Rust struct by using the derive attribute to implement the debug trait for println! output, and understand how mut makes a struct’s fields mutable.
Learn how to use Rust's default trait to initialize struct fields with default values, derive Default for automatic implementation, and selectively override fields with the .. syntax.
Pass a struct by reference using a mutable borrow and access its fields with the dot operator, no dereferencing needed, while updating a person's age and hinting at structure methods.
Learn to implement methods and associated functions for a Rust struct within an impl block, using self for borrows, and explore distance from origin, translate, and ownership.
Learn how associated functions act as constructors on a struct, called with the scope resolution operator, differentiate from methods using self, and implement new or default constructors for rectangles.
Learn how Rust enums define state variants with associated data, create enum instances, and use match pattern matching to safely handle car status and game states.
Learn to implement methods and associated functions for an enum shape with circle, rectangle, and square variants, and compute area accordingly.
Explore rust's option type, an enum with some and none variants that carry data of type T, enabling handling of absent values and safer code via pattern matching.
Explore the result enum for error handling in Rust, its okay and error variants with generic data types T and E, and how prelude allows using them without imports.
learn rust error handling using the result and option enums, unwrap and expect, panic macros for unrecoverable errors, and the question mark operator for error propagation in recoverable error management.
Master how the question mark operator propagates errors and performs early returns by unwrapping result or option values. It simplifies error handling and avoids verbose matches.
Learn how generics in Rust enable reusable code across types while preserving type safety. Explore generic functions, trait bounds, references, and monomorphization to write concise, efficient code.
Explore embedded hardware for practice using the fastbit stm32 nano board with an stm32 f3 cortex-m4, mpu6050 sensor, and a touch lcd shield while allowing use of other stm32 boards.
Differentiate native and cross compilation, then configure cargo build for a target such as armv7 m, using rustup target list and target add, and apply no_std for bare-metal development.
Explore bare-metal rust with no_std in VSCode, create an infinite loop, and use built-in attributes to control linting and tests while preparing a microcontroller build.
Learn how to build a no_std bare-metal Rust program, implement a panic handler, use no_main and no_mangle, and generate an ARM elf executable for embedded targets.
Explore rust's never type, marked by an exclamation mark, and learn how it signals functions that never return, enabling smarter type inference, coercion, and control flow analysis.
Explore the ELF executable format and inspect its headers, program headers, and sections using cargo-binutils, including text, data, symbol tables, and linker scripts for bare metal embedded projects.
Learn to write startup code for a microcontroller in Rust, defining the vector table, reset handler, and exception handlers, copying data to RAM, zeroing BSS, and calling main.
Explore the linker script and linker configuration to control memory layout for a Rust bare-metal project, pass linker flags with cargo build, and specify memory.ld via config.toml.
Write a linker script from scratch to define memory regions and map sections to outputs using memory and sections commands for flash, ram, core coupled memory, and battery backed ram.
Explore the different types of data in a program to understand linker script section placements, including read-only data (rodata) in flash, dot data, dot bss, and stack and heap.
Learn to use the linker script sections command to place dot txt, dot data, and bs sections in flash and ram, with load and execution addresses guided by Elf file.
Use the linker location counter to define data and bss boundaries and relocate data from flash to ram, aligning sections to four and eight byte boundaries. Set up initial stack pointer in ram, reserve stack and heap, declare the entry and reset handler with no mangling, and inspect the elf with read obj and object dump.
Define a vector table in startup code to map exception handlers and interrupt service routines, placed at the start of flash with the initial stack pointer as first entry.
Learn to fix Rust startup code by declaring external C functions, using extern "C" for the vector table's interrupt handlers, and aligning with the C ABI and ARM EABI.
Place the interrupt vector table at the start of flash for ARM Cortex-M by creating an ISR_vector section, using keep in the linker script to retain it.
Use the reset handler to copy the data section from flash to ram, zero the bss in ram, and wire linker-script symbols via extern blocks to prepare the program environment.
Explore raw pointers in Rust, including mutable and immutable C-like pointers, and learn why unsafe blocks govern their manipulation in safe versus unsafe Rust.
Implement a reset handler that copies the data section from flash to RAM and zeroes the BSS, using address_of and address_of_mut, raw pointers, and unsafe blocks, then calls main.
Flash the ELF to the target hardware and debug your Rust code using stlink or jlink with stm32cube clt and cortex-debug in vscode.
Configure VS Code to flash and debug embedded Rust using STM32CubeCLT tools, the GDB server, and ST-Link, by setting the ARM toolchain path, SVD files, and Cortex-Debug launch settings.
Learn to flash an ELF file onto embedded hardware using probe-rs tools, via cargo flash or cargo run with a probe-rs runner, including installation and chip selection.
Toggle the development board LEDs with a bare metal rust program using a button interrupt and raw pointer access to peripheral registers, using cargo generate and a linker script.
Implement the exercise by creating led and button modules, wiring up led init, on/off, and interrupt handling, and exposing functions via pub for use in main.rs.
Learn how to resolve function scope by using module paths and use statements, organize embedded rust code with board and mcu modules, and declare public constants for GPIO pins.
Implement led_init by configuring the GPIO pin as output using the GPIO port mode register, and perform volatile reads and writes with core::ptr read_volatile and write_volatile inside unsafe blocks.
Configure the GPIO output mode by calculating the bit position from the pin, applying a mode mask, and updating the register using clear_bits and set_bits helpers.
Refactor the LED module by moving GPIO code to a dedicated GPIO module and reg manipulation to reg, implement GPIO pin state with high, low, and toggle.
Enable the GPIO clock via RCC registers, derive RCC and GPIO base addresses, and set the 17th and 18th bits in RCC HBNR to activate GPIO A and B.
Configure the fast bit sdm32 nano board with blue, green, and red leds on gpio port a pins 1, 2, and 3, then run debugging to verify they light.
Compare unsafe blocks and unsafe functions in Rust, learn when to use each, and see how safe functions encapsulate unsafe code, plus basics of FFI and extern with raw pointers.
Explore rust's regular and documentation comments and how rustdoc and cargo doc generate HTML docs from item-level outer comments, with practical examples for led_init and gpio modules.
Explore inner documentation comments for modules and crates, explain outer documentation at the top of a module or crate, and show how cargo doc renders the crate front page.
Implement the button module with a public button_init that configures the port and pin in either input or interrupt mode, using Mode and Trigger enums.
Explore how gpio interrupts are delivered to the stm32 processor via the exti controller. Configure edge triggers with rising and falling registers and organize exti gpio modules for masking.
Implement the set_edge function to configure EXTI edge detection using the EXTI registers, map pins to EXTI lines with ExtiLine::from_pin, and enable interrupt delivery via the IMR1/IMR2 mask registers.
Demonstrates implementing enable_interrupt and disable_interrupt in Rust by using configure_interrupt, modifying IMR1/IMR2 via EXTI line numbers, and configuring SYSCFG EXTICR registers for correct GPIO-to-EXTI mapping.
Enable exti interrupts via the nvic by enabling the corresponding irq numbers; use iser/icer registers to control enablement and understand hardware priority (lower number means higher priority).
Create an IRQ enum in the MCU module that maps vector table entries to STM32 IRQ numbers. Implement a from_pin function to return the correct IRQ for a given pin.
Test the button interrupt implementation by debugging the ISR and observing LED toggling on user button presses. Fix the button pin reference and retest to ensure reliable interrupts.
Discover how external crates integrate into embedded Rust projects with cargo and crates.io, distinguish library and binary crates, and understand package structure with cargo.toml, main.rs, and lib.rs.
Explore essential Rust crates for embedded systems, from cortex-m and cortex-m-runtime to cortex-m-rtic and embedded-hal, hardware abstraction layers, and peripherals like mpu6050, with FFI basics, RTIC, embassy, and defmt logging.
Initialize the SysTick timer for 500 milliseconds, toggle an LED in its interrupt handler using Cortex-M crates and the Cortex-M runtime, and create a new project with cargo generate.
Learn to bootstrap a Cortex-M microcontroller with the cortex-m-rt crate, including startup code, vector table setup, entry and exception attributes, pre-init steps, stack initialization, and linker script integration.
Understand Rust semantic versioning for crates, including major, minor, and patch changes, and learn cargo version specifiers such as =, ^, ~, and range expressions to manage dependencies.
Explore panic handling crates for embedded Rust, including panic-halt and panic-itm, and configure a global panic handler. Learn to integrate cortex-m-rt and cortex-m for SysTick handling and ITM logging.
Learn to generate 500 ms SysTick interrupts on STM32 using the cortex-m crate, leveraging the singleton Peripherals pattern, take(), and reload value calculation for reliable timing.
Learn how to test a SysTick-based 500 ms time base on STM32 by using the cortex_m_rt linker script, memory.x, and cargo debugging to verify led toggling.
Create a Rust program that periodically sends debug messages over the ITM peripheral of Cortex-Mx, using the SysTick interrupt and the Cortex-M crate; initialize a Cargo generate project named itm_prints.
Explore instrumentation trace macrocell (itm) for sending log messages over swo via tpiu and itm stimulus ports on cortex-m processors; configure acpr baud rate and enable swo protocol.
Initialize ITM for SWO output by configuring the TPIU_ACPR prescaler, enabling trace, enabling stimulus port 0, and logging with iprintln in main and systick.
Refactor the code into a common itm_debug module with a public itm_print to centralize itm output and demonstrate flashing, itm tracing, and synchronization to avoid data races.
Mastering embedded Rust teaches how to handle shared hardware concurrency with critical sections and atomic types, preventing race conditions when interrupts and main code access peripherals like ITM.
Replace static mut with a static Mutex<RefCell<Option<Peripherals>>> to achieve interrupt-safe, race-free access via interior mutability and borrow semantics.
Learn how Rust interoperates with C through the foreign function interface, using extern \"C\", unsafe blocks, and precise type mapping to call and be called by C code.
Map types between rust and c using the standard ffi type aliases in std::ffi or core::ffi, such as c_int, to prevent memory-layout and calling-convention errors and avoid guessing.
Explains how Rust maps C void and void pointers via ffi c_void, showing mutable and const pointers, unsafe interop, and memory management in C and Rust.
Explains handling C void and void pointers in Rust via FFI, including mut pointers, casting to i32, null checks, and memory management with a free function.
Compare rust’s string representations with c strings, including null-terminated arrays, utf-8 requirements, and parsing between c and rust string types.
Discover how Rust safely accepts C strings via core::ffi::CStr, validates UTF-8, and converts to Rust strings or string slices with to_str and to_string, using unsafe blocks when calling into C.
Rust passes strings to C by converting them to std::ffi::CString, ensuring UTF-8 validity, null termination, and no interior nulls, then uses as_ptr to hand a const char pointer.
Explore how rust and c differ in struct memory layouts when passing struct values across FFI, including field ordering, padding, and size, and learn to use repr(C) to enforce layout.
Learn to pass a Rust struct to C by reference or by value, using raw pointers, repr(C), and layout matching, with heap-field handling via C strings and a print_item example.
==> Looking to add another embedded programming language to your arsenal? Give Rust a shot — you won’t be disappointed!! <==
This course is your starting point for using Rust on ARM Cortex Mx based microcontrollers such as STM32, even if you are new to embedded systems.
This is a fully hands on course that takes you from scratch into real world embedded Rust development on STM32. Each video builds on the previous, helping you progress step-by-step, from writing bare metal code to advanced topics like FFI, crates, driver development, and traits. Ideal for those new to Rust and embedded systems.
Why Rust for embedded?
Rust prevents many common memory issues (like null pointer dereferencing, buffer overflows, and use-after-free errors) through:
Ownership model: Rust’s strict rules around ownership, borrowing, and lifetimes prevent most accidental memory misuse.
Type safety: Rust’s type system ensures that you access data correctly and helps prevent certain types of invalid memory access by enforcing strict variable usage patterns.
Thanks to Cargo, Rust’s official package manager, you can easily add features by simply including external libraries, or "crates," which are like plug-and-play components.
What will you learn?
Here’s what you will master in this course:
A beginner-friendly introduction to Rust, tailored for embedded systems
Complete toolchain setup for cross-compiling, flashing, and debugging
Debugging and logging with defmt and probe-rs for real-time tracing
High-level peripheral programming with STM32 HAL crate
Step-by-step, build a real-world Flappy Bird game application using Rust
Interface with real sensors (MPU6050) to control game mechanics
Clean, modular coding practices and hardware abstraction
Build hardware-agnostic drivers using the embedded-hal traits
Writing and understanding your own linker scripts and startup code
Building generic embedded code using Rust generics and traits
Safe and seamless Rust + C integration through FFI
Confidence to write robust, reusable, and production-grade embedded firmware in Rust
Hardware Requirements
Note: If you already have a microcontroller development board, we recommend continuing with it. This course is designed with such thoroughness that the concepts and steps can be applied to most development boards though some minor adjustments may be needed. But, if you prefer to use the exact same board as the instructor for a smoother experience, you can check out the recommended hardware
1) Microcontroller development board
Option-1. STM32F303-Based Board
The course primarily utilizes Fastbit STM32 Nano board which is based on the STM32F303 microcontroller and onboard MPU6050 sensor.
Option 2. Any STM32 Microcontroller Board
You can use any development board featuring an STM32 microcontroller. The course content is designed to be adaptable, allowing you to follow along with the specific STM32 board you have available.
2) SWD-Based Debugger
An SWD (Serial Wire Debug) based debugger is required for programming and debugging your STM32 microcontroller. This tool is essential for loading your programs onto the microcontroller and for debugging your projects in real-time.
3) LCD shield
In one of the projects, you will need a TFT LCD module for experimentation. This course uses the Fastbit 1.28" TFT LCD with an 8-bit parallel interface, based on the GC9A01 LCD driver. However, you are free to use the same or a similar compatible module
4) MPU6050 sensor
Software requirements
VS Code
STM32CubeIDE