A collection of embedded Rust programming examples for the BBC micro:bit v2. This project demonstrates various aspects of embedded development using the nRF52833 microcontroller and the micro:bit v2 development board.
This project took inspiration and the initial Hello World example from The Embedded Rust Book and has evolved to include progressively advanced embedded programming concepts and techniques. The Embedded Rust Book is an excellent reference and readers are encouraged to refer to it alongside these examples.
Note on the name: This repository shares its name with the excellent The Rusty Bits YouTube channel by pure coincidence. They had the name first! The channel provides comprehensive embedded Rust programming tutorials and is definitely worth checking out, though it is unaffiliated with this project.
- 📋 vscode_setup.md - Complete VS Code configuration guide for embedded Rust development
- 🦀 embedded_rust_primer.md - Beginner's guide to embedded Rust: #![no_std],#[entry], HAL patterns, and memory management
- 🔍 deep_dive.md - Technical deep dive into the Rust-to-hardware compilation pipeline
- ⚡ hardware.md - How memory mapping physically works: address bus, Flash, RAM, and peripherals
- 📄 micro:bit v2 Schematic - Official hardware schematic (PDF)
This project follows a progressive learning journey from high-level convenience to bare-metal understanding. Each example strips away more abstraction layers to show you exactly what's happening under the hood.
This project uses independent examples rather than a Cargo workspace - each example can be copied and used standalone.
🎯 High-Level HAL Approach - "I want to blink an LED easily"
- Board initialization using convenient HAL crates
- Hardware timer usage with high-level APIs
- Simple LED matrix control
- 5 dependencies - Maximum convenience and safety
- Best for: Getting started quickly with embedded Rust
🔧 Direct Register Access - "How do GPIO registers actually work?"
- Direct hardware register manipulation
- Reduced dependencies while maintaining essential functionality
- 3 dependencies - Balance of control and convenience
- Best for: Understanding hardware interfaces and register-level programming
⚡ Bare Metal Implementation - "How does the ENTIRE system work?"
- Zero dependencies - Everything implemented from scratch
- Custom ARM Cortex-M vector table and reset handler
- Hand-crafted linker script and memory initialization
- Direct assembly integration and complete system control
- Best for: Deep understanding of embedded systems architecture
🔥 Advanced Bare-Metal Implementation - "Complete hardware control"
- Pure ARM Thumb assembly implementation with minimal Rust scaffolding
- Hardcoded memory addresses and stack pointer configuration
- 8-byte minimal vector table with no runtime initialization
- Best for: Silicon-level understanding and maximum performance optimization
🎮 Interactive Input Processing - "How do I handle user input with polling?"
- Button polling with edge detection and software debouncing
- GPIO input configuration with pull-up resistors
- State management for toggle functionality
- Active-low input logic and mutability requirements
- Best for: Learning basic input processing techniques and interactive embedded applications
⚡ Interrupt-Driven Processing - "How do I use hardware interrupts for efficient input handling?"
- GPIOTE peripheral configuration for hardware interrupt generation
- Atomic state management between interrupt handler and main loop
- Power-efficient operation with Wait-For-Interrupt (WFI) instruction
- Direct register access and minimal interrupt service routines
- Best for: Learning interrupt-driven architecture and power-efficient embedded design
🔍 Real-Time Debug Logging - "How do I debug embedded applications without UART?"
- RTT (Real-Time Transfer) for high-speed debug output
- Printf-style debugging via the debug probe connection
- Zero GPIO pins required, no UART configuration needed
- Automatic timestamping and formatted output support
- Best for: Learning modern embedded debugging techniques and real-time logging
💬 Interactive Debug Terminal - "How do I send commands to my embedded device?"
- Bidirectional RTT communication for interactive debugging
- Receive input from host PC and send responses
- Non-blocking I/O for real-time interactivity
- Direct channel control with UpChannel and DownChannel
- Best for: Building interactive debug interfaces and command interpreters
📡 I²C Sensor Communication - "How do I read data from I²C sensors?"
- I²C communication using TWIM (Two-Wire Interface Master) peripheral with DMA
- LSM303AGR accelerometer sensor driver integration
- Reading acceleration data in milligravities across three axes
- High-level driver abstractions over low-level register access
- Multiple abstraction layers from Rust code to hardware I²C signals
- Best for: Learning I²C protocol implementation and sensor interfacing
Note: Examples 07, 08, and 09 require
cargo embedinstead ofcargo runto access the interactive RTT terminal.
Educational Philosophy: Examples 03 and 04 represent progressively lower levels of embedded systems programming. While production code typically uses higher-level abstractions (Example 01 approach), understanding bare-metal implementation provides valuable insight into the underlying hardware behavior and system architecture.
Example 04 specifically demonstrates the absolute minimum required for ARM Cortex-M execution, showing exactly what instructions run on the processor without any runtime overhead or abstraction layers.
| Abstraction Level | Example 01 | Example 02 | Example 03 | Example 04 | 
|---|---|---|---|---|
| Dependencies | 5 crates | 3 crates | 0 crates | 0 crates | 
| Implementation | High-level Rust | Register access | Bare metal Rust | 99% Assembly | 
| Code Style | led.set_high()? | gpio.out.set(1 << 4) | ptr::write_volatile(0x50000508, 1 << 4) | str r1, [r0] | 
| Startup | Automatic | Automatic | Manual reset handler | Assembly reset handler | 
| Memory Init | Hidden | Hidden | Explicit RAM setup | No initialization | 
| Vector Table | Generated | Generated | Hand-crafted | 8-byte minimal | 
| Binary Size | ~4KB+ | ~2KB+ | ~1KB+ | ~100 bytes | 
| When to Use | Production code | Learning registers | Understanding systems | Performance optimization | 
Each example builds the same functionality (blinking LED) but reveals progressively more of the underlying machinery. The progression moves from high-level abstractions through register manipulation to complete bare-metal assembly implementation, providing comprehensive understanding of embedded systems from hardware reset vector to application logic.
Examples 5+: While Examples 01-04 focus on different abstraction levels of the same functionality, Examples 5 onwards explore different embedded programming concepts and peripherals (input processing, sensors, communication protocols, etc.) using practical implementation approaches.
💡 Note: Each example directory contains its own readme.md with detailed explanations, code walkthrough, and specific instructions for that example.
- BBC micro:bit v2 (with nRF52833 microcontroller)
Follow the installation guide from The Embedded Rust Book - Installation for complete setup instructions.
Required extensions for the best development experience:
- 
rust-analyzer ( rust-lang.rust-analyzer)- Provides Rust language support and Code Lens features
 
- 
probe-rs-debugger ( probe-rs.probe-rs-debugger)- Required for embedded debugging support
 
Each example is a complete, independent Rust project:
In VS Code (Recommended):
- Open an example's src/main.rsfile in VS Code
- Click the ▶️ Run button above the#[entry]function
- The program builds and flashes automatically to your micro:bit
From Command Line:
# Navigate to an example and run it
cd example_01_hello_world
cargo run💡 Tip: See vscode_setup.md for complete VS Code configuration and debugging setup.
