
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 and explore the Rust toolchain on Windows, including the compiler, cargo, std, and Clippy, and understand the target triple and tier one targets for ARM microcontrollers.
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.
Learn to create a hello world Rust project with cargo, set up VS Code with Rust extensions, and build and run binary crate using cargo build and cargo run.
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 create local and global static variables in Rust using let, type inference, and explicit casting with as, while exploring primitive data types and mutability.
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.
Learn how Rust’s char uses Unicode scalar values to represent characters as u32 code points, create them with the \u{...} syntax, and safely convert via from_u32.
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.
Learn how Rust constants use const to define compile-time values. Contrast them with static variables that are inlined and do not occupy memory, like peripheral base addresses and gpio pins.
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.
Discover the slice data type, a borrowed view into a data structure like an array, and learn to create, print, and safely access immutable and mutable slices using range expressions.
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 Rust match statement, its pattern matching, exhaustiveness, and catch-all patterns, with practical examples using integers, tuples, and arrays.
Master the if let statement as a shorthand for a single-pattern match, with an optional else block, and use the at operator, underscores, and chained else if let in Rust.
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.
Explain string slices in Rust, including string literal type &str, compare array slices with string slices using & and &mut borrows, and differentiate str from slice and utf-8 indexing.
Define rust functions with the fn keyword, a snake_case name, and an explicit parameter list. Call with parentheses and return values via a final expression or return statement.
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 how to print a Rust struct using println and the debug trait, derive a debug implementation with #[derive(Debug)], and understand when to implement display for custom types and mutability.
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 using an impl block, with self, &self, &mut self, and ownership-taking patterns, including converting to tuples and deriving Copy/Clone.
Define associated functions in Rust and show how they differ from methods using self, then illustrate constructors like new and default via impl blocks with rectangle and point examples.
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 how the result enum enables error handling in Rust, with okay and error variants carrying data, alongside generics T and E, and its prelude presence 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 the question mark operator in Rust, propagating errors and enabling early returns by unwrapping result or option values, with main returning a result or unit and reduced match boilerplate.
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.
Learn native versus cross compilation in embedded rust, configure a host toolchain for a target like armv7-m cortex-m, and build bare-metal no-std projects with cargo --target and target add.
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 to build a bare-metal rust program by adding a panic handler, using no_std with no_main and no_mangle, and generating an ARM elf executable.
Explore the never type in Rust, denoted by the exclamation mark, and see how it signals that panics, infinite loops, or main never return, enabling type inference and 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 how to create and connect a linker script for an embedded rust project, configure cargo to use a linker and pass flags, and place memory sections with memory.ld.
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.
Explain read-only data, initialized data, uninitialized data, and stack and heap in Rust, mapped to flash memory and RAM, with startup code handling dot data and BSS.
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.
Learn how to build a vector table in Rust for STM32 microcontrollers, placing stack pointer, exception and interrupt handlers at flash start, and using a default handler for unused IRQs.
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 ISR vector at the start of flash for ARM Cortex-M, using a custom ISR vector section, keep directive, and provide default handlers to ensure a valid vector table.
Copy the data section from flash to SRAM and zero-initialize the BSS in RAM within the reset handler, using linker-script symbols referenced via an extern block in Rust.
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.
Learn to flash and debug embedded Rust projects on STM32 microcontrollers using VS Code, cortex-debug, GDB servers, and ST-LINK, with setup of toolchains, SVD files, and launch configurations.
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 button interrupt in bare metal Rust using raw pointer access to peripheral registers and a template cargo project with no external dependencies.
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.
Learn how to enable and route external interrupts on STM32 with EXTI and NVIC, map EXTI lines to IRQ numbers, and implement enable_irq and disable_irq in a processor module.
Create an IRQn enum from the vector table, implement from_pin to map pins to IRQ numbers, and manage EXTI interrupts with NVIC enable and clear pending bits.
Test the button interrupt by building in debug mode, pausing at the ISR, and confirming that pressing the user button toggles the LED after correcting the button pin.
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