Rust, Embassy, Zephyr - Ready for Prime Time?

Bill Greiman

Well-known member
I have been spending lots of time exploring Rust and Zephyr. I am glad to see others here also trying these.

Occasionally I get excited but then am disappointed when I run Rust libraries and look at the code.

I will be following what others here are doing and will be interested in their opinions.

Here is a list of resources I have used:

The key document for the Rust language:


Here is a great way to explore Rust features by running and modifying examples. I ran the examples on Ubuntu and Windows.


I started with Rust Embedded but I am mostly using Embassy now. I tried Zephyr and it looks great when you need a true RTOS but is big and complex.


First of 12-part embedded Rust series on RP2350:


Embassy is a project to make async/await a first-class option for embedded development. Compared to alternatives such as an RTOS, async can yield better performance and lower power consumption because the executor doesn’t have to guess when a future is ready to execute.


The main Zephyr documentation.


First of 12-part video series exploring the Zephyr on ESP32-S3-DevKitC.


I am using Pico 2 (RP2350) to learn Rust, Embassy, and Zephyr. There is good support for these in the Raspberry Pi Pico Visual Studio Code extension.


I am starting to develop a fast Rust SD SPI driver in Rust. Also I am learning how to wrap existing C code so it can be called from Rust.

I use both Ubuntu and Windows. Ubuntu is nice, I have it on a 15.6 inch notebook with a fast AMD processor.

I use two PCs, one running Windows an one Ubuntu. I have a single 32 inch monitor with three inputs, and a Logitech Bolt keyboard and mouse that support three connections.

I like the fact that Rust programs run well on Linux, Windows, and MCUs.
 
Hi @Bill Greiman, looks like you are having fun.

So far I have not tried Rust, but have been playing with Zephyr for a while now, including the Arduino boards which builds on top of Zephyr.

Currently there is some Zephyr support for Teensy 4.x boards, although mostly barely limping along. Which someone would spend some time
setting up better configurations, for memory and the like. Currently they use teensy_locader_cli to download programs, so no support for protected teensy boards. Wish there was a version of Teensy.exe that ran on headless setups.

Will be interesting to see how you progress using Rust.
 
I am interested in all three. To me, it doesn't seem quite ready yet. And, I lack the time to help get them there. The big problem is one of inertia. There are just so many good libraries for Arduino that work out of the box. When you to something like Zephyr, you just don't have quite the same number of libraries. You end up having to write more code yourself. And, so, I end up largely sticking with TeensyDuino because all the stuff I need is already there. But, this is one of those chicken and egg situations. The support will never be there we we don't start using it but I don't want to use it because the support isn't there yet.
 
I am interested in all three. To me, it doesn't seem quite ready yet. And, I lack the time to help get them there. The big problem is one of inertia. There are just so many good libraries for Arduino that work out of the box. When you to something like Zephyr, you just don't have quite the same number of libraries. You end up having to write more code yourself. And, so, I end up largely sticking with TeensyDuino because all the stuff I need is already there. But, this is one of those chicken and egg situations. The support will never be there we we don't start using it but I don't want to use it because the support isn't there yet.
The support will develop. There are some talented groups starting to develop real products. Places like CERN, The European Laboratory for Particle Physics where the World Wide Web (WWW) was invented in 1989.

I worked at CERN on ATLAS experiment which discovered the Higgs boson. I am a physicist, I did design studies for ATLAS event builder network.

ATLAS event building demands massive throughput, jumping to full terabyte-per-second capabilities to collect events at up to 1 MHz. I finally proposed a Clos network built from off-the-shelf network switches and embedded processors. I was amazed when it was accepted and built.

At CERN, the Rust programming language is experiencing growing adoption and evaluation as a modern alternative to C++ for performance-critical systems, data acquisition (DAQ), and experimental data analysis. While C++ and Python remain the dominant pillars of the High-Energy Physics (HEP) ecosystem, Rust is increasingly valued for providing C++ level speeds combined with compile-time memory safety.
 
Last edited:
Hi @Bill Greiman, looks like you are having fun.

So far I have not tried Rust, but have been playing with Zephyr for a while now, including the Arduino boards which builds on top of Zephyr.

Currently there is some Zephyr support for Teensy 4.x boards, although mostly barely limping along. Which someone would spend some time
setting up better configurations, for memory and the like. Currently they use teensy_locader_cli to download programs, so no support for protected teensy boards. Wish there was a version of Teensy.exe that ran on headless setups.

