diff --git a/firmware/rust1/src/bin/heizung.rs b/firmware/rust1/src/bin/heizung.rs
index 5b9d0d93be967510949ab3ae2d280c473b588ced..f7f8dbbe3101dcf82d0f3d1c24962a99a0cb0d58 100644
--- a/firmware/rust1/src/bin/heizung.rs
+++ b/firmware/rust1/src/bin/heizung.rs
@@ -19,6 +19,7 @@ use {defmt_rtt as _, panic_probe as _};
 use heapless::String;
 
 use heizung::i2c_scan::{self, ScanResultForBus};
+use heizung::modbus_server::ModbusServer;
 use heizung::rs485::RS485;
 
 #[embassy_executor::task]
@@ -71,7 +72,7 @@ async fn beeper_task(pwm_channel: embassy_rp::peripherals::PWM_CH3, beeper_pin:
 }
 
 #[embassy_executor::task]
-async fn uart_task(this: RS485) {
+async fn uart_task(this: RS485<ModbusServer>) {
     this.run_task().await;
 }
 
@@ -123,7 +124,11 @@ async fn main(spawner: Spawner) {
     let mut uart_config = uart::Config::default();
     uart_config.baudrate = 19200;
     uart_config.parity = Parity::ParityEven;
-    let rs485 = RS485::new(p.UART0, rx, tx, tx_en, interrupt::take!(UART0_IRQ), p.DMA_CH1, uart_config, p.PIO0, p.DMA_CH0, p.DMA_CH2);
+    let rs485 = RS485::new(
+        p.UART0, rx, tx, tx_en, interrupt::take!(UART0_IRQ), p.DMA_CH1, uart_config,
+        p.PIO0, p.DMA_CH0, p.DMA_CH2,
+        ModbusServer::new(),
+    );
     unwrap!(spawner.spawn(uart_task(rs485)));
 
     if false {
diff --git a/firmware/rust1/src/dont_abort.rs b/firmware/rust1/src/dont_abort.rs
index 8ea3b3fa86bc9b6cf4940ace323d837c81885792..5c093c6419b83718213cbaf9fc90af8f341a13f2 100644
--- a/firmware/rust1/src/dont_abort.rs
+++ b/firmware/rust1/src/dont_abort.rs
@@ -8,7 +8,7 @@ pub enum DontAbortMode {
 }
 
 pub struct DontAbort<A> {
-    a: A,
+    a: Option<A>,
     done: bool,
     mode: DontAbortMode,
 }
@@ -19,7 +19,11 @@ pub struct DontAbortFuture<'a, A> {
 
 impl<A> DontAbort<A> {
     pub fn new(a: A, mode: DontAbortMode) -> DontAbort<A> {
-        DontAbort { a, done: false, mode }
+        DontAbort { a: Option::Some(a), done: false, mode }
+    }
+
+    pub fn hang() -> DontAbort<A> {
+        DontAbort { a: Option::None, done: false, mode: DontAbortMode::HangIfReused }
     }
 
     #[allow(dead_code)]
@@ -43,17 +47,20 @@ where
 
     fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
         let this = unsafe { self.get_unchecked_mut() };
-        if this.inner.done {
-            match this.inner.mode {
-                DontAbortMode::PanicIfReused => panic!("Ready future is called again!"),
-                DontAbortMode::HangIfReused => return Poll::Pending,  // will never finish because we haven't scheduled ourselves again
+        match (this.inner.done, this.inner.a.as_mut()) {
+            (true, _) | (_, Option::None) =>
+                match this.inner.mode {
+                    DontAbortMode::PanicIfReused => panic!("Ready future is called again!"),
+                    DontAbortMode::HangIfReused => return Poll::Pending,  // will never finish because we haven't scheduled ourselves again
+                },
+            (false, Option::Some(a)) => {
+                let a = unsafe { Pin::new_unchecked(a) };
+                if let Poll::Ready(x) = a.poll(cx) {
+                    this.inner.done = true;
+                    return Poll::Ready(x);
+                }
+                Poll::Pending
             }
         }
-        let a = unsafe { Pin::new_unchecked(&mut this.inner.a) };
-        if let Poll::Ready(x) = a.poll(cx) {
-            this.inner.done = true;
-            return Poll::Ready(x);
-        }
-        Poll::Pending
     }
 }
diff --git a/firmware/rust1/src/lib.rs b/firmware/rust1/src/lib.rs
index c969570ef887d1bea8b906fa4264985119554e79..0af20935b1863590f05e2b03507c8255dfa011ff 100644
--- a/firmware/rust1/src/lib.rs
+++ b/firmware/rust1/src/lib.rs
@@ -3,3 +3,4 @@ pub mod rp;
 pub mod dont_abort;
 pub mod i2c_scan;
 pub mod rs485;
+pub mod modbus_server;
diff --git a/firmware/rust1/src/modbus_server.rs b/firmware/rust1/src/modbus_server.rs
new file mode 100644
index 0000000000000000000000000000000000000000..1b08a5c1f91a9ee56fe503c3b1bd8f47f5184d4a
--- /dev/null
+++ b/firmware/rust1/src/modbus_server.rs
@@ -0,0 +1,261 @@
+use defmt::*;
+use {defmt_rtt as _, panic_probe as _};
+use futures::Future;
+use heapless::Vec;
+use crc::{Crc, CRC_16_MODBUS, Digest};
+use embassy_rp::uart;
+
+use crate::rs485::{RS485Handler};
+
+#[repr(u8)]
+pub enum ModbusErrorCode {
+    IllegalFunction = 1,
+    IllegalDataAddress = 2,
+    IllegalDataValue = 3,
+    ServerDeviceFailure = 4,
+    Acknowledge = 5,
+    ServerDeviceBusy = 6,
+    MemoryParityError = 7,
+    GatewayPathUnavailable = 0xa,
+    GatewayTargetDeviceFailedToRespond = 0xb,
+}
+
+fn read_modbus_holding_register(addr: u16) -> Result<u16, ModbusErrorCode> {
+    if addr == 1 {
+        return Ok(42)
+    }
+    Err(ModbusErrorCode::IllegalDataAddress)
+}
+
+fn read_modbus_input_register(addr: u16) -> Result<u16, ModbusErrorCode> {
+    if addr == 2 {
+        return Ok(42)
+    }
+    Err(ModbusErrorCode::IllegalDataAddress)
+}
+
+fn write_modbus_register(addr: u16, value: u16) -> Result<(), ModbusErrorCode> {
+    Err(ModbusErrorCode::IllegalDataAddress)
+}
+
+fn modbus_reply_error(rxbuf: &Vec<u8, 32>, txbuf: &mut Vec<u8, 32>, code: ModbusErrorCode) {
+    txbuf.clear();
+    txbuf.push(rxbuf[0]).unwrap();
+    txbuf.push(rxbuf[1] | 0x80).unwrap();
+    txbuf.push(code as u8).unwrap();
+}
+
+fn handle_modbus_frame2(rxbuf: &Vec<u8, 32>, txbuf: &mut Vec<u8, 32>) -> Result<(), ModbusErrorCode> {
+    use ModbusErrorCode::*;
+
+    info!("Modbus frame: {:?}", rxbuf.as_slice());
+
+    match rxbuf[1] {
+        0x03 => {
+            // read holding registers
+            if rxbuf.len() != 8 {
+                // we shouldn't get here
+                return Err(ServerDeviceFailure);
+            }
+            let start = ((rxbuf[2] as u16) << 8) | rxbuf[3] as u16;
+            let quantity = ((rxbuf[4] as u16) << 8) | rxbuf[5] as u16;
+            if quantity as usize > (txbuf.capacity() - 5) / 2 || quantity >= 128 {
+                return Err(IllegalDataValue); // is that right?
+            }
+            txbuf.push(rxbuf[0]).or(Err(ServerDeviceFailure))?;
+            txbuf.push(rxbuf[1]).or(Err(ServerDeviceFailure))?;
+            txbuf.push((quantity*2) as u8).or(Err(ServerDeviceFailure))?;
+            for i in 0..quantity {
+                let value = read_modbus_holding_register(start + i)?;
+                txbuf.push((value >> 8) as u8).or(Err(ServerDeviceFailure))?;
+                txbuf.push((value & 0xff) as u8).or(Err(ServerDeviceFailure))?;
+            }
+            Ok(())
+        },
+        0x04 => {
+            // read input registers
+            if rxbuf.len() != 8 {
+                // we shouldn't get here
+                return Err(ServerDeviceFailure);
+            }
+            let start = ((rxbuf[2] as u16) << 8) | rxbuf[3] as u16;
+            let quantity = ((rxbuf[4] as u16) << 8) | rxbuf[5] as u16;
+            if quantity as usize > (txbuf.capacity() - 5) / 2 || quantity >= 128 {
+                return Err(IllegalDataValue); // is that right?
+            }
+            txbuf.push(rxbuf[0]).or(Err(ServerDeviceFailure))?;
+            txbuf.push(rxbuf[1]).or(Err(ServerDeviceFailure))?;
+            txbuf.push((quantity*2) as u8).or(Err(ServerDeviceFailure))?;
+            for i in 0..quantity {
+                let value = read_modbus_input_register(start + i)?;
+                txbuf.push((value >> 8) as u8).or(Err(ServerDeviceFailure))?;
+                txbuf.push((value & 0xff) as u8).or(Err(ServerDeviceFailure))?;
+            }
+            Ok(())
+        },
+        0x06 => {
+            // write register
+            Err(IllegalDataAddress)
+        },
+        0x10 => {
+            // write multiple registers
+            Err(IllegalDataAddress)
+        },
+        _ => {
+            Err(IllegalFunction)
+        },
+    }
+}
+
+fn handle_modbus_frame(rxbuf: &Vec<u8, 32>, txbuf: &mut Vec<u8, 32>) {
+    match handle_modbus_frame2(rxbuf, txbuf) {
+        Ok(()) => {
+            if txbuf.capacity() - txbuf.len() < 2 {
+                // We don't have enough space for the CRC so reply with error instead.
+                modbus_reply_error(rxbuf, txbuf, ModbusErrorCode::ServerDeviceFailure);
+            }
+        },
+        Err(code) => {
+            modbus_reply_error(rxbuf, txbuf, code);
+        }
+    }
+
+    const CRC: Crc<u16> = Crc::<u16>::new(&CRC_16_MODBUS);
+    let x = CRC.checksum(txbuf.as_slice());
+    txbuf.push((x & 0xff) as u8).unwrap();
+    txbuf.push((x >> 8) as u8).unwrap();
+}
+
+enum ModbusFrameLength {
+    NeedMoreData(u16),
+    Length(u16),
+    Unknown,
+}
+
+//FIXME This won't work if this is a response frame!
+fn get_modbus_frame_length(rxbuf: &[u8]) -> ModbusFrameLength {
+    use ModbusFrameLength::*;
+
+    if rxbuf.len() < 3 {
+        return NeedMoreData(3);
+    }
+
+    match rxbuf[1] {
+        0x01..=0x06 => Length(8),
+        0x0f | 0x10 => if rxbuf.len() == 7 { Length(9 + rxbuf[6] as u16) } else { NeedMoreData(7) },
+        _ => Unknown,
+    }
+}
+
+const CRC: Crc<u16> = Crc::<u16>::new(&CRC_16_MODBUS);
+const TX_BUF_LENGTH: usize = 32;
+
+pub struct ModbusServer {
+    rxbuf: Vec<u8, 32>,
+    rxcrc: Digest<'static, u16>,
+    rx_expected_bytes: ModbusFrameLength,
+    rx_received_bytes: u16,
+    txbuf: Vec<u8, TX_BUF_LENGTH>,
+}
+
+impl ModbusServer {
+    pub fn new() -> ModbusServer {
+        ModbusServer { 
+            rxbuf: Vec::new(),
+            rxcrc: CRC.digest(),
+            rx_expected_bytes: ModbusFrameLength::NeedMoreData(3),
+            rx_received_bytes: 0,
+            txbuf: Vec::new(),
+        }
+    }
+}
+
+impl RS485Handler for ModbusServer {
+    //type CommandFuture = !;
+    const TX_BUF_LENGTH: usize = TX_BUF_LENGTH;
+
+    fn on_rx<F>(self: &mut Self, rx: Result<u8, uart::Error>, reply: Option<F>)
+        where F: FnOnce(&[u8]) {
+        match rx {
+            Ok(rx_char) => {
+                info!("RX {:?}", rx_char);
+
+                self.rxcrc.update(&[rx_char]);
+                if !self.rxbuf.is_full() {
+                    self.rxbuf.push(rx_char).unwrap_or_default();
+                }
+                self.rx_received_bytes += 1;
+
+                if let ModbusFrameLength::NeedMoreData(x) = self.rx_expected_bytes {
+                    if x == self.rx_received_bytes {
+                        self.rx_expected_bytes = get_modbus_frame_length(self.rxbuf.as_slice());
+                        match self.rx_expected_bytes {
+                            ModbusFrameLength::Unknown => {
+                                //FIXME Wait for pause.
+                            },
+                            _ => {}
+                        }
+                    }
+                }
+                if let ModbusFrameLength::Length(x) = self.rx_expected_bytes {
+                    if x == self.rx_received_bytes {
+                        let calculated_crc = self.rxcrc.clone().finalize();
+                        const CORRECT_CRC: u16 = 0;  // because we include the CRC bytes in our calculation
+                        if calculated_crc != CORRECT_CRC {
+                            info!("CRC: {:04x} (should be zero)", calculated_crc);
+                        }
+
+                        //FIXME In case of CRC mismatch, wait for gap/idle of >=1.5 chars.
+                        const OUR_ADDRESS: u8 = 1;
+                        if self.rxbuf[0] == OUR_ADDRESS && calculated_crc == CORRECT_CRC {
+                            self.txbuf.clear();
+                            handle_modbus_frame(&self.rxbuf, &mut self.txbuf);
+
+                            if !self.txbuf.is_empty() {
+                                info!("Modbus reply: {:?}", self.txbuf);
+                                match reply {
+                                    Option::Some(reply) => reply(self.txbuf.as_slice()),
+                                    Option::None => warn!("Cannot send reply because a reply is already in progress!"),
+                                }
+                            }
+                        }
+
+                        self.rxbuf.clear();
+                        self.rxcrc = CRC.digest();
+                        self.rx_expected_bytes = ModbusFrameLength::NeedMoreData(3);
+                        self.rx_received_bytes = 0;
+                    }
+                }
+            },
+            Err(e) => {
+                info!("RX error {:?}", e);
+        
+                //FIXME wait for gap/idle of >=1.5 chars.
+                self.rxbuf.clear();
+                self.rxcrc = CRC.digest();
+                self.rx_expected_bytes = ModbusFrameLength::NeedMoreData(3);
+                self.rx_received_bytes = 0;
+            }
+        }
+    }
+
+    fn on_idle(self: &mut Self) {
+        if !self.rxbuf.is_empty() {
+            warn!("Partial frame in rx buffer, cut short by inter-byte gap: {:?}", self.rxbuf);
+        }
+
+        self.rxbuf.clear();
+        self.rxcrc = CRC.digest();
+        self.rx_expected_bytes = ModbusFrameLength::NeedMoreData(3);
+        self.rx_received_bytes = 0;
+    }
+
+    fn on_tx_done(self: &mut Self) {
+        //TODO
+    }
+
+    fn on_autobaud_success(self: &mut Self, baudrate: f32) {
+        //TODO
+        info!("Guessed baud rate: {}", baudrate);
+    }
+}
diff --git a/firmware/rust1/src/rs485.rs b/firmware/rust1/src/rs485.rs
index 38915c84efdcb251d7475bf2f277e5ac76ae3efa..d3e3ad2d2c3fe34e108ce602c495de14244fcaee 100644
--- a/firmware/rust1/src/rs485.rs
+++ b/firmware/rust1/src/rs485.rs
@@ -13,20 +13,31 @@ use embassy_rp::Peripheral;
 use {defmt_rtt as _, panic_probe as _};
 use fixed::traits::ToFixed;
 use fixed_macro::types::U56F8;
-use heapless::Vec;
-use crc::{Crc, CRC_16_MODBUS};
+use futures::Future;
 
 use crate::dont_abort::DontAbort;
 use crate::dont_abort::DontAbortMode::*;
 
-pub struct RS485 {
+pub trait RS485Handler {
+    //type CommandFuture: Future<Output = ()>;
+    const TX_BUF_LENGTH: usize;
+
+    fn on_rx<F>(self: &mut Self, rx: Result<u8, uart::Error>, reply: Option<F>)
+        where F: FnOnce(&[u8]);
+    fn on_idle(self: &mut Self);
+    fn on_tx_done(self: &mut Self);
+    fn on_autobaud_success(self: &mut Self, baudrate: f32);
+}
+
+pub struct RS485<H: RS485Handler> {
     pio: peripherals::PIO0,
     rx_pin: peripherals::PIN_17,
     tx_pin: peripherals::PIN_16,
     tx_en_pin: peripherals::PIN_15,
     dma_channel: peripherals::DMA_CH0,
     tx_dma_channel: peripherals::DMA_CH2,
-    rx: UartRx<'static, UART0, uart::Async>
+    rx: UartRx<'static, UART0, uart::Async>,
+    handler: H
 }
 
 fn pin_io<P: gpio::Pin>(pin: &P) -> pac::io::Gpio {
@@ -52,152 +63,13 @@ async fn debug_print_pio_addr(sm: pac::pio::StateMachine) {
     }
 }
 
-#[repr(u8)]
-pub enum ModbusErrorCode {
-    IllegalFunction = 1,
-    IllegalDataAddress = 2,
-    IllegalDataValue = 3,
-    ServerDeviceFailure = 4,
-    Acknowledge = 5,
-    ServerDeviceBusy = 6,
-    MemoryParityError = 7,
-    GatewayPathUnavailable = 0xa,
-    GatewayTargetDeviceFailedToRespond = 0xb,
-}
-
-fn read_modbus_holding_register(addr: u16) -> Result<u16, ModbusErrorCode> {
-    if addr == 1 {
-        return Ok(42)
-    }
-    Err(ModbusErrorCode::IllegalDataAddress)
-}
-
-fn read_modbus_input_register(addr: u16) -> Result<u16, ModbusErrorCode> {
-    if addr == 2 {
-        return Ok(42)
-    }
-    Err(ModbusErrorCode::IllegalDataAddress)
-}
-
-fn write_modbus_register(addr: u16, value: u16) -> Result<(), ModbusErrorCode> {
-    Err(ModbusErrorCode::IllegalDataAddress)
-}
-
-fn modbus_reply_error(rxbuf: &Vec<u8, 32>, txbuf: &mut Vec<u8, 32>, code: ModbusErrorCode) {
-    txbuf.clear();
-    txbuf.push(rxbuf[0]).unwrap();
-    txbuf.push(rxbuf[1] | 0x80).unwrap();
-    txbuf.push(code as u8).unwrap();
-}
-
-fn handle_modbus_frame2(rxbuf: &Vec<u8, 32>, txbuf: &mut Vec<u8, 32>) -> Result<(), ModbusErrorCode> {
-    use ModbusErrorCode::*;
-
-    info!("Modbus frame: {:?}", rxbuf.as_slice());
-
-    match rxbuf[1] {
-        0x03 => {
-            // read holding registers
-            if rxbuf.len() != 8 {
-                // we shouldn't get here
-                return Err(ServerDeviceFailure);
-            }
-            let start = ((rxbuf[2] as u16) << 8) | rxbuf[3] as u16;
-            let quantity = ((rxbuf[4] as u16) << 8) | rxbuf[5] as u16;
-            if quantity as usize > (txbuf.capacity() - 5) / 2 || quantity >= 128 {
-                return Err(IllegalDataValue); // is that right?
-            }
-            txbuf.push(rxbuf[0]).or(Err(ServerDeviceFailure))?;
-            txbuf.push(rxbuf[1]).or(Err(ServerDeviceFailure))?;
-            txbuf.push((quantity*2) as u8).or(Err(ServerDeviceFailure))?;
-            for i in 0..quantity {
-                let value = read_modbus_holding_register(start + i)?;
-                txbuf.push((value >> 8) as u8).or(Err(ServerDeviceFailure))?;
-                txbuf.push((value & 0xff) as u8).or(Err(ServerDeviceFailure))?;
-            }
-            Ok(())
-        },
-        0x04 => {
-            // read input registers
-            if rxbuf.len() != 8 {
-                // we shouldn't get here
-                return Err(ServerDeviceFailure);
-            }
-            let start = ((rxbuf[2] as u16) << 8) | rxbuf[3] as u16;
-            let quantity = ((rxbuf[4] as u16) << 8) | rxbuf[5] as u16;
-            if quantity as usize > (txbuf.capacity() - 5) / 2 || quantity >= 128 {
-                return Err(IllegalDataValue); // is that right?
-            }
-            txbuf.push(rxbuf[0]).or(Err(ServerDeviceFailure))?;
-            txbuf.push(rxbuf[1]).or(Err(ServerDeviceFailure))?;
-            txbuf.push((quantity*2) as u8).or(Err(ServerDeviceFailure))?;
-            for i in 0..quantity {
-                let value = read_modbus_input_register(start + i)?;
-                txbuf.push((value >> 8) as u8).or(Err(ServerDeviceFailure))?;
-                txbuf.push((value & 0xff) as u8).or(Err(ServerDeviceFailure))?;
-            }
-            Ok(())
-        },
-        0x06 => {
-            // write register
-            Err(IllegalDataAddress)
-        },
-        0x10 => {
-            // write multiple registers
-            Err(IllegalDataAddress)
-        },
-        _ => {
-            Err(IllegalFunction)
-        },
-    }
-}
-
-fn handle_modbus_frame(rxbuf: &Vec<u8, 32>, txbuf: &mut Vec<u8, 32>) {
-    match handle_modbus_frame2(rxbuf, txbuf) {
-        Ok(()) => {
-            if txbuf.capacity() - txbuf.len() < 2 {
-                // We don't have enough space for the CRC so reply with error instead.
-                modbus_reply_error(rxbuf, txbuf, ModbusErrorCode::ServerDeviceFailure);
-            }
-        },
-        Err(code) => {
-            modbus_reply_error(rxbuf, txbuf, code);
-        }
-    }
-
-    const CRC: Crc<u16> = Crc::<u16>::new(&CRC_16_MODBUS);
-    let x = CRC.checksum(txbuf.as_slice());
-    txbuf.push((x & 0xff) as u8).unwrap();
-    txbuf.push((x >> 8) as u8).unwrap();
-}
-
-enum ModbusFrameLength {
-    NeedMoreData(u16),
-    Length(u16),
-    Unknown,
-}
-
-//FIXME This won't work if this is a response frame!
-fn get_modbus_frame_length(rxbuf: &[u8]) -> ModbusFrameLength {
-    use ModbusFrameLength::*;
-
-    if rxbuf.len() < 3 {
-        return NeedMoreData(3);
-    }
-
-    match rxbuf[1] {
-        0x01..=0x06 => Length(8),
-        0x0f | 0x10 => if rxbuf.len() == 7 { Length(9 + rxbuf[6] as u16) } else { NeedMoreData(7) },
-        _ => Unknown,
-    }
-}
-
-impl RS485 {
+impl<H: RS485Handler> RS485<H> {
     pub fn new(
             uart: UART0, rx_pin: peripherals::PIN_17, tx_pin: peripherals::PIN_16, tx_en_pin: peripherals::PIN_15,
             rx_irq: UART0_IRQ, rx_dma_channel: peripherals::DMA_CH1, uart_config: uart::Config,
-            pio: peripherals::PIO0, dma_channel: peripherals::DMA_CH0, tx_dma_channel: peripherals::DMA_CH2
-        ) -> RS485 {
+            pio: peripherals::PIO0, dma_channel: peripherals::DMA_CH0, tx_dma_channel: peripherals::DMA_CH2,
+            handler: H
+        ) -> RS485<H> {
         // SAFETY: The auto-baud will only read from this pin and we will set it back to UART mode after initialising the PIO.
         let rx_pin_for_autobaud = unsafe { rx_pin.clone_unchecked() };
 
@@ -208,7 +80,7 @@ impl RS485 {
             rx_dma_channel,
             uart_config,
         );
-        RS485 { pio, rx_pin: rx_pin_for_autobaud, tx_pin, tx_en_pin, dma_channel, tx_dma_channel, rx: uart_rx }
+        RS485 { pio, rx_pin: rx_pin_for_autobaud, tx_pin, tx_en_pin, dma_channel, tx_dma_channel, rx: uart_rx, handler }
     }
 
     pub async fn run_task(mut self: Self) {
@@ -237,7 +109,7 @@ impl RS485 {
 
             // Wait for pin to be high for a longer time (UART is idle), copy waiting time to X
             // and decrement until done, start again when pin changes.
-            "wait_idle:",
+            "public wait_idle:",
             "wait 1 pin 0",
             "mov x, osr",
             "continue_wait_idle:",
@@ -249,6 +121,8 @@ impl RS485 {
             // We have a long 1, i.e. UART is idle. Wait for start bit.
             "have_long_high:",
             "irq set 1",          // notify MCU in case they want to know about this (end of Modbus frame)
+            "public wait_for_not_idle:",
+            ".define public wait_for_not_idle wait_for_not_idle",
             "wait 0 pin 0",
             "nop [4]",
             "jmp pin wait_idle",  // abort if zero was just a glitch
@@ -366,7 +240,9 @@ impl RS485 {
             pin_io(&self.rx_pin).ctrl().write(|w| w.set_funcsel(embassy_rp::pac::io::vals::Gpio17ctrlFuncsel::UART0_RX.0));
         }
 
-        info!("Program size for auto-baud: {}, for tx: {}", prg_autobaud.program.code.len(), prg_tx.program.code.len());
+        info!("Program size for auto-baud: {}, for tx: {}, wait_for_not_idle is at: {}",
+            prg_autobaud.program.code.len(), prg_tx.program.code.len(),
+            prg_autobaud.public_defines.wait_for_not_idle);
 
 
         let relocated = RelocatedProgram::new(&prg_tx.program);
@@ -390,16 +266,9 @@ impl RS485 {
         sm1.set_pin_dirs(embassy_rp::pio::Direction::Out, &[&tx_en_pin_pio, &tx_pin_pio]);
         sm1.set_enable(true);
 
-        //FIXME Can we split Modbus parts from UART/RS485?
-        let mut rxbuf = Vec::<u8, 32>::new();
-        const CRC: Crc<u16> = Crc::<u16>::new(&CRC_16_MODBUS);
-        let mut rxcrc = CRC.digest();
-        let mut rx_expected_bytes = ModbusFrameLength::NeedMoreData(3);
-        let mut rx_received_bytes = 0u16;
-
-        const TX_BUF_LENGTH: usize = 32;
-        let mut txbuf = Vec::<u8, TX_BUF_LENGTH>::new();
-        let mut tx_data = [0; TX_BUF_LENGTH+1];
+        const TX_BUF_LENGTH: usize = 32; //H::TX_BUF_LENGTH;
+        let mut tx_data = [0u32; TX_BUF_LENGTH+1];
+        let mut tx_in_progress = false;
 
         let mut dma_in_ref = self.dma_channel.into_ref();
         let mut dma_tx_ref = self.tx_dma_channel.into_ref();
@@ -407,18 +276,18 @@ impl RS485 {
         let mut bit_index = 0;
         let mut rx_buf_one = [0; 1];
         let mut rx_future = DontAbort::new(self.rx.read(&mut rx_buf_one), PanicIfReused);
-        // We transmit with length zero to have a dummy no-op future with the right type. This seems to work ok.
-        let mut tx_future = DontAbort::new(sm1.tx().dma_push(dma_tx_ref.reborrow(), &tx_data[0..0]), HangIfReused);
+        let mut tx_future = DontAbort::hang();
         loop {
             let x = select4(
                 irq0.wait(),
                 sm.rx().dma_pull(dma_in_ref.reborrow(), &mut din),
                 //debug_print_pio_addr(embassy_rp::pac::PIO0.sm(0)),
                 tx_future.continue_wait(),
-                select(
+                select3(
                     rx_future.continue_wait(),
                     //debug_print_pio_addr(embassy_rp::pac::PIO0.sm(1)),
                     core::future::pending::<()>(),
+                    irq1.wait(),
                 ),
             ).await;
             match x {
@@ -489,7 +358,7 @@ impl RS485 {
                     if got_to_eight && ok {
                         let millis = sum as f32 / SM_FREQ as f32 * 1000. * 2. / 8.;
                         let baud = 1000. / millis;
-                        info!("Guessed baud rate: {}", baud);
+                        self.handler.on_autobaud_success(baud);
                     }
                 },
                 Either4::Second(_) => {
@@ -505,86 +374,45 @@ impl RS485 {
                     }
                 },
                 Either4::Third(_) => {
-                    //drop(tx_future);
-                    //tx_future = DontAbort::new(sm1.tx().dma_push(dma_tx_ref.reborrow(), &tx_data), HangIfReused);
+                    tx_in_progress = false;
+                    self.handler.on_tx_done();
                 },
-                Either4::Fourth(Either::First(x)) => {
+                Either4::Fourth(Either3::First(x)) => {
                     drop(rx_future);
-                    match x {
-                        Result::Ok(()) => {
-                            info!("RX {:?}", rx_buf_one);
-                            let rx_char = rx_buf_one[0];
-
-                            rxcrc.update(&[rx_char]);
-                            if !rxbuf.is_full() {
-                                rxbuf.push(rx_char).unwrap_or_default();
-                            }
-                            rx_received_bytes += 1;
-
-                            if let ModbusFrameLength::NeedMoreData(x) = rx_expected_bytes {
-                                if x == rx_received_bytes {
-                                    rx_expected_bytes = get_modbus_frame_length(rxbuf.as_slice());
-                                    match rx_expected_bytes {
-                                        ModbusFrameLength::Unknown => {
-                                            //FIXME Wait for pause.
-                                        },
-                                        _ => {}
-                                    }
-                                }
-                            }
-                            if let ModbusFrameLength::Length(x) = rx_expected_bytes {
-                                if x == rx_received_bytes {
-                                    let calculated_crc = rxcrc.finalize();
-                                    rxcrc = CRC.digest();  // re-init to placate borrow checker
-                                    const CORRECT_CRC: u16 = 0;  // because we include the CRC bytes in our calculation
-                                    if calculated_crc != CORRECT_CRC {
-                                        info!("CRC: {:04x} (should be zero)", calculated_crc);
-                                    }
-
-                                    //FIXME In case of CRC mismatch, wait for gap/idle of >=1.5 chars.
-                                    const OUR_ADDRESS: u8 = 1;
-                                    if rxbuf[0] == OUR_ADDRESS && calculated_crc == CORRECT_CRC {
-                                        txbuf.clear();
-                                        handle_modbus_frame(&rxbuf, &mut txbuf);
-
-                                        if !txbuf.is_empty() {
-                                            drop(tx_future);
-
-                                            tx_data[0] = (txbuf.len() - 1) as u32;
-                                            for i in 0..txbuf.len() {
-                                                let x = txbuf[i] & 0xff;
-                                                let mut parity = 0;
-                                                for j in 0..8 {
-                                                    parity ^= x>>j;
-                                                }
-                                                tx_data[i + 1] = x as u32 | (((parity as u32) & 1) << 8);
-                                            }
-
-                                            info!("Modbus reply: {:?}", txbuf);
-
-                                            tx_future = DontAbort::new(sm1.tx().dma_push(dma_tx_ref.reborrow(),
-                                                &tx_data[0..(txbuf.len()+1)]), HangIfReused);
-                                        }
-                                    }
-
-                                    rxbuf.clear();
-                                    //rxcrc = CRC.digest();  // re-init already done above, don't repeat to avoid warning
-                                    rx_expected_bytes = ModbusFrameLength::NeedMoreData(3);
-                                    rx_received_bytes = 0;
+                    let rx_char = x.map(|_| -> u8 { rx_buf_one[0] });
+                    rx_future = DontAbort::new(self.rx.read(&mut rx_buf_one), PanicIfReused);
+
+                    if tx_in_progress {
+                        // use a dummy closure to fix the type of the option because I don't know a better way...
+                        let dummy = |_txdata: &[u8]| {};
+                        self.handler.on_rx(rx_char, if false {Option::Some(dummy)} else {Option::None});
+                    } else {
+                        drop(tx_future);
+                        tx_future = DontAbort::hang();
+                        let mut txlen = Option::None;
+
+                        self.handler.on_rx(rx_char, Option::Some(|txdata: &[u8]| {
+                            tx_data[0] = (txdata.len() - 1) as u32;
+                            for i in 0..txdata.len() {
+                                let x = txdata[i] & 0xff;
+                                let mut parity = 0;
+                                for j in 0..8 {
+                                    parity ^= x>>j;
                                 }
+                                tx_data[i + 1] = x as u32 | (((parity as u32) & 1) << 8);
                             }
-                        },
-                        Result::Err(e) => {
-                            info!("RX error {:?}", e);
-
-                            //FIXME wait for gap/idle of >=1.5 chars.
-                            rxbuf.clear();
-                            rxcrc = CRC.digest();
-                            rx_expected_bytes = ModbusFrameLength::NeedMoreData(3);
-                            rx_received_bytes = 0;
-                        },
+                            txlen = Option::Some(txdata.len());
+                        }));
+                        if let Option::Some(len) = txlen {
+                            //FIXME Wait for idle and then wait again from `wait_idle` before sending. We should have a gap of 3.5 chars.
+                            tx_future = DontAbort::new(sm1.tx().dma_push(dma_tx_ref.reborrow(),
+                                &tx_data[0..(len+1)]), HangIfReused);
+                            tx_in_progress = true;
+                        }
                     }
-                    rx_future = DontAbort::new(self.rx.read(&mut rx_buf_one), PanicIfReused);
+                },
+                Either4::Fourth(Either3::Third(())) => {
+                    self.handler.on_idle();
                 },
                 _ => {
                 },