async_modbus/
embedded_io.rs

1//! Client functions for [`embedded_io_async`]-based IO.
2
3use embedded_io_async::{Read, ReadExactError, Write};
4use zerocopy::{FromBytes, Immutable, IntoBytes};
5
6use crate::{
7    Error, request,
8    response::{self, Response},
9};
10
11/// Read multiple holding registers from a Modbus device.
12pub async fn read_holdings<const N: usize, E>(
13    mut serial: impl Read<Error = E> + Write<Error = E>,
14    addr: u8,
15    starting_register: u16,
16) -> Result<[u16; N], Error<E>> {
17    let req = request::ReadHoldings::new(addr, starting_register, N as u16);
18    write_message(&mut serial, &req).await.map_err(Error::Io)?;
19
20    let res: response::ReadHoldings<N> = read_message(&mut serial).await?;
21    let data = res.into_data(&req)?;
22    Ok(data.map(|holding| holding.get()))
23}
24
25/// Write a single holding register to a Modbus device.
26pub async fn write_holding<E>(
27    mut serial: impl Read<Error = E> + Write<Error = E>,
28    addr: u8,
29    register: u16,
30    value: u16,
31) -> Result<(), Error<E>> {
32    let req = request::WriteHolding::new(addr, register, value);
33    write_message(&mut serial, &req).await.map_err(Error::Io)?;
34
35    let res: response::WriteHolding = read_message(&mut serial).await?;
36    Ok(res.into_data(&req)?)
37}
38
39/// Write multiple holding registers to a Modbus device.
40pub async fn write_holdings<const N: usize, E>(
41    mut serial: impl Read<Error = E> + Write<Error = E>,
42    addr: u8,
43    starting_register: u16,
44    data: [u16; N],
45) -> Result<(), Error<E>> {
46    let req = request::WriteHoldings::new(addr, starting_register, data);
47    write_message(&mut serial, &req).await.map_err(Error::Io)?;
48
49    let res: response::WriteHoldings = read_message(&mut serial).await?;
50    Ok(res.into_data(&req)?)
51}
52
53/// Read multiple input registers from a Modbus device.
54pub async fn read_inputs<const N: usize, E>(
55    mut serial: impl Read<Error = E> + Write<Error = E>,
56    addr: u8,
57    starting_register: u16,
58) -> Result<[u16; N], Error<E>> {
59    let req = request::ReadInputs::new(addr, starting_register, N as u16);
60    write_message(&mut serial, &req).await.map_err(Error::Io)?;
61
62    let res: response::ReadInputs<N> = read_message(&mut serial).await?;
63    let data = res.into_data(&req)?;
64    Ok(data.map(|input| input.get()))
65}
66
67async fn write_message<T, E>(mut dst: impl Write<Error = E>, message: &T) -> Result<(), E>
68where
69    T: IntoBytes + Immutable,
70{
71    dst.write_all(message.as_bytes()).await?;
72    dst.flush().await
73}
74
75async fn read_message<T, E>(mut src: impl Read<Error = E>) -> Result<T, ReadExactError<E>>
76where
77    T: FromBytes + IntoBytes,
78{
79    let mut message = T::new_zeroed();
80    src.read_exact(message.as_mut_bytes()).await?;
81    Ok(message)
82}
83
84impl<E> From<ReadExactError<E>> for crate::Error<E> {
85    fn from(e: ReadExactError<E>) -> Self {
86        match e {
87            ReadExactError::Other(e) => Self::Io(e),
88            ReadExactError::UnexpectedEof => Self::UnexpectedEof,
89        }
90    }
91}