Welcome to Embedded Systems Programming with STM32 and Rust
So you want to make hardware do things. Real, physical things — blink an LED, read a temperature sensor, drive a motor, talk to a satellite. You have come to the right place.
This book will take you from zero embedded experience to writing production-quality firmware in Rust on STM32 microcontrollers. No prior knowledge of electronics, embedded systems, or even Rust is assumed. If you can write a for loop in any language, you are ready.
What This Book Covers
We are going to build things. Lots of things. Along the way, you will learn:
- Electronics fundamentals — just enough to not destroy your hardware
- What a microcontroller actually is and how it works at the register level
- The STM32 family — the most popular 32-bit microcontroller platform on the planet
- Rust for embedded — why it is uniquely suited to firmware development
- The Embassy async framework — modern, safe, productive embedded Rust
- Every major peripheral — GPIO, UART, SPI, I2C, Timers, ADC, DAC, DMA, USB, CAN
- Real-world projects — from blinking LEDs to wireless sensor networks
Which STM32 Chips Are Covered?
All of them. Well, almost all of them. This book covers the major STM32 families:
| Category | Families | Cortex-M Core |
|---|---|---|
| Entry-Level | F0, G0, L0 | M0 / M0+ |
| Mainstream | F1, F3, G4 | M3 / M4F |
| Workhorse | F4 | M4F |
| High Performance | F7, H7, H5 | M7 / M33 |
| Ultra-Low-Power | L4, U5 | M4F / M33 |
| Wireless | WB, WL | M4F / M0+ |
The examples primarily target the STM32F411 (a cheap, widely available board called the "Black Pill") and the STM32H743 (a high-performance beast), but every concept translates across the entire lineup. That is the beauty of STM32 — learn one, and you know them all.
Why Rust?
C has been the language of embedded systems for 40 years. It works. But it also gives you:
- Buffer overflows that corrupt memory silently
- Data races in interrupt handlers that happen once a week under a full moon
- Null pointer dereferences that brick a device in the field
- Use-after-free bugs that take months to track down
Rust eliminates entire categories of these bugs at compile time. Not at runtime. Not with a linter. At compile time, before your code ever touches hardware. The compiler is your co-pilot, and it has seen every crash you are about to write.
Why Embassy?
Embassy is an async runtime for embedded Rust. If you have used async/await in JavaScript, Python, or Rust on desktop, the mental model is the same — except Embassy runs on a microcontroller with no operating system, no heap allocation, and no standard library.
Here is what a complete Embassy program looks like:
#![no_std] #![no_main] use embassy_executor::Spawner; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_time::Timer; use defmt_rtt as _; use panic_probe as _; #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); let mut led = Output::new(p.PC13, Level::High, Speed::Low); loop { led.set_high(); Timer::after_millis(500).await; led.set_low(); Timer::after_millis(500).await; } }
That is a fully working blink program. No register manipulation, no unsafe blocks, no 200 lines of boilerplate. Just intent, clearly expressed.
What You Will Need
To follow along with this book, you will need:
- A computer — Windows, macOS, or Linux all work fine
- An STM32 development board — the STM32F411 "Black Pill" (around 300 INR) is our recommended starter board
- A USB cable — to connect the board to your computer
- A debugger/programmer — the ST-Link V2 clone (around 200 INR) or a DAPLink probe
- Basic components — LEDs, resistors, jumper wires, a breadboard (a starter kit for under 500 INR)
- Curiosity — the most important tool in this list
You do not need an electronics degree. You do not need to know what a capacitor does (yet). You do not need to have touched a soldering iron. Chapter 1 covers everything you need to know about electronics to get started safely.
How to Read This Book
This book is designed to be read sequentially. Each chapter builds on the previous one. Resist the urge to skip ahead to the SPI chapter before understanding GPIO — you will just end up coming back.
That said, once you have the fundamentals down (Chapters 1-6), the peripheral chapters (7+) can be read in any order that interests you.
Throughout the book, you will see callout boxes:
Fun Fact: These contain interesting tidbits about hardware, history, or the embedded industry. They are fun. Read them.
Think About It: These pose questions to deepen your understanding. Pause and actually think before reading on. Your future self debugging at 2 AM will thank you.
Warning: These highlight common mistakes that can damage hardware or waste hours of debugging. Read these twice.
What Embassy Gives You Over Bare-Metal
You might wonder why we use a framework at all. Why not just write to registers directly? Here is a comparison of reading a UART byte with and without Embassy:
#![allow(unused)] fn main() { // Without Embassy — manual register polling unsafe { let usart2_sr = 0x4000_4400 as *const u32; let usart2_dr = 0x4000_4404 as *const u32; // Spin-wait until a byte arrives while core::ptr::read_volatile(usart2_sr) & (1 << 5) == 0 {} let byte = core::ptr::read_volatile(usart2_dr) as u8; } // With Embassy — async, safe, readable let mut uart = Uart::new(p.USART2, p.PA3, p.PA2, Irqs, p.DMA1_CH5, p.DMA1_CH6, uart_config); let mut buf = [0u8; 1]; uart.read(&mut buf).await.unwrap(); let byte = buf[0]; }
Embassy provides:
- Async/await — no busy-waiting, no manual interrupt juggling
- Type safety — the compiler ensures you connect the right pins to the right peripherals
- DMA integration — data transfers happen in hardware while your CPU does other work
- Multi-tasking — run multiple concurrent tasks without an RTOS or threading bugs
- Timers and delays —
Timer::after_millis(100).awaitjust works, accurately - Portable — same API across STM32, nRF, RP2040, and more
The Journey Ahead
Here is a roadmap of what lies ahead:
| Chapters | Topic | What You Will Build |
|---|---|---|
| 1-3 | Foundations | Understanding electronics, MCUs, and the STM32 family |
| 4-5 | Setup & First Code | Toolchain setup, your first blink program |
| 6-7 | GPIO & Interrupts | Button inputs, LED control, external interrupts |
| 8-9 | Timers & PWM | Precise timing, LED dimming, servo control |
| 10-11 | UART & Serial | PC communication, GPS modules, debug logging |
| 12-13 | SPI & I2C | Displays, sensors, EEPROMs |
| 14-15 | ADC & DAC | Voltage reading, analog output, signal generation |
| 16-17 | DMA & Performance | Zero-copy transfers, efficient data handling |
| 18-19 | USB & CAN | USB devices, automotive communication |
| 20+ | Advanced Projects | Wireless sensors, motor control, RTOS concepts |
Each chapter follows the same pattern: concept explanation, register-level understanding, Embassy code, and a hands-on project you build and run on real hardware.
A Note on Mistakes
You will make mistakes. You will wire something backwards, forget a pull-up resistor, flash the wrong binary, and stare at a serial terminal printing garbage because your baud rate is off by one digit. This is normal. This is how learning works.
The good news: STM32 chips are surprisingly durable. Short of connecting 12V to a 3.3V pin, they will survive most beginner mistakes. And at 300 INR each, even the worst-case scenario is a cheap lesson.
Fun Fact: Every professional embedded engineer has a drawer of dead microcontrollers somewhere. It is a badge of honor. The only engineer who has never killed a chip is the one who has never built anything.
Let Us Begin
The microcontroller on your desk is a tiny computer. It has a processor, memory, and input/output pins — everything a computer needs. It just happens to run at 3.3 volts, fit on your thumbnail, and cost less than a cup of coffee.
By the end of this book, you will understand every transistor-level detail of how it works. But more importantly, you will be able to build things with it — reliably, safely, and in Rust.
Turn the page. Let us start with the basics.