You're staring at a 32KB flash limit and a deadline. The C code works but threading is a nightmare of callbacks and state machines. You've heard Rust's async story is better — zero-cost abstractions, memory safety, no data races. Then you hit the wall: which runtime? Embassy? RTIC? A custom executor? The wrong choice costs weeks.
Why Standard Async Fails on Bare Metal
Desktop runtimes like Tokio assume an OS: heap allocation, thread pools, epoll/kqueue. Microcontrollers have none of that. No MMU. No scheduler. No dynamic memory if you're doing it right. Every byte and cycle counts. Standard futures pull in massive dependency trees — alloc, std, parking_lot — instantly blowing your flash budget.
Embassy: The Pragmatic Choice
Embassy solves this by design. No heap required. Tasks are statically allocated. The executor runs in a single interrupt priority level, using WFE/WFI for sleep. You get async/await syntax with deterministic memory usage. As of 2026, it supports 50+ MCU families via HAL crates and ships 100+ driver crates — sensors, radios, displays, storage.
When to Choose RTIC Instead
RTIC (Real-Time Interrupt-driven Concurrency) takes a different approach: no executor at all. It uses hardware interrupts as tasks with static priorities. The framework analyzes your code at compile time, guaranteeing zero data races and optimal stack usage. Best for hard real-time systems where you need provable worst-case execution times (WCET).
| Criteria | Embassy | RTIC |
|---|---|---|
| Programming model | async/await | Interrupt handlers + resources |
| Priority inversion | Possible (single priority) | Impossible (hardware priorities) |
| Stack usage | Per-task, configurable | Shared, analyzed statically |
| Ecosystem depth | 100+ drivers, growing | Fewer drivers, mature core |
| Learning curve | Lower (familiar async) | Steeper (resource model) |
"The best runtime is the one you don't have to think about. Embassy lets you write business logic, not scheduler logic.
— James Munns, Embassy core team
Optimization Checklist for Any Runtime
Regardless of choice, these moves shrink flash and RAM:
Use 'embassy-time' for timers instead of busy-wait loops. Leverage DMA for peripheral transfers — the CPU sleeps while data moves. Profile with 'cargo size --release -A' after every major change. Track flash/RAM like a budget.
Start Here This Week
Pick one: Embassy for productivity and ecosystem, RTIC for hard real-time guarantees. Clone the embassy-stm32 or rtic-stm32 template repo. Blink an LED with async. Add a sensor driver from crates.io. Measure flash usage. Ship something. The runtime war ends when you deploy.