Will be interesting to see how you progress using Rust.
I expect Rust will be supported as open source by groups of large organizations. Look at Tikio, some of the worlds largest companies are sharing Rust technology for a wide range of systems, from large servers with dozens of cores to small embedded devices.

I want to go to the source of this technology and stop using Arduino I am watching as more MPU suppliers support Rust Embassy and Zephyr.

It will be some time before it will be routine to use this technology. I enjoy seeing how new technology develops and this is new.

There are still breaking updates. Often the documentation changes daily. Here is today's timestamp for the Embassy Book:

Last updated 2026-06-17 23:56:13 UTC.

Here is the current time: 2026-06-18 16:44:34 UTC.
 
Hi Bill, most of my application code is custom, except SdFat. So, are you planning to make a Rust version of it?
 
I’m still early in the learning curve too, but my experience so far has been encouraging.

I’ve been doing mostly close-to-the-metal Rust bring-up work rather than high-level application work. On Teensy 4.x I’ve been working directly with the FlexCAN registers and have classical CAN running between CAN1 and CAN2 through external transceivers.

I’ve also been doing bare-metal STM32G431 ESC work in Rust, including low-level timer/PWM/ADC bring-up for BLDC/FOC-style development. That project has been a good reminder that Rust is not only about big frameworks or async runtimes. You can still get very close to the hardware when needed.

My current view is: not a full replacement for Teensyduino or C/C++ yet, but definitely ready enough to start building serious foundations.

Here is one of my STM32 Rust repos as a work-in-progress example:

https://github.com/OIEIEIO/stm32-rust-test

It is not meant to be a polished library yet. It is more of a bring-up trail showing experiments, failures, fixes, and working checkpoints.

That is actually what has encouraged me about Rust so far. On the STM32G431 ESC board I’ve been able to stay very close to the metal: timers, PWM, ADC, motor-control bring-up, and early BLDC/FOC-style work without hiding everything behind a large framework.

So I agree Rust is not “Arduino easy” yet, but for low-level embedded bring-up it already feels very real.
Screenshot from 2026-06-09 04-00-33.png
 
Last edited:
Hi Bill, most of my application code is custom, except SdFat. So, are you planning to make a Rust version of it?
I am exploring making a Rust SD SPI block driver crate.

I had a strange experience with translating part of SdFat to Rust. I have limited access to Claude Code.

I asked Claude to translate functions that map UTF-8 to exFAT UTF-16 for directories. The C++ had no comments. Claude did a great job but commented on improvements for how I use the functions. Clearly Claude knows about SdFat. I suspect it knows about all of GitHub.

I really don't want to just translate SdFat. SdFat has evolved since 2008 as a FAT16 short filename library for 16KB Arduino. I would never structure a new Rust Crate like SdFat.

I don't know how far I will go. I may try wrapping SdFat in a Rust Crate. There is a Rust Crate that wraps fatfs.

I also need to check this fatfs Crate. I suspect it only supports FAT12, FAT16, FAT32.
 
I’m still early in the learning curve too, but my experience so far has been encouraging.

I’ve been doing mostly close-to-the-metal Rust bring-up work rather than high-level application work. On Teensy 4.x I’ve been working directly with the FlexCAN registers and have classical CAN running between CAN1 and CAN2 through external transceivers.

I’ve also been doing bare-metal STM32G431 ESC work in Rust, including low-level timer/PWM/ADC bring-up for BLDC/FOC-style development. That project has been a good reminder that Rust is not only about big frameworks or async runtimes. You can still get very close to the hardware when needed.

My current view is: not a full replacement for Teensyduino or C/C++ yet, but definitely ready enough to start building serious foundations.

Here is one of my STM32 Rust repos as a work-in-progress example:

https://github.com/OIEIEIO/stm32-rust-test

It is not meant to be a polished library yet. It is more of a bring-up trail showing experiments, failures, fixes, and working checkpoints.

That is actually what has encouraged me about Rust so far. On the STM32G431 ESC board I’ve been able to stay very close to the metal: timers, PWM, ADC, motor-control bring-up, and early BLDC/FOC-style work without hiding everything behind a large framework.

So I agree Rust is not “Arduino easy” yet, but for low-level embedded bring-up it already feels very real.View attachment 39516
Rust is nice for embedded since you can use embedded-hal to have total hardware access. The next step is Embassy for cooperative multitasking without requiring a traditional RTOS. Rust with Zephyr and Embassy provides full RTOS and fast coop.

