Implementing embedded_io:Write trait for STM32G0 HAL usarts

⚓ rust    📅 2025-07-16    👤 surdeus    👁️ 1      

surdeus

Hey!

I have an application where im using the stm32g0xx_hal crate together with the embedded_io crate.

Im a bit new at embedded rust and im coming from c, so some of the mechanisms in rust are quite new to me.

I want a generic way of using the uarts (TX part only) so i dont have to hardcode the usart with a driver.

I have therefore tried to use the Write trait in the embedded_io crate by creating a wrapper that takes a generic type(but restricted to a set of acceptable USART TX types from the STMG0 HAL crate) and then the wrapper implements the Write trait.
The Wrapper is later passed to a struct(named TerminalData) who then owns it where it will be used from later.

I just cant get it to work, I keep getting the "Bounds not satisfied" error. This is the code:

use embedded_io::Write;
use stm32g0xx_hal::serial::Tx;


pub struct TerminalData 
    {
        terminal_writer: Box<dyn embedded_io::Write<Error = Infallible>>,
    }

pub trait UsartInstance {}
impl UsartInstance for hal::pac::USART1 {}
impl UsartInstance for hal::pac::USART2 {}
impl UsartInstance for hal::pac::USART3 {}
impl UsartInstance for hal::pac::USART4 {}

impl embedded_io::ErrorType for TerminalData {
    type Error = Infallible; // Define the associated Error type
}

impl<T> ErrorType for TxWrapper<T> {
    type Error = Infallible; // Define the associated error type
}

pub struct TxWrapper<T>(hal::serial::Tx<T, hal::serial::FullConfig>);

impl<T> TxWrapper<T>
where
    T: UsartInstance, // Restrict T to USART types
    {
    pub fn new(tx: hal::serial::Tx<T, hal::serial::FullConfig>) -> Self {
        TxWrapper(tx)
    }
}

impl<T> Write for TxWrapper<T>
where
    T: UsartInstance,
    hal::serial::Tx<T, hal::serial::FullConfig>: embedded_io::Write<Error = Infallible>,    
    {
        fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
        // Write the buffer to the terminal writer
            self.0.write(buf);
        Ok(buf.len())
    }

    fn flush(&mut self) -> Result<(), Self::Error> {
        // Flush the terminal writer
        Ok(())
    }

    fn write_all(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
        self.write(buf).map(|_| ())
    }
}

impl TerminalData 
    {
    pub fn new<W1>(input_tx:  W1) -> Self
    where 
        W1: embedded_io::Write<Error = Infallible> + 'static, // Ensure the writer is 'static
    
    {
        Self {
            terminal_writer: Box::new(input_tx),
        }
    } 
}

The error comes when I from main.rs create the instance of the Struct "Terminal":

        let mut terminal_com_port: Serial<stm32::USART2, FullConfig> = ctx.device.USART2
            .usart(
                (gpioa.pa2, gpioa.pa3),
                FullConfig::default().baudrate(115200.bps()).fifo_enable().rx_fifo_enable_interrupt().rx_fifo_threshold(FifoThreshold::FIFO_1_BYTE),
                &mut rcc,
            )
            .unwrap();

        /* split the serial object in TX and RX parts */
        let (mut terminal_com_port_tx, mut terminal_com_port_rx) = terminal_com_port.split();

        let terminal_com_port_tx_wrapper = TxWrapper::new(terminal_com_port_tx);

        let mut local_terminal_instance: terminal::TerminalData = terminal::TerminalData::new(terminal_com_port_tx_wrapper);

Bounds are not satisfied:

error[E0277]: the trait bound `hal::serial::Tx<hal::pac::USART2, hal::serial::FullConfig>: embedded_io::Write` is not satisfied
   --> src\main.rs:244:95
    |
244 |         let mut local_terminal_instance: terminal::TerminalData = terminal::TerminalData::new(terminal_com_port_tx);
    |                                                                   --------------------------- ^^^^^^^^^^^^^^^^^^^^ the trait `embedded_io::Write` is not implemented for `hal::serial::Tx<hal::pac::USART2, hal::serial::FullConfig>`
    |                                                                   |
    |                                                                   required by a bound introduced by this call
    |
    = help: the following other types implement trait `embedded_io::Write`:
              &mut T
              &mut [u8]
              terminal::TxWrapper<T>
note: required for `terminal::TxWrapper<hal::pac::USART2>` to implement `embedded_io::Write`
   --> src\terminal.rs:97:9
    |
97  | impl<T> Write for TxWrapper<T>
    |         ^^^^^     ^^^^^^^^^^^^
...
100 |     hal::serial::Tx<T, hal::serial::FullConfig>: embedded_io::Write<Error = Infallible>,
    |                                                  -------------------------------------- unsatisfied trait bound introduced here    
note: required by a bound in `TerminalData::new`
   --> src\terminal.rs:136:13
    |
134 |     pub fn new<W1>(input_writer:  W1) -> Self
    |            --- required by a bound in this associated function
135 |     where
136 |         W1: embedded_io::Write<Error = Infallible> + 'static, // Ensure the writer is 'static
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `TerminalData::new`

What am I missing?. Am I way off in how to use these crates and their types?

2 posts - 2 participants

Read full topic

🏷️ rust_feed