Writing
Embedded Rust on XIAO BLE, Part 1: Blinky With Embassy
Run the first Embassy firmware example, blink the onboard RGB LED, and understand the active-low pins.
Series Embedded Rust on XIAO BLE / Part 1 of 2
March 05, 2026 / 2 min read
- Embedded Rust
- Embassy
- XIAO BLE
- GPIO
Article
This part follows the first video, Embassy Rust + XIAO BLE - 01 Blinky, and the matching repository crate, crates/01-blinky.
The job is tiny on purpose: make the onboard RGB LED cycle through red, green, and blue. That small loop introduces most of the shape you will keep seeing in Embassy projects.
git clone https://github.com/sarmadgulzar/xiao-ble-embassy
cd xiao-ble-embassy
cargo run --bin blinky
The workspace crate is named 01-blinky, while the binary package is named blinky. With the board connected and the probe setup working, cargo run --bin blinky builds and flashes that example.
The firmware starts with two embedded Rust signals:
#![no_std]
#![no_main]
no_std means this program is not using the normal desktop/server Rust standard library. no_main means the usual fn main() entrypoint is replaced by the runtime entrypoint supplied by Embassy.
The Embassy entrypoint looks like this:
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_nrf::init(Default::default());
}
That initialization gives the program ownership of the nRF52840 peripherals. From there, the code turns three GPIO pins into outputs for the onboard RGB LED:
- red:
P0_26 - green:
P0_30 - blue:
P0_06
The important hardware detail is that the onboard LED is active low. Starting a pin at Level::High keeps that LED channel off. Calling set_low() turns it on.
let mut red = Output::new(p.P0_26, Level::High, OutputDrive::Standard);
red.set_low();
Timer::after_millis(1000).await;
red.set_high();
That inversion is one of the first embedded lessons hiding inside the blinky example. Code that reads like "low" can mean "on" because the board wiring decides the electrical behavior.
Embassy's async timer is the other useful piece. Timer::after_millis(1000).await waits without turning the firmware into a busy delay loop. In this first example, there is only one task, but the shape is already ready for more work later.
Once the example runs, make one small change before moving on:
- change the delay to
200milliseconds - change the color order
- turn two LED channels on together to make mixed colors
The win is not just blinking an LED. It is seeing the full firmware loop: initialize peripherals, configure pins, drive outputs, wait, repeat.
Continue The Series
Embedded Rust Series
Embedded Rust on XIAO BLE
Part 1 of 2
A hands-on Embassy Rust series for the Seeed Studio XIAO BLE and nRF52840.
-
1
Embedded Rust on XIAO BLE, Part 1: Blinky With Embassy
Run the first Embassy firmware example, blink the onboard RGB LED, and understand the active-low pins.
2 min read
-
2
Embedded Rust on XIAO BLE, Part 2: Digital Input With Pull-Ups
Read a button with Embassy, use a pull-up input, and drive the onboard LED from GPIO state.
2 min read