From c9b44d2644993fa417c9a69fe51cf227c2abdabd Mon Sep 17 00:00:00 2001
From: Benjamin Koch <bbbsnowball@gmail.com>
Date: Tue, 30 May 2023 01:49:18 +0200
Subject: [PATCH] implement readwrite_registers

---
 firmware/rust1/rtumaster_pymodbus.py |  5 +++++
 firmware/rust1/src/bin/heizung.rs    | 15 +++++++++++++--
 firmware/rust1/src/modbus_server.rs  | 26 ++++++++++++++++++++++++++
 3 files changed, 44 insertions(+), 2 deletions(-)

diff --git a/firmware/rust1/rtumaster_pymodbus.py b/firmware/rust1/rtumaster_pymodbus.py
index 27cc982..a5cb29d 100644
--- a/firmware/rust1/rtumaster_pymodbus.py
+++ b/firmware/rust1/rtumaster_pymodbus.py
@@ -6,6 +6,7 @@
 import logging
 import time
 import sys
+import struct
 import pymodbus
 from pymodbus.exceptions import ModbusException
 from pymodbus.client import ModbusSerialClient
@@ -49,6 +50,10 @@ def main():
         logger.info("read scratch reg: %s: %r", x, x.registers)
         assert x.registers == [0x17]
 
+        x = client.readwrite_registers(read_address=0, read_count=3, write_address=1,
+            write_registers=[struct.unpack(">H", b"PI")[0], 103], slave=DEVICE_ADDR)
+        logger.info("read/write regs: %s: %r", x, x.registers)
+
         x = client.read_file_record([
             # documentation says tuple of: Reference type, File number, Record Number, Record Length
             # but actually an object is expected:
diff --git a/firmware/rust1/src/bin/heizung.rs b/firmware/rust1/src/bin/heizung.rs
index 7008396..ef40a5a 100644
--- a/firmware/rust1/src/bin/heizung.rs
+++ b/firmware/rust1/src/bin/heizung.rs
@@ -461,8 +461,16 @@ impl<'a> ModbusRegisters for ModBusRegs<'a> {
         match addr {
             0 => { self.device_state = value; Ok(self.device_state) },
             1 => {
-                const OK: u16 = 'O' as u16 | (('K' as u16) << 8);
-                const UP: u16 = 'U' as u16 | (('P' as u16) << 8);
+                const fn be(s: &str) -> u16 {
+                    core::assert!(s.len() == 2);
+                    core::assert!(s.as_bytes().len() == 2);
+                    let x = s.as_bytes()[0];
+                    let y = s.as_bytes()[1];
+                    u16::from_be_bytes([x, y])
+                }
+                const OK: u16 = be("OK");  // mark booted
+                const UP: u16 = be("UP");  // mark updated -> swap to DFU on next reset
+                const PI: u16 = be("PI");  // ping / no-op
                 match value {
                     OK => {
                         self.uf2updater.mark_booted().map(|_| value)
@@ -473,6 +481,9 @@ impl<'a> ModbusRegisters for ModBusRegs<'a> {
                     UP => {
                         Ok(0)
                     },
+                    PI => {
+                        Ok(value)
+                    },
                     _ => Err(ModbusErrorCode::IllegalDataValue),
                 }
             },
diff --git a/firmware/rust1/src/modbus_server.rs b/firmware/rust1/src/modbus_server.rs
index 66d433c..fa1c6d2 100644
--- a/firmware/rust1/src/modbus_server.rs
+++ b/firmware/rust1/src/modbus_server.rs
@@ -138,6 +138,7 @@ fn get_modbus_frame_length(rxbuf: &[u8]) -> ModbusFrameLength {
         0x0f | 0x10 => if rxbuf.len() == 7 { Length(9 + rxbuf[6] as u16) } else { NeedMoreData(7) },
         0x14 | 0x15 => if rxbuf.len() == 3 { Length(5 + rxbuf[2] as u16) } else { NeedMoreData(3) },
         0x16 => Length(10),
+        0x17 => if rxbuf.len() == 11 { Length(13 + rxbuf[10] as u16) } else { NeedMoreData(11) },
         _ => Unknown,
     }
 }
@@ -428,6 +429,31 @@ impl<REGS: ModbusRegisters> ModbusServer<REGS> {
                 push_many(txbuf, &rxbuf[0..rxbuf.len()-2])?;
                 Ok(())
             },
+            0x17 => {
+                let read_addr = rx.read_u16be()?;
+                let read_count = rx.read_u16be()?;
+                let write_addr = rx.read_u16be()?;
+                let write_count = rx.read_u16be()?;
+                let write_byte_count = rx.read_u8()?;
+                if read_count < 1 || read_count > 0x7d || write_count < 1 || write_count > 0x79 || write_byte_count as u16 != write_count * 2 {
+                    return Err(IllegalDataValue)
+                }
+
+                for i in 0..write_count {
+                    info!("write {}", write_addr+i);
+                    self.regs.write_register(device_addr, write_addr + i, rx.read_u16be()?)?;
+                }
+
+                push_many(txbuf, &rxbuf[0..2])?;
+                push(txbuf, read_count as u8 * 2)?;
+                for i in 0..read_count {
+                    info!("read {}", read_addr+i);
+                    let value = self.regs.read_holding_register(device_addr, read_addr + i)?;
+                    push_u16be(txbuf, value)?;
+                }
+                info!("done");
+                Ok(())
+            },
             _ => {
                 Err(IllegalFunction)
             },
-- 
GitLab