Here is are slides from a talk about Rust with Zephyr. "Combining Rust with the Zephyr RTOS is highly rewarding but carries a famously steep learning curve."
 
I have been using more AI to translate parts of the SdFat SPI driver. Here is an interesting result.

DeepSeek V4 Pro is a large-scale Mixture-of-Experts model from DeepSeek with 1.6T total parameters and 49B activated parameters. It is designed for advanced reasoning, coding, and long-horizon agent workflows. Released 24th April 2026.

I gave it this C++ file with no comments.
Code:
uint8_t CRC7(const uint8_t* data, uint8_t n) {
  uint8_t crc = 0;
  for (uint8_t i = 0; i < n; i++) {
    uint8_t d = data[i];
    for (uint8_t j = 0; j < 8; j++) {
      crc <<= 1;
      if ((d & 0x80) ^ (crc & 0x80)) {
        crc ^= 0x09;
      }
      d <<= 1;
    }
  }
  return (crc << 1) | 1;
}


This is the Rust DeepSeek V4 Pro returned. It understood how the code was to be used and commented it!

Code:
/// Computes the CRC7 checksum of the given data bytes.
///
/// This function implements the CRC-7 algorithm with polynomial `0x09` (x^7 + x^3 + 1),
/// commonly used in MMC/SD cards. The returned value is the 7-bit checksum left-shifted
/// by one, with the least significant bit set to 1, following the SD card format where the
/// checksum is followed by a '1' stop bit.
pub fn crc7(data: &[u8]) -> u8 {
    let mut crc: u8 = 0;
    for &byte in data {
        let mut d = byte;
        for _ in 0..8 {
            crc <<= 1;
            if ((d & 0x80) ^ (crc & 0x80)) != 0 {
                crc ^= 0x09;
            }
            d <<= 1;
        }
    }
    (crc << 1) | 1
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_empty_input() {
        // With zero bytes, CRC is 0, result becomes 0b00000001 = 0x01
        assert_eq!(crc7(&[]), 0x01);
    }

    #[test]
    fn test_single_zero_byte() {
        // 0x00 should produce CRC 0x00, returned as 0x01
        assert_eq!(crc7(&[0x00]), 0x01);
    }

    #[test]
    fn test_known_value() {
        // Test vector from SD/MMC specification: CRC7 of 0x40, 0x00, 0x00, 0x00, 0x00
        // yields 0x4A (CRC) then returned with stop bit → 0x95
        assert_eq!(crc7(&[0x40, 0x00, 0x00, 0x00, 0x00]), 0x95);
    }
}

Makes me shiver. The final test case is the SD command to init an SD card.

Chinese models are getting very close to frontier level AI.

I gave DeepSeek the CRC for SD data in SPI mode which is a modification of standard CCITT. It didn't understand it was for SD data in SPI mode. SDIO mode is very different.

Here is what it returned:
Code:
/// This function is a direct translation of the provided C code.
/// Despite the name `CRC_CCITT`, the algorithm does not correspond to the standard CRC-CCITT;
/// it appears to be a custom implementation. The logic is preserved exactly.
fn crc_ccitt(data: &[u8]) -> u16 {
    let mut crc: u16 = 0;
    for &byte in data {
        // Equivalent of byte-swapping a 16-bit value
        crc = (crc >> 8) | (crc << 8);
        crc ^= u16::from(byte);
        // The original cast `(uint8_t)(crc & 0xff)` is redundant; the mask already produces an 8-bit value.
        crc ^= (crc & 0xff) >> 4;
        crc ^= crc << 12;
        crc ^= (crc & 0xff) << 5;
    }
    crc
}

// Example usage
fn main() {
    let test_data = b"123456789";
    let result = crc_ccitt(test_data);
    println!("CRC: 0x{:04X}", result);
}
 
