async_modbus/
request.rs

1//! Modbus request messages. You can use [`zerocopy::IntoBytes`] to convert
2//! them into byte buffers for sending.
3//!
4//! ```
5//! # use async_modbus::request::WriteHolding;
6//! # use hex_literal::hex;
7//! use async_modbus::zerocopy::IntoBytes;
8//!
9//! let message = WriteHolding::new(0x01, 0x10BC, 12345);
10//! assert_eq!(message.as_bytes(), hex!("01 06 10 BC 30 39 98 FC"));
11//! ```
12
13use super::util::modbus_message;
14use zerocopy::{IntoBytes, big_endian};
15use zerocopy_derive::*;
16
17modbus_message! {
18    /// Write single holding register request
19    WriteHolding {
20        function_code: 0x06,
21        register: big_endian::U16,
22        value: big_endian::U16,
23    }
24}
25
26impl WriteHolding {
27    /// Create a new write holding register request
28    pub fn new(addr: u8, register: u16, value: u16) -> Self {
29        Self::new_inner(addr, register.into(), value.into())
30    }
31}
32
33modbus_message! {
34    /// Read holding registers request
35    ReadHoldings {
36        function_code: 0x03,
37        starting_register: big_endian::U16,
38        n_registers: big_endian::U16,
39    }
40}
41
42impl ReadHoldings {
43    /// Create a new read holding registers request
44    pub fn new(addr: u8, starting_register: u16, n_registers: u16) -> Self {
45        Self::new_inner(addr, starting_register.into(), n_registers.into())
46    }
47}
48
49modbus_message! {
50    /// Write multiple holding registers request
51    WriteHoldings<const N: usize> {
52        function_code: 0x10,
53        starting_register: big_endian::U16,
54        n_registers: big_endian::U16,
55        data_bytes: u8,
56        data: [big_endian::U16; N],
57    }
58}
59
60impl<const N: usize> WriteHoldings<N> {
61    /// Create a new write multiple holding registers request
62    pub fn new(addr: u8, starting_register: u16, data: [u16; N]) -> Self {
63        Self::new_inner(
64            addr,
65            starting_register.into(),
66            big_endian::U16::new(N as u16),
67            (N as u8) * 2,
68            data.map(big_endian::U16::new),
69        )
70    }
71}
72
73modbus_message! {
74    /// Read input registers request
75    ReadInputs {
76        function_code: 0x04,
77        starting_register: big_endian::U16,
78        n_registers: big_endian::U16,
79    }
80}
81
82impl ReadInputs {
83    /// Create a new read input registers request
84    pub fn new(addr: u8, starting_register: u16, n_registers: u16) -> Self {
85        Self::new_inner(addr, starting_register.into(), n_registers.into())
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use hex_literal::hex;
92    use zerocopy::IntoBytes;
93
94    use super::*;
95
96    #[test]
97    fn test_write_holding_register() {
98        let msg = WriteHolding::new(0x01, 0x1001, 0x03E8);
99        assert_eq!(msg.as_bytes(), hex!("01 06 10 01 03 E8 DC 74"),);
100    }
101
102    #[test]
103    fn test_read_holding_registers() {
104        let msg = ReadHoldings::new(0x01, 0x1001, 1000);
105        assert_eq!(msg.as_bytes(), hex!("01 03 10 01 03 E8 10 74"),);
106    }
107}