Rust on Embedded
Didacta Workshop
STM32F103C8 Bluepill + ST-Link + Rust
Workshop Goal
- Understand core Rust basics
- Build and flash no_std firmware
- Blink LED, read button, read analog input
- See output with probe-rs RTT logs
Flow (60 min)
- 00 setup, 01-03 Rust basics
- 04 first embedded app
- 05 LED blink
- 06 button input
- 07 analog readout
- 08 final combined app
How We Work
- Edit only
task/ folders
- Use
solution/ only when stuck
- One step at a time, linear slides
Step 00 Task: Setup Live
Next task: Prepare host + probe tools.
Learn: Embedded Rust toolchain basics.
bash scripts/setup-live.sh
bash scripts/verify-host.sh
bash scripts/verify-probe.sh
Step 00 Solution
rustup, cargo, probe-rs available
- Target
thumbv7m-none-eabi installed
probe-rs list sees ST-Link
probe-rs chip list contains STM32F103C8
Step 01 Task: Rust Hello
Next task: Edit the TODO values and run.
Learn: fn main, variables, println!.
bash scripts/run-step.sh 01 task
Step 01 Solution
- Program compiles and runs with
cargo run
- Console output shows your name and workshop title
- You can navigate the task/solution structure
Step 02 Task: Types + Control Flow
Next task: Tune threshold logic in classify_adc.
Learn: explicit types, if, match.
bash scripts/run-step.sh 02 task
Step 02 Solution
- ADC values map to low/medium/high
- Button states map to pressed/released
- Program output changes correctly with input values
Step 03 Task: Ownership + Borrowing
Next task: Complete/update state mutation functions.
Learn: &T vs &mut T, safe mutation design.
bash scripts/run-step.sh 03 task
Step 03 Solution
- State is read via immutable reference
- State changes happen through mutable references
- Compiler enforces safe access patterns
Step 04 Task: Embedded no_std Hello
Next task: Flash first no_std firmware and watch RTT logs.
Learn: #![no_std], #![no_main], #[entry].
bash scripts/run-step.sh 04 task
Step 04 Solution
- Firmware flashes to Bluepill
- RTT shows boot + alive messages
- You now have a working embedded Rust baseline
Step 05 Task: LED Blink
Next task: Toggle PC13 in a timed loop.
Learn: HAL GPIO output + active-low LED behavior.
bash scripts/run-step.sh 05 task
Step 05 Solution
- PC13 low = LED on, PC13 high = LED off
- Timer loop controls blink frequency
- RTT logs match LED state changes
Step 06 Task: Button Input
Next task: Read PA0 with pull-up and detect transitions.
Learn: digital input polling + simple edge detection.
bash scripts/run-step.sh 06 task
Step 06 Solution
- Pressed/released events are logged correctly
- No repeated spam while button is held
- Wiring pattern: PA0 -> button -> GND
Step 07 Task: Analog Readout
Next task: Read ADC on PA1 and print values.
Learn: one-shot ADC + value classification.
bash scripts/run-step.sh 07 task
Step 07 Solution
- ADC raw value changes with analog input
- Classification low/medium/high works
- Periodic readout appears in RTT console
Step 08 Task: Final Combined
Next task: Integrate LED + button + ADC behavior.
Learn: small embedded state machine.
bash scripts/run-step.sh 08 task
Step 08 Solution
- Button toggles mode: Blinking / ForcedOff
- ADC value controls blink interval in Blinking mode
- Integrated firmware runs stable with logs
Workshop Wrap-up
- You moved from Rust basics to real MCU firmware
- You used no_std, HAL GPIO, button input, and ADC
- Next: extend with UART, interrupts, or Embassy async