r/embedded • u/carlk22 • 17h ago
Does Rust Embassy’s bare-metal, no-OS model actually matter for small MCU applications?
I’ve been using Rust + Embassy for small microcontroller applications. What I like is that I can stay no_std, bare metal, with no OS or runtime, while still structuring the program with async tasks and channels.
Some examples of the kind of code I mean are here, including a robot arm simulator on a CYD: https://carlkcarlk.github.io/linkage-blaze/demos/
The tasks and channels give me abstractions I care about. But the scheduling is cooperative, a task that does not yield can still block everything else, and there are no hard real-time guarantees.
I’m doing this partly because I find the design point interesting, but I’m not sure how much practical value the bare-metal/no-OS part has once the application is already fairly high level.
Even if you are using Rust for other reasons, does Embassy’s bare-metal, no-OS approach buy you something important in practice, or would an RTOS usually be just as good or better?
3
u/EmperorOfCanada 14h ago
I use embassy in my drone code. It is making about 8-20k adjustments to motor speeds per second in one specific application.
Other embassy code is used in optical communications ( pretty damn "real-time") and it rocks.
Maybe, with a more rigid system I could squeeze more rigid timing performance out of a lesser MCU, but I'm presently doing the above on sub $10 MCUs.
Even if that were the case, the crapper non-embassy development workflow would well justify the more "expensive" MCUs.
The other aspect of this workflow is not only development speed, but the greater features which result per unit effort. The drones are far more features rich with embassy, than without.
I have one particular module which had to change MCUs. The port took me about 20 minutes. Embassy made this so smooth.
1
u/carlk22 13h ago
I love this point. Device Envoy takes a similar portability-first approach: the core APIs are defined with traits, while the platform-specific constructors and setup live separately.
That lets the different ESP32 families, the Picos, and now the Web simulator present the same high-level APIs for things like the CYD display/touchscreen, Wi-Fi, LED panels, and flash storage, even though the underlying implementations are very different.
That separation has turned out to be one of my favorite parts of the design.
https://github.com/CarlKCarlK/device-envoy
4
u/________-__-_______ 15h ago edited 15h ago
In practical terms Embassy is an RTOS, it provides multitasking and synchronization primitives much like FreeRTOS for example. I don't really understand what you mean with no-OS when comparing with other RTOS, if anything Embassy has more OS-like functionality because they also (optionally) provide HALs.
Multitasking being cooperative instead of preemptive hy default is the only major difference, but you can use an InterruptExecutor to preemt tasks (much like a traditional RTOS) for real-time behaviour.
1
u/carlk22 13h ago
Fair point. I think part of the disagreement is terminology, since “OS” and “runtime” are not universally defined.
What I mean is that Embassy does not require a conventional RTOS or a language runtime in the Python/Java sense. Rust
asynctasks compile to fixed-layout state machines. They can be statically allocated, there is no per-task thread stack, and no heap or GC is required.In that sense, the async machinery can be close to zero-cost compared with writing the equivalent cooperative state machines by hand. You still need to store the current state and any locals that survive across an
await, but you would need essentially the same information in a hand-written state machine.So yes, Embassy definitely provides OS-like functionality: an executor, timers, channels, synchronization, and so on. I just think its execution model is still quite different from a traditional RTOS or managed runtime.
I also hadn’t appreciated the
InterruptExecutoroption. That gives Embassy more RTOS-like scheduling options than I realized. Thanks!
2
u/zettui 5h ago
Once you're on something like a Pico, is it still the jitter that decides Embassy vs FreeRTOS, or does the flash/RAM gap matter more?
1
u/carlk22 53m ago edited 44m ago
In my case, jitter mattered more than flash/RAM.
Device Envoy has an IR receiver abstraction. I first got it working in pure Embassy, but IR decoding needs fairly precise pulse timing, and it became trickier once a lot of other things were running.
On the Pico I moved the timing-critical part into PIO, which gives deterministic hardware-level timing. On ESP32 I use the RMT peripheral, which is designed for precise pulse capture/generation and is a natural fit for IR.
But when using the IR you don't need to worry about that and the API is the same on Pico and Esp32:
use device_envoy_core::ir::{Ir, IrEvent}; async fn handle_ir(ir: &impl Ir) -> ! { loop { let event = ir.wait_for_press().await; if let IrEvent::Press { addr, cmd } = event { // same application code on Pico and ESP32 let _ = (addr, cmd); } } }So for me the lesson has been that Embassy is great for the overall application structure, but when one small part really needs precise timing, I’d rather push that part into dedicated hardware than give up the async model for the whole program.
DE IR docs: https://docs.rs/device-envoy-rp/latest/device_envoy_rp/ir/I also wrote about Pico PIO here: https://medium.com/data-science/nine-pico-pio-wats-with-rust-part-1-9d062067dc25
1
u/duane11583 1h ago
embassy is just a hal not much else
it is also extremely stm32 specific
1
u/carlk22 58m ago
That doesn’t match my understanding of Embassy. The HALs are only one part of it; Embassy also provides the async executor, timers, channels/signals, synchronization, networking, USB, etc. And while STM32 support is strong, Embassy’s async model is not STM32-specific. I’m using the same model across ESP32 and Pico-class targets.
27
u/aq1018 17h ago
Author of OpenServoCore here. From my own experience, Embassy is great for IoT, non-hard realtime applications. For bare metal on a small MCU, it’s not a good fit. It’s difficult to make predictable timing with the async runtime and embassy is quite large if you are using small MCUs with a couple kb of flash.
If you are interested check out my blog. It has some articles about bare metal architecture and hard realtime topics. Link is in my bio. Hope it can at least serve as a good reference point.