mini_slcan/
write.rs

1//! Encoding of SLCAN messages.
2
3#[cfg(test)]
4mod tests;
5
6use core::mem;
7
8use crate::{CanFrame, Error, ExtIdentifier, Identifier, SerialNumber, Status};
9use defmt::Format;
10
11const MAX_RESPONSE_LEN: usize = 6;
12const MAX_NOTIF_LEN: usize = 1 + 8 + 1 + 16 + 4 + 1; // Tiiiiiiiilddddddddddddddddssss\r
13
14/// A byte buffer that can hold any `Response`.
15#[derive(Default, Debug)]
16pub struct ResponseBuf([u8; MAX_RESPONSE_LEN]);
17
18impl ResponseBuf {
19    pub const LEN: usize = MAX_RESPONSE_LEN;
20
21    pub const fn new() -> Self {
22        Self([0; MAX_RESPONSE_LEN])
23    }
24
25    pub fn as_slice(&self) -> &[u8] {
26        &self.0
27    }
28
29    pub fn as_slice_mut(&mut self) -> &mut [u8] {
30        &mut self.0
31    }
32}
33
34/// A response to a `Command`.
35#[derive(Debug, Clone, Eq, PartialEq, Format)]
36#[non_exhaustive]
37pub enum Response {
38    /// General error response (ASCII BELL).
39    Error,
40
41    /// Generic acknowledgement of a command (`CR`).
42    Ack,
43
44    /// Standard CAN frame enqueued for transmission.
45    TxAck,
46
47    /// Extended CAN frame enqueued for transmission.
48    ExtTxAck,
49
50    /// Status flags response.
51    Status(Status),
52
53    /// Response to the `ReadVersion` command.
54    Version {
55        hardware_version: u8,
56        software_version: u8,
57    },
58
59    /// Response to the `ReadSerial` command.
60    Serial(SerialNumber),
61}
62
63impl Response {
64    pub fn encode<'a>(&self, buf: &'a mut ResponseBuf) -> Result<&'a [u8], Error> {
65        let mut writer = Writer { buf: &mut buf.0 };
66        match self {
67            Response::Error => {
68                // BELL (ASCII 7) - not followed by CR
69                writer.write(7)?;
70            }
71            Response::Ack => writer.write(b'\r')?,
72            Response::TxAck => {
73                writer.write(b'z')?;
74                writer.write(b'\r')?;
75            }
76            Response::ExtTxAck => {
77                writer.write(b'Z')?;
78                writer.write(b'\r')?;
79            }
80            Response::Status(flags) => {
81                writer.write(b'F')?;
82                writer.write_hex_u8(flags.bits().into())?;
83                writer.write(b'\r')?;
84            }
85            Response::Version {
86                hardware_version,
87                software_version,
88            } => {
89                writer.write(b'V')?;
90                writer.write_hex_u8((*hardware_version).into())?;
91                writer.write_hex_u8((*software_version).into())?;
92                writer.write(b'\r')?;
93            }
94            Response::Serial(serial) => {
95                writer.write(b'N')?;
96                for byte in &serial.0 {
97                    writer.write(*byte)?;
98                }
99                writer.write(b'\r')?;
100            }
101        }
102
103        let remaining = writer.buf.len();
104        let used = buf.0.len() - remaining;
105        Ok(&buf.0[..used])
106    }
107}
108
109#[derive(Default, Debug)]
110pub struct NotificationBuf([u8; MAX_NOTIF_LEN]);
111
112impl NotificationBuf {
113    pub const fn new() -> Self {
114        Self([0; MAX_NOTIF_LEN])
115    }
116}
117
118/// An unprompted message sent by the SLCAN device.
119#[derive(Debug, Format)]
120pub enum Notification {
121    Rx {
122        identifier: Identifier,
123        frame: CanFrame,
124    },
125
126    RxExt {
127        identifier: ExtIdentifier,
128        frame: CanFrame,
129    },
130
131    RxRtr {
132        identifier: Identifier,
133        /// Must be in range 0..=8.
134        len: u8,
135    },
136
137    RxExtRtr {
138        identifier: ExtIdentifier,
139        /// Must be in range 0..=8.
140        len: u8,
141    },
142}
143
144impl Notification {
145    pub fn encode<'a>(&self, buf: &'a mut NotificationBuf) -> Result<&'a [u8], Error> {
146        let mut writer = Writer { buf: &mut buf.0 };
147        match self {
148            Notification::Rx { identifier, frame } => {
149                writer.write(b't')?;
150                writer.write_identifier(*identifier)?;
151                writer.write_frame(frame)?;
152            }
153            Notification::RxExt { identifier, frame } => {
154                writer.write(b'T')?;
155                writer.write_ext_identifier(*identifier)?;
156                writer.write_frame(frame)?;
157            }
158            Notification::RxRtr { identifier, len } => {
159                writer.write(b'r')?;
160                writer.write_identifier(*identifier)?;
161                writer.write_hex_u4(*len)?;
162            }
163            Notification::RxExtRtr { identifier, len } => {
164                writer.write(b'R')?;
165                writer.write_ext_identifier(*identifier)?;
166                writer.write_hex_u4(*len)?;
167            }
168        }
169
170        let remaining = writer.buf.len();
171        let used = buf.0.len() - remaining;
172        Ok(&buf.0[..used])
173    }
174}
175
176/// A notification with 16-bit timestamp.
177///
178/// Timestamps are disabled by default, and are turned on by the host by sending a `SetRxTimestamp`
179/// command.
180#[derive(Debug)]
181pub struct TimestampedNotification {
182    notif: Notification,
183    timestamp: u16,
184}
185
186impl TimestampedNotification {
187    /// `timestamp` must be in range `0..=0xEA5F`.
188    pub fn new(notif: Notification, timestamp: u16) -> Self {
189        Self { notif, timestamp }
190    }
191
192    pub fn encode<'a>(&self, buf: &'a mut NotificationBuf) -> Result<&'a [u8], Error> {
193        let used = self.notif.encode(buf)?.len();
194        let mut writer = Writer {
195            buf: &mut buf.0[used..],
196        };
197        writer.write_hex_u16(self.timestamp)?;
198
199        let remaining = writer.buf.len();
200        let used = buf.0.len() - remaining;
201        Ok(&buf.0[..used])
202    }
203}
204
205struct Writer<'a> {
206    buf: &'a mut [u8],
207}
208
209impl<'a> Writer<'a> {
210    fn write(&mut self, byte: u8) -> Result<(), Error> {
211        let buf = mem::replace(&mut self.buf, &mut []);
212        match buf {
213            [] => Err(Error::eof()),
214            [b, rest @ ..] => {
215                *b = byte;
216                self.buf = rest;
217                Ok(())
218            }
219        }
220    }
221
222    fn write_hex_u4(&mut self, val: u8) -> Result<(), Error> {
223        self.write_hex(val.into(), 1)
224    }
225
226    fn write_hex_u8(&mut self, val: u8) -> Result<(), Error> {
227        self.write_hex(val.into(), 2)
228    }
229
230    fn write_hex_u16(&mut self, val: u16) -> Result<(), Error> {
231        self.write_hex(val.into(), 4)
232    }
233
234    fn write_identifier(&mut self, id: Identifier) -> Result<(), Error> {
235        self.write_hex(id.as_raw().into(), 3)
236    }
237
238    fn write_ext_identifier(&mut self, id: ExtIdentifier) -> Result<(), Error> {
239        self.write_hex(id.as_raw().into(), 8)
240    }
241
242    fn write_frame(&mut self, frame: &CanFrame) -> Result<(), Error> {
243        self.write_hex_u4(frame.len() as u8)?;
244        for b in frame.data() {
245            self.write_hex_u8(*b)?;
246        }
247        Ok(())
248    }
249
250    fn write_hex(&mut self, value: u32, digits: u8) -> Result<(), Error> {
251        let mut shift = digits * 4;
252
253        for _ in 0..digits {
254            shift -= 4;
255            let digit = (value >> shift) & 0xF;
256            self.write(hex(digit as u8))?;
257        }
258
259        Ok(())
260    }
261}
262
263fn hex(nibble: u8) -> u8 {
264    match nibble {
265        0..=9 => b'0' + nibble,
266        10..=15 => b'A' + nibble - 10,
267        _ => unreachable!(),
268    }
269}