Funny that the AI did not realize that
Code:
 if ((d & 0x80) ^ (crc & 0x80)) {
can be simplified from three logic operations to two (or technically, one logic op and a sign extension):
Code:
 if ((d ^ crc) & 0x80) {
 
I'm assuming the prompt was "translate this code" not "translate this code and also optimize it", as we all have probably had negative experiences with people trying to both at the same time. The original code had the (slightly) inefficient statement that would be optimized away by the compiler in most cases anyway, so good job to AI for doing the task it was asked, not the task it wanted to do!
 
Funny that the AI did not realize that
Code:
 if ((d & 0x80) ^ (crc & 0x80)) {
can be simplified from three logic operations to two (or technically, one logic op and a sign extension):
Code:
 if ((d ^ crc) & 0x80) {
I started looking for other Rust SD libraries. I find this function in other Crates, clearly translated from SdFat and they don't have the optimization.

I do get credit sometimes. This is from the popular embedded-sdmmc Crate:
Code:
//!
//! Based on SdFat, under the following terms:
//!
//! > Copyright (c) 2011-2018 Bill Greiman
//! > This file is part of the SdFat library for SD memory cards.
//! >
 
Embedded Rust SPI is difficult to use for SD cards. SD cards don't share the way Rust developers expect. Here is comments from the popular embedded-sdmmc Crate.

Code:
/// Built from an [`SpiDevice`] implementation and a Chip Select pin.
///
/// Before talking to the SD Card, the caller needs to send 74 clocks cycles on
/// the SPI Clock line, at 400 kHz, with no chip-select asserted (or at least,
/// not the chip-select of the SD Card).
///
/// This kind of breaks the embedded-hal model, so how to do this is left to
/// the caller. You could drive the SpiBus directly, or use an SpiDevice with
/// a dummy chip-select pin. Or you could try just not doing the 74 clocks and
/// see if your card works anyway - some do, some don't.

The result of using an SpiDevice is that this implementation is very slow - often write is on the order of 50 KB/sec.

There is no real equivalent to Arduino beginTransaction(), endTransactio(). The latest embedded hal has a limited transaction:

Code:
pub fn read_status(&mut self) -> Result<u8, SPI::Error> {
        let mut buf = [0];
        // Transaction asserts CS, writes 0x90, reads response, then deasserts CS
        self.spi.transaction(&mut [
            Operation::Write(&[0x90]),
            Operation::Read(&mut buf),
        ])?;
        Ok(buf[0])
    }

I suspect improvements in SPI since many users have problems with SPI.

I will use SpiBus rather than SpiDevice so I have total control, like SdFat dedicated SPI.
 
Small embedded projects utilizing Teensy or Arduino don't need Rust per se. A program encompassing a a few thousand lines can be made reliably in C by a single person.

Rust is only needed for code bases consisting of tens to thousands up to millions of lines of code IMHO.
 
Rust bare-metal works great. I use Rust embedded regularly across MCU firmware and bootloader work, and no OS is required.
No Zephyr, no RTOS, no scheduler layer is needed unless the project specifically benefits from one. Rust can sit right at the register level just like C: startup code, linker script, interrupts, peripherals, and a main loop.
Frameworks like Zephyr, Embassy, or RTIC are useful tools, but they are optional. They are not what makes Rust embedded possible.
 
Also Rust bare-metal - this runs on Teensy4.1 as a companion computer to a drone flight controller
Nueral Net and inference also hand written in bare Rust.

Snow Owl: standard flight controller stack plus an external AI shadow brain watching, learning, comparing, and preparing for supervised autonomy.
1782498704845.png
 
Rust bare-metal works great. I use Rust embedded regularly across MCU firmware and bootloader work, and no OS is required.
No Zephyr, no RTOS, no scheduler layer is needed unless the project specifically benefits from one. Rust can sit right at the register level just like C: startup code, linker script, interrupts, peripherals, and a main loop.
Frameworks like Zephyr, Embassy, or RTIC are useful tools, but they are optional. They are not what makes Rust embedded possible.

A bare bones scheduler isn't that hard either.
 
Working on some Teensy 4.1 firmware in bare-metal Rust.

The Teensy 4.x boards have been really nice targets for this kind of low-level work, and the project structure has stayed pretty straightforward. This setup is running without an OS or RTOS, but with USB mass storage, CDC serial status logging, FC UART/MAVLink parsing, NanoBuck GPIO rail control, and a FlexCAN listener split into small Rust modules.

Thought I’d share a screenshot of what my programming setup looks like. Still a work in progress, but Teensy + Rust has been a great learning path so far.
Screenshot from 2026-06-26 17-19-53.png
 
5 Teensy's (Smart nodes are T4.0) on CANbus - everything written in Rust
Screenshot from 2026-06-29 18-38-32.png

Screenshot from 2026-06-29 20-10-34.png

Everything but the flight controller written in Rust.
Dashboard is Work in Progress - Flight Controller over UART in this screenshot - CANbus monitoring 1 motor.
Teensy Rust Programming :)
 
Back
Top