diff --git a/firmware/rust1/src/bin/heizung.rs b/firmware/rust1/src/bin/heizung.rs index f7f8dbbe3101dcf82d0f3d1c24962a99a0cb0d58..12715b9b5c56e0405d2e4fcfff7469f3fd225c65 100644 --- a/firmware/rust1/src/bin/heizung.rs +++ b/firmware/rust1/src/bin/heizung.rs @@ -19,7 +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::modbus_server::{ModbusServer, ModbusRegisters, ModbusErrorCode}; use heizung::rs485::RS485; #[embassy_executor::task] @@ -72,10 +72,33 @@ async fn beeper_task(pwm_channel: embassy_rp::peripherals::PWM_CH3, beeper_pin: } #[embassy_executor::task] -async fn uart_task(this: RS485<ModbusServer>) { +async fn uart_task(this: RS485<ModbusServer<ModBusRegs>>) { this.run_task().await; } +struct ModBusRegs { +} + +impl ModbusRegisters for ModBusRegs { + fn read_modbus_holding_register(self: &mut Self, addr: u16) -> Result<u16, ModbusErrorCode> { + if addr == 1 { + return Ok(42) + } + Err(ModbusErrorCode::IllegalDataAddress) + } + + fn read_modbus_input_register(self: &mut Self, addr: u16) -> Result<u16, ModbusErrorCode> { + if addr == 2 { + return Ok(42) + } + Err(ModbusErrorCode::IllegalDataAddress) + } + + fn write_modbus_register(self: &mut Self, addr: u16, value: u16) -> Result<(), ModbusErrorCode> { + Err(ModbusErrorCode::IllegalDataAddress) + } +} + #[embassy_executor::main] async fn main(spawner: Spawner) { let p = embassy_rp::init(Default::default()); @@ -127,7 +150,7 @@ async fn main(spawner: Spawner) { 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(), + ModbusServer::new(ModBusRegs { }), ); unwrap!(spawner.spawn(uart_task(rs485))); diff --git a/firmware/rust1/src/modbus_server.rs b/firmware/rust1/src/modbus_server.rs index 1b08a5c1f91a9ee56fe503c3b1bd8f47f5184d4a..ce71a295e49155e1118ac0b2bbc62497f6c10d76 100644 --- a/firmware/rust1/src/modbus_server.rs +++ b/firmware/rust1/src/modbus_server.rs @@ -20,110 +20,10 @@ pub enum ModbusErrorCode { 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(); +pub trait ModbusRegisters { + fn read_modbus_holding_register(self: &mut Self, addr: u16) -> Result<u16, ModbusErrorCode>; + fn read_modbus_input_register(self: &mut Self, addr: u16) -> Result<u16, ModbusErrorCode>; + fn write_modbus_register(self: &mut Self, addr: u16, value: u16) -> Result<(), ModbusErrorCode>; } enum ModbusFrameLength { @@ -150,27 +50,120 @@ fn get_modbus_frame_length(rxbuf: &[u8]) -> ModbusFrameLength { const CRC: Crc<u16> = Crc::<u16>::new(&CRC_16_MODBUS); const TX_BUF_LENGTH: usize = 32; -pub struct ModbusServer { +pub struct ModbusServer<REGS: ModbusRegisters> { rxbuf: Vec<u8, 32>, rxcrc: Digest<'static, u16>, rx_expected_bytes: ModbusFrameLength, rx_received_bytes: u16, txbuf: Vec<u8, TX_BUF_LENGTH>, + regs: REGS, } -impl ModbusServer { - pub fn new() -> ModbusServer { +impl<REGS: ModbusRegisters> ModbusServer<REGS> { + pub fn new(regs: REGS) -> ModbusServer<REGS> { ModbusServer { rxbuf: Vec::new(), rxcrc: CRC.digest(), rx_expected_bytes: ModbusFrameLength::NeedMoreData(3), rx_received_bytes: 0, txbuf: Vec::new(), + regs, } } + + + fn modbus_reply_error(self: &mut Self, code: ModbusErrorCode) { + self.txbuf.clear(); + self.txbuf.push(self.rxbuf[0]).unwrap(); + self.txbuf.push(self.rxbuf[1] | 0x80).unwrap(); + self.txbuf.push(code as u8).unwrap(); + } + + fn handle_modbus_frame2(self: &mut Self) -> Result<(), ModbusErrorCode> { + use ModbusErrorCode::*; + + let rxbuf = &self.rxbuf; + let txbuf = &mut self.txbuf; + 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 = self.regs.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 = self.regs.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(self: &mut Self) { + match self.handle_modbus_frame2() { + Ok(()) => { + if self.txbuf.capacity() - self.txbuf.len() < 2 { + // We don't have enough space for the CRC so reply with error instead. + self.modbus_reply_error(ModbusErrorCode::ServerDeviceFailure); + } + }, + Err(code) => { + self.modbus_reply_error(code); + } + } + + const CRC: Crc<u16> = Crc::<u16>::new(&CRC_16_MODBUS); + let x = CRC.checksum(self.txbuf.as_slice()); + self.txbuf.push((x & 0xff) as u8).unwrap(); + self.txbuf.push((x >> 8) as u8).unwrap(); + } } -impl RS485Handler for ModbusServer { +impl<REGS: ModbusRegisters> RS485Handler for ModbusServer<REGS> { //type CommandFuture = !; const TX_BUF_LENGTH: usize = TX_BUF_LENGTH; @@ -209,7 +202,7 @@ impl RS485Handler for ModbusServer { 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); + self.handle_modbus_frame(); if !self.txbuf.is_empty() { info!("Modbus reply: {:?}", self.txbuf);