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}