Thanks to visit codestin.com
Credit goes to docs.rs

serialmessage/
lib.rs

1//! # serialmessage
2//!
3//! `serialmessage` enables you to pack serial data into a fast, reliable,
4//! and packetized form for communicating with e.g. a Microcontroller. It is compatible with
5//! the [Arduino SerialTransfer][STArduino] and [Python pySerialTransfer][STPython]
6//! libraries by [PowerBroker2][GithubPowerBroker]. This crate is designed to be used
7//! with any serial crate you desire, and does therefore not implement any serial
8//! communication on its own. This crate is optionally fully no_std compatible and can
9//! be used on any Microcontroller of your choice.
10//!
11//! The message format:
12//! - uses start and stop bytes
13//! - uses packet ids
14//! - uses consistent overhead byte stuffing
15//! - uses CRC-8 (Polynomial 0x9B with lookup table)
16//! - allows the use of dynamically sized packets (packets can have payload lengths anywhere from 0 to 254 bytes)
17//! - can transfer bytes, ints, floats, structs, arrays, vectors
18//!
19//! # Packet Anatomy:
20//! ```text
21//! 01111110 00000000 11111111 00000000 00000000 00000000 ... 00000000 10000001
22//! |      | |      | |      | |      | |      | |      | | | |      | |______|__Stop byte (constant)
23//! |      | |      | |      | |      | |      | |      | | | |______|___________8-bit CRC
24//! |      | |      | |      | |      | |      | |      | |_|____________________Rest of payload
25//! |      | |      | |      | |      | |      | |______|________________________2nd payload byte
26//! |      | |      | |      | |      | |______|_________________________________1st payload byte
27//! |      | |      | |      | |______|__________________________________________# of payload bytes
28//! |      | |      | |______|___________________________________________________COBS Overhead byte
29//! |      | |______|____________________________________________________________Packet ID
30//! |______|_____________________________________________________________________Start byte (constant)
31//!
32//! ```
33//!
34//! # Examples
35//! ### Basic Example
36//!
37//! ```rust
38//! use serialmessage::{SerMsg, ParseState};
39//!
40//! let send_data_vec: Vec<u8> = vec![1, 2, 3, 4];
41//! let send_msg = SerMsg::create_msg_vec(&send_data_vec, 1).unwrap();
42//! // Send the message bytes with a serial crate of your choice
43//!
44//! //Parsing received bytes
45//! let mut ser_msg = SerMsg::new();
46//! let (parse_state, _parsed_bytes) = ser_msg.parse_read_bytes(&send_msg);
47//! match parse_state {
48//!     ParseState::DataReady => {
49//!         let rcvd_data = ser_msg.return_read_data();
50//!         assert_eq!(&send_data_vec, rcvd_data);
51//!     }
52//!     _ => {
53//!         println!("Parsestate: {:?}", parse_state);
54//!     }
55//! }
56//! ```
57//!
58//! ### no_std zerocopy Example
59//! ```rust
60//! use serialmessage::{SerMsg, ParseState};
61//! use zerocopy::{AsBytes, FromBytes};
62//!
63//! #[repr(C, packed)]
64//! #[derive(FromBytes, AsBytes, Debug, Clone, Copy, PartialEq)]
65//! struct ExampleData {
66//!     i_32: i32,
67//!     f_32: f32,
68//! }
69//!
70//! let send_struct = ExampleData {i_32: 26, f_32: 55.845};
71//! let send_bytes = send_struct.as_bytes();
72//! let (send_msg, msg_len) = SerMsg::create_msg_arr(send_bytes, 1).unwrap();
73//! // Send: send_msg[..msg_len];
74//!
75//! //Parsing received bytes
76//! let mut ser_msg = SerMsg::new();
77//! let (parse_state, _parsed_bytes) = ser_msg.parse_read_bytes(&send_msg[..msg_len]);
78//! match parse_state {
79//!     ParseState::DataReady => {
80//!         let rcvd_struct = ExampleData::read_from(ser_msg.return_read_data()).unwrap();
81//!         assert_eq!(send_struct, rcvd_struct);
82//!     }
83//!     _ => {
84//!         println!("Parsestate: {:?}", parse_state);
85//!     }
86//! }
87//! ```
88//!
89//! [GithubPowerBroker]: https://github.com/PowerBroker2
90//! [STArduino]: https://github.com/PowerBroker2/SerialTransfer
91//! [STPython]: https://github.com/PowerBroker2/pySerialTransfer
92
93#![no_std]
94
95/// Shows the progress/error when parsing bytes with [SerMsg.parse_read_bytes()][parse_read_bytes].
96///
97/// # Example
98///
99/// ```rust
100/// use serialmessage::{SerMsg, ParseState};
101/// let rcvd_message_vec: Vec<u8> = vec![126, 1, 0, 4, 0, 72, 105, 33, 246, 129];
102/// let mut ser_msg = SerMsg::new();
103/// let (parse_state, _parsed_bytes) = ser_msg.parse_read_bytes(&rcvd_message_vec);
104/// match parse_state {
105///     ParseState::DataReady => {
106///         // Do something with the received data
107///     }
108///     // No error occurred, but no complete message is ready
109///     ParseState::Continue => (),
110///     // Handle or ignore these error
111///     ParseState::CrcError => (),
112///     ParseState::HighPayloadError => (),
113///     ParseState::StopByteError => (),
114///     ParseState::COBSError => (),
115/// }
116/// ```
117///
118/// [parse_read_bytes]: SerMsg::parse_read_bytes()
119#[derive(Debug)]
120pub enum ParseState {
121    /// The bytes were handled successfully, but no complete message is ready
122    Continue,
123    /// The complete message was parsed successfully and the data is ready
124    DataReady,
125    /// The CrcCheck failed, the message is most likely corrupted
126    CrcError,
127    /// The Payload length exceeded its maximum of 254
128    HighPayloadError,
129    /// The StopByte was not found after the CrcCheck
130    StopByteError,
131    /// Couldn't successfully unpack the Consistent Overhead Byte Stuffing
132    COBSError,
133}
134
135enum FindByte {
136    Start,
137    Id,
138    Overhead,
139    PayloadLen,
140    Payload,
141    Crc,
142    End,
143}
144
145/// Struct that implements all the functionality of this crate
146pub struct SerMsg {
147    msg_state: FindByte,
148    payload_len: u8,
149    cobs_byte: u8,
150    rcvd_id: u8,
151    rcvd_data: [u8; 254],
152    rcvd_ind: usize,
153}
154
155impl Default for SerMsg {
156    fn default() -> Self {
157        Self::new()
158    }
159}
160
161impl SerMsg {
162    const START_BYTE: u8 = 126;
163    const STOP_BYTE: u8 = 129;
164    const MAX_PACKET_SIZE: u8 = 254;
165
166    const LOOKUP_TABLE: [u8; 256] = [
167        0x00, 0x9b, 0xad, 0x36, 0xc1, 0x5a, 0x6c, 0xf7, 0x19, 0x82, 0xb4, 0x2f, 0xd8, 0x43, 0x75,
168        0xee, 0x32, 0xa9, 0x9f, 0x04, 0xf3, 0x68, 0x5e, 0xc5, 0x2b, 0xb0, 0x86, 0x1d, 0xea, 0x71,
169        0x47, 0xdc, 0x64, 0xff, 0xc9, 0x52, 0xa5, 0x3e, 0x08, 0x93, 0x7d, 0xe6, 0xd0, 0x4b, 0xbc,
170        0x27, 0x11, 0x8a, 0x56, 0xcd, 0xfb, 0x60, 0x97, 0x0c, 0x3a, 0xa1, 0x4f, 0xd4, 0xe2, 0x79,
171        0x8e, 0x15, 0x23, 0xb8, 0xc8, 0x53, 0x65, 0xfe, 0x09, 0x92, 0xa4, 0x3f, 0xd1, 0x4a, 0x7c,
172        0xe7, 0x10, 0x8b, 0xbd, 0x26, 0xfa, 0x61, 0x57, 0xcc, 0x3b, 0xa0, 0x96, 0x0d, 0xe3, 0x78,
173        0x4e, 0xd5, 0x22, 0xb9, 0x8f, 0x14, 0xac, 0x37, 0x01, 0x9a, 0x6d, 0xf6, 0xc0, 0x5b, 0xb5,
174        0x2e, 0x18, 0x83, 0x74, 0xef, 0xd9, 0x42, 0x9e, 0x05, 0x33, 0xa8, 0x5f, 0xc4, 0xf2, 0x69,
175        0x87, 0x1c, 0x2a, 0xb1, 0x46, 0xdd, 0xeb, 0x70, 0x0b, 0x90, 0xa6, 0x3d, 0xca, 0x51, 0x67,
176        0xfc, 0x12, 0x89, 0xbf, 0x24, 0xd3, 0x48, 0x7e, 0xe5, 0x39, 0xa2, 0x94, 0x0f, 0xf8, 0x63,
177        0x55, 0xce, 0x20, 0xbb, 0x8d, 0x16, 0xe1, 0x7a, 0x4c, 0xd7, 0x6f, 0xf4, 0xc2, 0x59, 0xae,
178        0x35, 0x03, 0x98, 0x76, 0xed, 0xdb, 0x40, 0xb7, 0x2c, 0x1a, 0x81, 0x5d, 0xc6, 0xf0, 0x6b,
179        0x9c, 0x07, 0x31, 0xaa, 0x44, 0xdf, 0xe9, 0x72, 0x85, 0x1e, 0x28, 0xb3, 0xc3, 0x58, 0x6e,
180        0xf5, 0x02, 0x99, 0xaf, 0x34, 0xda, 0x41, 0x77, 0xec, 0x1b, 0x80, 0xb6, 0x2d, 0xf1, 0x6a,
181        0x5c, 0xc7, 0x30, 0xab, 0x9d, 0x06, 0xe8, 0x73, 0x45, 0xde, 0x29, 0xb2, 0x84, 0x1f, 0xa7,
182        0x3c, 0x0a, 0x91, 0x66, 0xfd, 0xcb, 0x50, 0xbe, 0x25, 0x13, 0x88, 0x7f, 0xe4, 0xd2, 0x49,
183        0x95, 0x0e, 0x38, 0xa3, 0x54, 0xcf, 0xf9, 0x62, 0x8c, 0x17, 0x21, 0xba, 0x4d, 0xd6, 0xe0,
184        0x7b,
185    ];
186
187    /// Returns a new SerMsg instance for parsing read data
188    pub fn new() -> SerMsg {
189        let msg_state = FindByte::Start;
190        let rcvd_id: u8 = 0;
191        let cobs_byte: u8 = 0;
192        let payload_len: u8 = 0;
193        let rcvd_data: [u8; 254] = [0; 254];
194        let rcvd_ind: usize = 0;
195
196        SerMsg {
197            msg_state,
198            rcvd_id,
199            cobs_byte,
200            payload_len,
201            rcvd_data,
202            rcvd_ind,
203        }
204    }
205
206    /// Returns the data of the parsed message, should only be used after the [ParseState][ParseState] is [DataReady][DataReady]
207    ///
208    /// [ParseState]: ParseState
209    /// [DataReady]: ParseState::DataReady
210    pub fn return_read_data(&self) -> &[u8] {
211        &self.rcvd_data[..self.rcvd_ind]
212    }
213
214    /// Returns the id of the parsed message, should only be used after the [ParseState][ParseState] is [DataReady][DataReady]
215    ///
216    /// [ParseState]: ParseState
217    /// [DataReady]: ParseState::DataReady
218    pub fn return_msg_id(&self) -> u8 {
219        self.rcvd_id
220    }
221
222    /// no_std function to create a message.
223    /// Packs the slice into the message format and returns an array with a fixed length
224    /// of the maximum message size and the last index of the relevant bytes in the array.
225    /// Returns None if the input slice exceeds the maximum supported length of 254 bytes.
226    pub fn create_msg_arr(data: &[u8], id: u8) -> Option<([u8; 260], usize)> {
227        if data.len() > SerMsg::MAX_PACKET_SIZE as usize {
228            return None;
229        }
230        let mut msg: [u8; 260] = [0; 260];
231        msg[0] = SerMsg::START_BYTE;
232        msg[1] = id;
233        msg[3] = data.len() as u8;
234        for (i, &val) in data.iter().enumerate() {
235            msg[i + 4] = val;
236        }
237        msg[2] = SerMsg::pack_cobs(&mut msg[4..4 + data.len()]);
238        msg[4 + data.len()] = SerMsg::retrieve_crc(&msg[4..4 + data.len()]);
239        msg[5 + data.len()] = SerMsg::STOP_BYTE;
240        Some((msg, data.len() + 6))
241    }
242
243    /// Parses the bytes of the input slice. Returns a [ParseState][ParseState] and the amount of bytes parsed
244    /// when an error occured, a complete message was parsed or when all bytes were read.
245    ///
246    /// [ParseState]: ParseState
247    pub fn parse_read_bytes(&mut self, arr: &[u8]) -> (ParseState, usize) {
248        for (i, val) in arr.iter().enumerate() {
249            let state = self.parse_byte(*val);
250            match state {
251                ParseState::Continue => (),
252                _ => return (state, i + 1),
253            }
254        }
255        (ParseState::Continue, arr.len())
256    }
257
258    fn retrieve_crc(data_slice: &[u8]) -> u8 {
259        let mut calc_crc = 0;
260        for val in data_slice.iter() {
261            calc_crc = SerMsg::LOOKUP_TABLE[(calc_crc ^ val) as usize];
262        }
263        calc_crc
264    }
265
266    fn unpack_cobs(&mut self) -> bool {
267        if self.cobs_byte <= SerMsg::MAX_PACKET_SIZE {
268            if (self.cobs_byte as usize) >= self.payload_len as usize {
269                return false;
270            }
271            while self.rcvd_data[self.cobs_byte as usize] > 0 {
272                let delta: u8 = self.rcvd_data[self.cobs_byte as usize];
273
274                // check if delta makes us point outside of the payload region
275                // this means the data was corrupted or malformed in a lot of places
276                // and by chance got past the CRC
277                // the saturating add avoids a panic where an overflow might have occured
278                if (delta.saturating_add(self.cobs_byte)) >= self.payload_len {
279                    return false;
280                }
281                self.rcvd_data[self.cobs_byte as usize] = SerMsg::START_BYTE;
282                self.cobs_byte += delta;
283            }
284            self.rcvd_data[self.cobs_byte as usize] = SerMsg::START_BYTE;
285            true
286        } else {
287            true
288        }
289    }
290
291    fn pack_cobs(data_slice: &mut [u8]) -> u8 {
292        let mut overhead_byte = 0xFF;
293        for (f_ind, val) in (0_u8..).zip(data_slice.iter()) {
294            if *val == SerMsg::START_BYTE {
295                overhead_byte = f_ind;
296                break;
297            }
298        }
299        if (data_slice.len() <= SerMsg::MAX_PACKET_SIZE as usize) && (overhead_byte < 0xFF) {
300            let mut last_start_byte = 0;
301
302            let mut r_ind = data_slice.len();
303            for _ in 0..data_slice.len() {
304                r_ind -= 1;
305                if data_slice[r_ind] == SerMsg::START_BYTE {
306                    last_start_byte = r_ind;
307                    break;
308                }
309            }
310            r_ind = data_slice.len() - 1;
311            for _ in 0..data_slice.len() {
312                if data_slice[r_ind] == SerMsg::START_BYTE {
313                    data_slice[r_ind] = (last_start_byte - r_ind) as u8;
314                    last_start_byte = r_ind;
315                }
316                if r_ind == 0 {
317                    break;
318                }
319                r_ind -= 1;
320            }
321        }
322        overhead_byte
323    }
324
325    fn parse_byte(&mut self, val: u8) -> ParseState {
326        match self.msg_state {
327            FindByte::Start => {
328                if val == SerMsg::START_BYTE {
329                    self.rcvd_data.fill(0);
330                    self.rcvd_ind = 0;
331                    self.msg_state = FindByte::Id;
332                }
333                ParseState::Continue
334            }
335
336            FindByte::Id => {
337                self.rcvd_id = val;
338                self.msg_state = FindByte::Overhead;
339                ParseState::Continue
340            }
341
342            FindByte::Overhead => {
343                self.cobs_byte = val;
344                self.msg_state = FindByte::PayloadLen;
345                ParseState::Continue
346            }
347
348            FindByte::PayloadLen => {
349                self.payload_len = val;
350                if val > SerMsg::MAX_PACKET_SIZE {
351                    self.msg_state = FindByte::Start;
352                    ParseState::HighPayloadError
353                } else if val == 0 {
354                    self.msg_state = FindByte::Crc;
355                    ParseState::Continue
356                } else {
357                    self.msg_state = FindByte::Payload;
358                    ParseState::Continue
359                }
360            }
361
362            FindByte::Payload => {
363                self.rcvd_data[self.rcvd_ind] = val;
364                self.rcvd_ind += 1;
365                if (self.payload_len as usize - self.rcvd_ind) == 0 {
366                    self.msg_state = FindByte::Crc;
367                }
368                ParseState::Continue
369            }
370
371            FindByte::Crc => {
372                if val == SerMsg::retrieve_crc(&self.rcvd_data[..self.rcvd_ind]) {
373                    self.msg_state = FindByte::End;
374                    ParseState::Continue
375                } else {
376                    self.msg_state = FindByte::Start;
377                    ParseState::CrcError
378                }
379            }
380
381            FindByte::End => {
382                self.msg_state = FindByte::Start;
383                if val == SerMsg::STOP_BYTE {
384                    if self.unpack_cobs() {
385                        ParseState::DataReady
386                    } else {
387                        ParseState::COBSError
388                    }
389                } else {
390                    ParseState::StopByteError
391                }
392            }
393        }
394    }
395}
396
397cfg_if::cfg_if! {
398    if #[cfg(feature="alloc")] {
399        extern crate alloc;
400        use alloc::{vec::Vec};
401        impl SerMsg {
402            /// Packs the slice into the message format and returns a Vector containing the message bytes.
403            /// Returns None if the input slice exceeds the maximum supported length of 254 bytes.
404            #[allow(clippy::vec_init_then_push)]
405            pub fn create_msg_vec(data: &[u8], id: u8) -> Option<Vec<u8>> {
406                if data.len() > SerMsg::MAX_PACKET_SIZE as usize {
407                    return None;
408                }
409                let mut data_vec: Vec<u8> = Vec::with_capacity(data.len() + 6);
410                data_vec.push(SerMsg::START_BYTE);
411                data_vec.push(id);
412                data_vec.push(0); // 0 as a placeholder for the COBS
413                data_vec.push(data.len() as u8);
414                data_vec.extend(data);
415                data_vec[2] = SerMsg::pack_cobs(&mut data_vec[4..4 + data.len()]);
416                let crc = SerMsg::retrieve_crc(&data_vec[4..4 + data.len()]);
417                data_vec.push(crc);
418                data_vec.push(SerMsg::STOP_BYTE);
419                Some(data_vec)
420            }
421        }
422    }
423}