A simple "Hello World" example for controlling an SSD1306 OLED display (128x32, I2C interface) using embedded Rust on the BBC micro:bit v2.
This project serves as an accessible introduction to embedded Rust, I2C communication, and hardware debugging before tackling more complex IIoT systems. It's ideal for:
- Embedded Rust beginners - gentle introduction with excellent tooling and documentation
- IIoT preparation - foundational skills for STM32, MQTT, OPC-UA, and industrial protocols
- Hardware debugging practice - real-world troubleshooting of I2C buses, power issues, and pin mappings
- Display integration - essential for sensor visualization and HMI development
The patterns learned here (HAL abstractions, peripheral initialization, error handling, protocol debugging) transfer directly to professional embedded and industrial automation projects.
The BBC micro:bit v2 is an ARM-based embedded development board featuring:
- Nordic nRF52833 microcontroller (Cortex-M4, 64MHz, 512KB Flash, 128KB RAM)
- Built-in sensors: accelerometer, magnetometer, temperature sensor, microphone
- 5×5 LED matrix display and speaker
- Edge connector with 25 pins for external peripherals
- Bluetooth Low Energy support
- Excellent for learning embedded systems and IoT projects
Rust brings memory safety and zero-cost abstractions to embedded development:
- Memory safety without garbage collection - catch bugs at compile time
- No undefined behavior - eliminates entire classes of embedded bugs
- Excellent tooling - cargo, rustfmt, clippy work seamlessly
- Growing ecosystem - mature HAL crates for most microcontrollers
- Performance - same speed as C/C++, but safer
The micro:bit has excellent Rust support through the microbit-v2 crate, making it an ideal platform for learning embedded Rust before moving to STM32, ESP32, or industrial-grade microcontrollers.
This project uses a micro:bit expansion board (like the IO BIT V2.0) which:
- Breaks out all edge connector pins to convenient screw terminals or headers
- Provides stable power via USB or battery for external peripherals
- Makes prototyping easier - no need for alligator clips or breadboard adapters
- Protects the micro:bit - prevents accidental damage to the edge connector
This simple OLED "Hello World" serves as a foundation for more complex projects:
Sensor Data Display:
- Show accelerometer readings (tilt angle, gesture detection)
- Display temperature and compass heading from built-in sensors
- Add external I2C sensors (BME280 for humidity/pressure, MPU6050 for gyroscope)
- Graph real-time data with scrolling charts
User Interface:
- Create menus navigated with the A/B buttons
- Display status messages and error codes
- Show WiFi/Bluetooth connection status
- Build data logging interfaces
Multi-Device Projects:
- Combine multiple I2C devices (sensors + OLED) on the same bus
- Add SPI displays or SD card storage
- Create weather stations, motion monitors, or game consoles
- Build robotics projects with motor controllers
Industrial/IIoT Preparation:
- Practice I2C debugging and protocol analysis (transferable to Modbus, PROFINET)
- Learn display-based HMI patterns for SCADA-like interfaces
- Understand peripheral initialization and error handling
- Build toward MQTT sensor nodes and OPC-UA integration
The patterns learned here apply directly to more advanced embedded Rust projects, STM32 development, and professional IIoT systems.
- BBC micro:bit v2 (nRF52833)
- OLED Display Module - SSD1306, 128x32 resolution, I2C interface, white font
- micro:bit Expansion Board (e.g., IO BIT V2.0 Horizontal Adapter Plate)
- 4 female-to-female jumper wires (for VCC, GND, SCL, SDA)
- 2 USB cables (one for programming micro:bit, one for powering expansion board)
Connect the OLED display to the expansion board's edge connector pins:
| OLED Pin | Expansion Board Pin | Description |
|---|---|---|
| VCC | 3V | Power (3.3V) |
| GND | GND | Ground |
| SCL | Pin 19 (SCL) | I2C Clock |
| SDA | Pin 20 (SDA) | I2C Data |
Important: The expansion board must be powered separately via its USB port to provide stable 3.3V to the OLED.
-
Install Rust and tools:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh rustup target add thumbv7em-none-eabihf cargo install probe-rs --features cli
-
Install objcopy (for creating hex files):
# Already included with Rust toolchain as rust-objcopy
microbit-oled/
├── Cargo.toml
├── .cargo/
│ └── config.toml
└── src/
└── main.rs
[package]
name = "microbit-oled"
version = "0.1.0"
edition = "2021"
[dependencies]
cortex-m = "0.7"
cortex-m-rt = { version = "0.7", features = ["device"] }
panic-halt = "0.2"
microbit-v2 = "0.13"
ssd1306 = "0.8"
embedded-graphics = "0.8"
[profile.release]
codegen-units = 1
debug = true
lto = true[target.thumbv7em-none-eabihf]
runner = "probe-rs run --chip nRF52833_xxAA"
rustflags = [
"-C", "link-arg=-Tlink.x",
]
[build]
target = "thumbv7em-none-eabihf"Simply run:
cargo run --releaseThe program will automatically build, flash to the micro:bit, and run.
If you prefer manual flashing or cargo run has issues:
# Build the project
cargo build --release
# Convert to Intel HEX format
rust-objcopy target/thumbv7em-none-eabihf/release/microbit-oled -O ihex microbit.hex
# Copy to micro:bit (it appears as USB drive)
cp microbit.hex /media/$USER/MICROBIT/The micro:bit will automatically reset and run the program.
The example program:
- Shows a smiley face on the LED matrix when starting
- Initializes the I2C connection to the OLED
- If initialization fails, shows a blinking X pattern on the LED matrix
- On success, shows a checkmark on the LED matrix
- Displays "Hello World!" on the OLED screen
- Shows a heart pattern on the LED matrix to confirm completion
Problem: Using board.i2c_internal instead of board.i2c_external
Solution: The edge connector pins (19/20) are on the external I2C bus:
// ❌ Wrong - this is for internal sensors
let i2c = twim::Twim::new(board.TWIM0, board.i2c_internal.into(), twim::Frequency::K100);
// ✅ Correct - use external I2C for edge connector
let i2c = twim::Twim::new(board.TWIM0, board.i2c_external.into(), twim::Frequency::K100);Problem: Using individual LED pins incorrectly
Solution: Use the Display API properly:
// ❌ Wrong - doesn't work reliably
let mut led = board.display_pins.col1.into_push_pull_output(Level::Low);
led.set_high().ok();
// ✅ Correct - use the Display API
let mut display = Display::new(board.display_pins);
display.show(&mut timer, pattern, 1000);Possible causes:
- Expansion board not powered: Connect a separate USB cable to the expansion board's USB port
- Wrong wiring: Double-check connections to pins 19 (SCL) and 20 (SDA)
- micro:bit inserted upside down: LED matrix should face UP, buttons on top
- Wrong I2C address: Try 0x3D if 0x3C doesn't work
- Loose connections: Ensure jumper wires are firmly connected
Problem: Missing linker script configuration
Solution: Ensure .cargo/config.toml has the correct rustflags:
rustflags = [
"-C", "link-arg=-Tlink.x",
]And cortex-m-rt has the device feature:
cortex-m-rt = { version = "0.7", features = ["device"] }Problem: Code isn't being linked properly
Solution: Clean and rebuild:
cargo clean
cargo build --releaseVerify sections exist:
rust-objdump -h target/thumbv7em-none-eabihf/release/microbit-oledYou should see .text, .rodata, .data, and .bss sections.
The micro:bit documentation refers to edge connector pins (like "Pin 19"), but these map to specific nRF52833 GPIO pins (like "P0.16"). The microbit-v2 crate provides convenient abstractions:
- ✅ Use
board.i2c_externalfor edge connector I2C - ❌ Don't try to manually specify GPIO pin numbers
- micro:bit is V2 (not V1)
- micro:bit is seated correctly in expansion board (LED matrix facing up)
- Expansion board has separate USB power connected
- OLED wiring: VCC→3V, GND→GND, SCL→19, SDA→20
- Code uses
board.i2c_external(noti2c_internal) - Binary has
.textsection (check withrust-objcopy) - Used drag-and-drop hex file method for flashing
Once you have the basic example working, you can:
- Display sensor data (temperature, accelerometer, etc.)
- Show animations or scrolling text
- Draw shapes and graphics using
embedded-graphics - Add button controls to change what's displayed
- Connect multiple I2C devices
- micro:bit Hardware
- microbit-v2 Rust Crate
- embedded-graphics Documentation
- SSD1306 Driver
- probe-rs Documentation
Thanks to the Rust Embedded community and the maintainers of the microbit-v2, ssd1306, and embedded-graphics crates.