1#![cfg_attr(not(feature = "std"), no_std)]
2
3#[cfg(not(feature = "std"))]
4extern crate alloc;
5
6pub mod commands;
7pub mod structs;
8
9#[cfg(feature = "std")]
11mod std_impl {
12 pub use std::{
13 error::Error,
14 fmt::{self, Debug, Display, Formatter},
15 mem,
16 };
17}
18
19#[cfg(not(feature = "std"))]
21mod no_std_impl {
22 pub use core::{
23 fmt::{self, Debug, Display, Formatter},
24 mem,
25 };
26}
27
28#[cfg(not(feature = "std"))]
29use no_std_impl::*;
30#[cfg(feature = "std")]
31use std_impl::*;
32
33use crc_any::CRCu8;
34use packed_struct::PackedStruct;
35use smallvec::SmallVec;
36
37#[derive(Clone, PartialEq)]
38pub struct MspPacketData(pub(crate) SmallVec<[u8; 256]>);
39
40impl MspPacketData {
41 pub(crate) fn new() -> MspPacketData {
42 MspPacketData(SmallVec::new())
43 }
44
45 pub fn as_mut_slice(&mut self) -> &mut [u8] {
46 let MspPacketData(data) = self;
47 data.as_mut_slice()
48 }
49}
50impl Debug for MspPacketData {
53 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
54 let MspPacketData(data) = self;
55 if data.is_empty() {
56 write!(f, "empty")?;
57 return Ok(());
58 }
59 write!(f, "0x")?;
60 for byte in data {
61 write!(f, "{byte:02X}")?;
62 }
63 Ok(())
64 }
65}
66
67impl From<&[u8]> for MspPacketData {
68 fn from(data: &[u8]) -> Self {
69 MspPacketData(SmallVec::from_slice(data))
70 }
71}
72
73impl MspPacketData {
74 pub fn as_slice(&self) -> &[u8] {
75 let Self(data) = self;
76 data
77 }
78}
79
80#[derive(Copy, Clone, Debug, PartialEq)]
82pub enum MspPacketParseError {
83 OutputBufferSizeMismatch,
84 CrcMismatch { expected: u8, calculated: u8 },
85 InvalidData,
86 InvalidHeader1,
87 InvalidHeader2,
88 InvalidDirection,
89 InvalidDataLength,
90}
91
92impl Display for MspPacketParseError {
93 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
94 match self {
95 MspPacketParseError::OutputBufferSizeMismatch => {
96 write!(f, "Output buffer size mismatch")
97 }
98 MspPacketParseError::CrcMismatch {
99 expected,
100 calculated,
101 } => write!(
102 f,
103 "CRC mismatch, expected: 0x{expected:02X}, calculated: 0x{calculated:02X}"
104 ),
105 MspPacketParseError::InvalidData => write!(f, "Invalid data"),
106 MspPacketParseError::InvalidHeader1 => write!(f, "Invalid header 1"),
107 MspPacketParseError::InvalidHeader2 => write!(f, "Invalid header 2"),
108 MspPacketParseError::InvalidDirection => write!(f, "Invalid direction"),
109 MspPacketParseError::InvalidDataLength => write!(f, "Invalid data length"),
110 }
111 }
112}
113
114#[cfg(feature = "std")]
115impl Error for MspPacketParseError {}
116
117#[derive(Copy, Clone, Debug, PartialEq)]
119pub enum MspPacketDirection {
120 ToFlightController,
122 FromFlightController,
124 Unsupported,
126}
127
128impl MspPacketDirection {
129 pub fn to_byte(&self) -> u8 {
131 let b = match *self {
132 MspPacketDirection::ToFlightController => '<',
133 MspPacketDirection::FromFlightController => '>',
134 MspPacketDirection::Unsupported => '!',
135 };
136 b as u8
137 }
138}
139
140#[derive(Debug, Clone, PartialEq)]
141pub struct MspPacket {
143 pub cmd: u16,
144 pub direction: MspPacketDirection,
145 pub data: MspPacketData,
146}
147
148#[derive(Copy, Clone, PartialEq, Debug)]
149enum MspParserState {
150 Header1,
151 Header2,
152 Direction,
153 FlagV2,
154 DataLength,
155 DataLengthV2,
156 Command,
157 CommandV2,
158 Data,
159 DataV2,
160 Crc,
161}
162
163#[derive(Copy, Clone, PartialEq, Debug)]
164enum MspVersion {
165 V1,
166 V2,
167}
168
169pub struct MspParser {
171 state: MspParserState,
172 packet_version: MspVersion,
173 packet_direction: MspPacketDirection,
174 packet_cmd: u16,
175 packet_data_length_remaining: usize,
176 packet_data: MspPacketData,
177 packet_crc: u8,
178 packet_crc_v2: CRCu8,
179}
180
181impl MspParser {
182 pub fn new() -> MspParser {
184 Self {
185 state: MspParserState::Header1,
186 packet_version: MspVersion::V1,
187 packet_direction: MspPacketDirection::ToFlightController,
188 packet_data_length_remaining: 0,
189 packet_cmd: 0,
190 packet_data: MspPacketData::new(),
191 packet_crc: 0,
192 packet_crc_v2: CRCu8::crc8dvb_s2(),
193 }
194 }
195
196 pub fn state_is_between_packets(&self) -> bool {
198 self.state == MspParserState::Header1
199 }
200
201 pub fn parse(&mut self, input: u8) -> Result<Option<MspPacket>, MspPacketParseError> {
204 match self.state {
205 MspParserState::Header1 => {
206 if input == b'$' {
207 self.state = MspParserState::Header2;
208 } else {
209 self.reset();
210 }
211 }
212
213 MspParserState::Header2 => {
214 self.packet_version = match input as char {
215 'M' => MspVersion::V1,
216 'X' => MspVersion::V2,
217 _ => {
218 self.reset();
219 return Err(MspPacketParseError::InvalidHeader2);
220 }
221 };
222
223 self.state = MspParserState::Direction;
224 }
225
226 MspParserState::Direction => {
227 match input {
228 60 => self.packet_direction = MspPacketDirection::ToFlightController, 62 => self.packet_direction = MspPacketDirection::FromFlightController, 33 => self.packet_direction = MspPacketDirection::Unsupported, _ => {
232 self.reset();
233 return Err(MspPacketParseError::InvalidDirection);
234 }
235 }
236
237 self.state = match self.packet_version {
238 MspVersion::V1 => MspParserState::DataLength,
239 MspVersion::V2 => MspParserState::FlagV2,
240 };
241 }
242
243 MspParserState::FlagV2 => {
244 self.state = MspParserState::CommandV2;
246 self.packet_data = MspPacketData::new();
247 self.packet_crc_v2.digest(&[input]);
248 }
249
250 MspParserState::CommandV2 => {
251 let MspPacketData(data) = &mut self.packet_data;
252 data.push(input);
253
254 if data.len() == 2 {
255 let mut s = [0u8; core::mem::size_of::<u16>()];
256 s.copy_from_slice(data);
257 self.packet_cmd = u16::from_le_bytes(s);
258
259 self.packet_crc_v2.digest(&data);
260 data.clear();
261 self.state = MspParserState::DataLengthV2;
262 }
263 }
264
265 MspParserState::DataLengthV2 => {
266 let MspPacketData(data) = &mut self.packet_data;
267 data.push(input);
268
269 if data.len() == 2 {
270 let mut s = [0u8; core::mem::size_of::<u16>()];
271 s.copy_from_slice(data);
272 self.packet_data_length_remaining = u16::from_le_bytes(s).into();
273 self.packet_crc_v2.digest(data);
274 data.clear();
275 if self.packet_data_length_remaining == 0 {
276 self.state = MspParserState::Crc;
277 } else {
278 self.state = MspParserState::DataV2;
279 }
280 }
281 }
282
283 MspParserState::DataV2 => {
284 let MspPacketData(data) = &mut self.packet_data;
285 data.push(input);
286 self.packet_data_length_remaining -= 1;
287
288 if self.packet_data_length_remaining == 0 {
289 self.state = MspParserState::Crc;
290 }
291 }
292
293 MspParserState::DataLength => {
294 let MspPacketData(data) = &mut self.packet_data;
295 self.packet_data_length_remaining = input as usize;
296 self.state = MspParserState::Command;
297 self.packet_crc ^= input;
298 data.clear();
299 }
300
301 MspParserState::Command => {
302 self.packet_cmd = input as u16;
303
304 if self.packet_data_length_remaining == 0 {
305 self.state = MspParserState::Crc;
306 } else {
307 self.state = MspParserState::Data;
308 }
309
310 self.packet_crc ^= input;
311 }
312
313 MspParserState::Data => {
314 let MspPacketData(data) = &mut self.packet_data;
315 data.push(input);
316 self.packet_data_length_remaining -= 1;
317
318 self.packet_crc ^= input;
319
320 if self.packet_data_length_remaining == 0 {
321 self.state = MspParserState::Crc;
322 }
323 }
324
325 MspParserState::Crc => {
326 let MspPacketData(data) = &mut self.packet_data;
327 if self.packet_version == MspVersion::V2 {
328 self.packet_crc_v2.digest(data);
329 self.packet_crc = self.packet_crc_v2.get_crc();
330 }
331
332 let packet_crc = self.packet_crc;
333 if input != packet_crc {
334 self.reset();
335 return Err(MspPacketParseError::CrcMismatch {
336 expected: input,
337 calculated: packet_crc,
338 });
339 }
340
341 let mut n = MspPacketData::new();
342 mem::swap(&mut self.packet_data, &mut n);
343
344 let packet = MspPacket {
345 cmd: self.packet_cmd,
346 direction: self.packet_direction,
347 data: n,
348 };
349
350 self.reset();
351
352 return Ok(Some(packet));
353 }
354 }
355
356 Ok(None)
357 }
358
359 pub fn reset(&mut self) {
360 let MspPacketData(data) = &mut self.packet_data;
361 self.state = MspParserState::Header1;
362 self.packet_direction = MspPacketDirection::ToFlightController;
363 self.packet_data_length_remaining = 0;
364 self.packet_cmd = 0;
365 data.clear();
366 self.packet_crc = 0;
367 self.packet_crc_v2.reset();
368 }
369}
370
371impl Default for MspParser {
372 fn default() -> Self {
373 Self::new()
374 }
375}
376
377impl MspPacket {
378 pub fn packet_size_bytes(&self) -> usize {
380 let MspPacketData(data) = &self.data;
381 6 + data.len()
382 }
383
384 pub fn packet_size_bytes_v2(&self) -> usize {
386 let MspPacketData(data) = &self.data;
387 9 + data.len()
388 }
389
390 pub fn serialize(&self, output: &mut [u8]) -> Result<(), MspPacketParseError> {
392 let MspPacketData(data) = &self.data;
393 let l = output.len();
394
395 if l != self.packet_size_bytes() {
396 return Err(MspPacketParseError::OutputBufferSizeMismatch);
397 }
398
399 output[0] = b'$';
400 output[1] = b'M';
401 output[2] = self.direction.to_byte();
402 output[3] = data.len() as u8;
403 output[4] = self.cmd as u8;
404
405 output[5..l - 1].copy_from_slice(data);
406
407 let mut crc = output[3] ^ output[4];
408 for b in data {
409 crc ^= *b;
410 }
411 output[l - 1] = crc;
412
413 Ok(())
414 }
415
416 pub fn serialize_v2(&self, output: &mut [u8]) -> Result<(), MspPacketParseError> {
418 let MspPacketData(data) = &self.data;
419 let l = output.len();
420
421 if l != self.packet_size_bytes_v2() {
422 return Err(MspPacketParseError::OutputBufferSizeMismatch);
423 }
424
425 output[0] = b'$';
426 output[1] = b'X';
427 output[2] = self.direction.to_byte();
428 output[3] = 0;
429 output[4..6].copy_from_slice(&self.cmd.to_le_bytes());
430 output[6..8].copy_from_slice(&(data.len() as u16).to_le_bytes());
431
432 output[8..l - 1].copy_from_slice(data);
433
434 let mut crc = CRCu8::crc8dvb_s2();
435 crc.digest(&output[3..l - 1]);
436 output[l - 1] = crc.get_crc();
437
438 Ok(())
439 }
440
441 pub fn decode_as<T: PackedStruct>(&self) -> Result<T, packed_struct::PackingError> {
442 let expected_size = core::mem::size_of::<T::ByteArray>();
443
444 if self.data.0.len() < expected_size {
445 return Err(packed_struct::PackingError::BufferSizeMismatch {
446 expected: expected_size,
447 actual: self.data.0.len(),
448 });
449 }
450
451 let byte_array: &T::ByteArray = unsafe { &*(self.data.0.as_ptr() as *const T::ByteArray) };
452
453 T::unpack(byte_array)
454 }
455}
456
457#[cfg(test)]
458mod tests {
459 use super::*;
460 use smallvec::smallvec;
461 #[test]
462 fn test_serialize() {
463 let packet = MspPacket {
464 cmd: 2,
465 direction: MspPacketDirection::ToFlightController,
466 data: MspPacketData(smallvec![0xbe, 0xef]),
467 };
468
469 let size = packet.packet_size_bytes();
470 assert_eq!(8, size);
471
472 #[cfg(feature = "std")]
473 let mut output = std::vec![0; size];
474 #[cfg(not(feature = "std"))]
475 let mut output = alloc::vec![0; size];
476 packet.serialize(&mut output).unwrap();
477 let expected = [b'$', b'M', b'<', 2, 2, 0xbe, 0xef, 81];
478 assert_eq!(&expected, output.as_slice());
479
480 let mut packet_parsed = None;
481 let mut parser = MspParser::new();
482 for b in output {
483 let s = parser.parse(b);
484 if let Ok(Some(p)) = s {
485 packet_parsed = Some(p);
486 break;
487 }
488 }
489
490 assert_eq!(packet, packet_parsed.unwrap());
491 }
492
493 #[test]
494 fn test_roundtrip() {
495 fn roundtrip(packet: &MspPacket) {
496 let size = packet.packet_size_bytes();
497 #[cfg(feature = "std")]
498 let mut output = std::vec![0; size];
499 #[cfg(not(feature = "std"))]
500 let mut output = alloc::vec![0; size];
501
502 packet.serialize(&mut output).unwrap();
503 let mut parser = MspParser::new();
504 let mut packet_parsed = None;
505 for b in output {
506 let s = parser.parse(b);
507 if let Ok(Some(p)) = s {
508 packet_parsed = Some(p);
509 break;
510 }
511 }
512 assert_eq!(packet, &packet_parsed.unwrap());
513 }
514
515 {
516 let packet = MspPacket {
517 cmd: 1,
518 direction: MspPacketDirection::ToFlightController,
519 data: MspPacketData(smallvec![0x00, 0x00, 0x00]),
520 };
521 roundtrip(&packet);
522 }
523
524 {
525 let packet = MspPacket {
526 cmd: 200,
527 direction: MspPacketDirection::FromFlightController,
528 data: MspPacketData::new(),
529 };
530 roundtrip(&packet);
531 }
532
533 {
534 let packet = MspPacket {
535 cmd: 100,
536 direction: MspPacketDirection::Unsupported,
537 data: MspPacketData(smallvec![0x44, 0x20, 0x00, 0x80]),
538 };
539 roundtrip(&packet);
540 }
541 }
542}