Skip to main content

embedded_aim_motor/motor/
mod.rs

1mod parameters;
2
3use crate::{Error, Result, RtuBaud};
4use defmt::debug;
5use embassy_time::{Duration, Instant, Timer, with_timeout};
6use modbus_core::{
7    Data, FunctionCode, Request, RequestPdu, Response,
8    rtu::{Header, RequestAdu},
9};
10
11pub struct Motor<I: embedded_io_async::Read + embedded_io_async::Write> {
12    comm: I,
13
14    t15: Duration,
15    t35: Duration,
16    response_timeout: Duration,
17
18    address: u8,
19
20    buffer: [u8; 64],
21    earliest_next_frame: Instant,
22}
23
24impl<I: embedded_io_async::Read + embedded_io_async::Write> Motor<I> {
25    pub fn new(comm: I, baud: RtuBaud, address: u8, response_timeout: Duration) -> Self {
26        Self {
27            comm,
28            t15: baud.t15(),
29            t35: baud.t35(),
30            response_timeout,
31            address,
32            buffer: [0u8; 64],
33            earliest_next_frame: Instant::now(),
34        }
35    }
36
37    async fn modbus_transaction<'a>(&'a mut self, req: RequestPdu<'a>) -> Result<Response<'a>> {
38        // Ensure we wait for at least the inter-frame delay
39        Timer::at(self.earliest_next_frame).await;
40
41        // Create request
42        let request = RequestAdu {
43            hdr: Header {
44                slave: self.address,
45            },
46            pdu: req,
47        };
48
49        // Encode request
50        let n = modbus_core::rtu::client::encode_request(request, &mut self.buffer)
51            .map_err(|_| Error::Transport)?;
52        let data = &self.buffer[..n];
53        debug!("Encoded request: ({}) {:x}", n, data);
54
55        // Send request
56        self.comm
57            .write_all(data)
58            .await
59            .map_err(|_| Error::Transport)?;
60
61        let mut timeout = self.response_timeout;
62        let mut total_read = 0;
63
64        // Receive data
65        'rx: loop {
66            match with_timeout(timeout, self.comm.read(&mut self.buffer[total_read..])).await {
67                Ok(Ok(n)) => {
68                    total_read += n;
69                    self.earliest_next_frame = Instant::now() + self.t35;
70                }
71                Ok(Err(_)) => {
72                    return Err(Error::Transport);
73                }
74                Err(_) => break 'rx,
75            }
76
77            timeout = self.t15;
78        }
79
80        if total_read == 0 {
81            // Timeout if nothing has been received
82            Err(Error::Timeout)
83        } else {
84            let data = &self.buffer[..total_read];
85            debug!("Received: ({}) {:x}", total_read, data);
86
87            // Try to parse the response
88            let response = modbus_core::rtu::client::decode_response(data)
89                .map_err(|_| Error::Transport)?
90                .ok_or(Error::Modbus)?;
91
92            Ok(response.pdu.0.map_err(|_| Error::Modbus)?)
93        }
94    }
95
96    async fn read_one_word_parameter<T, F>(&mut self, address: u16, transform: F) -> Result<T>
97    where
98        F: Fn(u16) -> Result<T>,
99    {
100        let request = RequestPdu(Request::ReadHoldingRegisters(address, 1));
101
102        match self.modbus_transaction(request).await? {
103            Response::ReadHoldingRegisters(data) => {
104                if data.len() == 1 {
105                    let raw = data.get(0).unwrap();
106                    Ok(transform(raw)?)
107                } else {
108                    Err(Error::UnexpectedResponseLength(data.len(), 1))
109                }
110            }
111            _ => Err(Error::UnexpectedResponseType),
112        }
113    }
114
115    async fn read_two_word_parameter<T, F>(&mut self, address: u16, transform: F) -> Result<T>
116    where
117        F: Fn(u16, u16) -> Result<T>,
118    {
119        let request = RequestPdu(Request::ReadHoldingRegisters(address, 2));
120
121        match self.modbus_transaction(request).await? {
122            Response::ReadHoldingRegisters(data) => {
123                if data.len() == 2 {
124                    let raw_0 = data.get(0).unwrap();
125                    let raw_1 = data.get(1).unwrap();
126                    Ok(transform(raw_0, raw_1)?)
127                } else {
128                    Err(Error::UnexpectedResponseLength(data.len(), 2))
129                }
130            }
131            _ => Err(Error::UnexpectedResponseType),
132        }
133    }
134
135    async fn write_one_word_parameter<T, F>(
136        &mut self,
137        address: u16,
138        value: T,
139        transform: F,
140    ) -> Result<()>
141    where
142        F: Fn(T) -> Result<u16>,
143    {
144        let data = transform(value)?;
145
146        let request = RequestPdu(Request::WriteSingleRegister(address, data));
147
148        match self.modbus_transaction(request).await? {
149            Response::WriteSingleRegister(a, d) => {
150                if a == address && d == data {
151                    Ok(())
152                } else {
153                    Err(Error::UnexpectedResponseData)
154                }
155            }
156            _ => Err(Error::UnexpectedResponseType),
157        }
158    }
159
160    async fn write_two_word_parameter<T, F>(
161        &mut self,
162        address: u16,
163        value: T,
164        transform: F,
165    ) -> Result<()>
166    where
167        F: Fn(T) -> Result<[u16; 2]>,
168    {
169        let data = transform(value)?;
170
171        let mut buff = [0u8; 4];
172        let data = Data::from_words(&data, &mut buff).unwrap();
173
174        let request = RequestPdu(Request::WriteMultipleRegisters(address, data));
175
176        match self.modbus_transaction(request).await? {
177            Response::WriteMultipleRegisters(a, 2) => {
178                if a == address {
179                    Ok(())
180                } else {
181                    Err(Error::UnexpectedResponseData)
182                }
183            }
184            _ => Err(Error::UnexpectedResponseType),
185        }
186    }
187
188    pub async fn set_baud_rate(&mut self, baud: RtuBaud) -> Result<()> {
189        let baud = match baud {
190            RtuBaud::Baud115200 => 803,
191            RtuBaud::Baud38400 => 802,
192            RtuBaud::Baud19200 => 801,
193            RtuBaud::Baud9600 => 800,
194        };
195
196        self.write_one_word_parameter(0x00, 1, Ok).await?;
197        self.write_one_word_parameter(0x02, baud, Ok).await?;
198        self.write_one_word_parameter(0x03, 129, Ok).await?;
199        self.write_one_word_parameter(0x00, 506, Ok).await?;
200
201        Ok(())
202    }
203
204    pub async fn set_target_position_custom(&mut self, value: u32) -> Result<()> {
205        const FC: FunctionCode = FunctionCode::Custom(0x78);
206
207        let data = value.to_be_bytes();
208        let request = RequestPdu(Request::Custom(FC, &data));
209
210        match self.modbus_transaction(request).await? {
211            Response::Custom(FC, d) => {
212                if d == data {
213                    Ok(())
214                } else {
215                    Err(Error::UnexpectedResponseData)
216                }
217            }
218            _ => Err(Error::UnexpectedResponseType),
219        }
220    }
221
222    pub async fn set_absolute_position_custom(&mut self, value: u32) -> Result<()> {
223        const FC: FunctionCode = FunctionCode::Custom(0x7B);
224
225        let data = value.to_be_bytes();
226        let request = RequestPdu(Request::Custom(FC, &data));
227
228        match self.modbus_transaction(request).await? {
229            Response::Custom(FC, d) => {
230                if d == data {
231                    Ok(())
232                } else {
233                    Err(Error::UnexpectedResponseData)
234                }
235            }
236            _ => Err(Error::UnexpectedResponseType),
237        }
238    }
239}