1#![cfg_attr(not(feature = "std"), no_std)]
2
3extern crate alloc;
4
5pub mod commands;
6pub mod structs;
7
8use core::{
9 fmt::{self, Debug, Display, Formatter},
10 mem,
11};
12#[cfg(feature = "std")]
13use std::error::Error;
14
15use crc_any::CRCu8;
16use heapless::Vec as HeaplessVec;
17use packed_struct::{PackedStruct, types::bits::ByteArray as PackedByteArray};
18
19#[derive(Clone, PartialEq)]
20pub struct MspPacketData(pub(crate) MspPacketDataBuffer);
21pub const MSP_MAX_PAYLOAD_LEN: usize = 255;
22const MSP_V2_FRAME_ID: u8 = 255;
23pub(crate) type MspPacketDataBuffer = HeaplessVec<u8, MSP_MAX_PAYLOAD_LEN>;
24
25impl MspPacketData {
26 pub(crate) fn new() -> MspPacketData {
27 MspPacketData::default()
28 }
29
30 pub fn as_mut_slice(&mut self) -> &mut [u8] {
31 let MspPacketData(data) = self;
32 data.as_mut_slice()
33 }
34}
35impl Debug for MspPacketData {
38 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
39 let MspPacketData(data) = self;
40 if data.is_empty() {
41 write!(f, "empty")?;
42 return Ok(());
43 }
44 write!(f, "0x")?;
45 for byte in data {
46 write!(f, "{byte:02X}")?;
47 }
48 Ok(())
49 }
50}
51
52impl Default for MspPacketData {
53 fn default() -> Self {
54 MspPacketData(MspPacketDataBuffer::new())
55 }
56}
57
58impl From<&[u8]> for MspPacketData {
59 fn from(data: &[u8]) -> Self {
60 let data = MspPacketDataBuffer::from_slice(data)
61 .expect("MSP packet payload exceeds MSP_MAX_PAYLOAD_LEN");
62 MspPacketData(data)
63 }
64}
65
66impl MspPacketData {
67 pub fn as_slice(&self) -> &[u8] {
68 let Self(data) = self;
69 data
70 }
71}
72
73#[derive(Copy, Clone, Debug, PartialEq)]
75pub enum MspPacketParseError {
76 OutputBufferSizeMismatch,
77 CrcMismatch { expected: u8, calculated: u8 },
78 InvalidData,
79 InvalidHeader1,
80 InvalidHeader2,
81 InvalidDirection,
82 InvalidDataLength,
83}
84
85impl Display for MspPacketParseError {
86 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
87 match self {
88 MspPacketParseError::OutputBufferSizeMismatch => {
89 write!(f, "Output buffer size mismatch")
90 }
91 MspPacketParseError::CrcMismatch {
92 expected,
93 calculated,
94 } => write!(
95 f,
96 "CRC mismatch, expected: 0x{expected:02X}, calculated: 0x{calculated:02X}"
97 ),
98 MspPacketParseError::InvalidData => write!(f, "Invalid data"),
99 MspPacketParseError::InvalidHeader1 => write!(f, "Invalid header 1"),
100 MspPacketParseError::InvalidHeader2 => write!(f, "Invalid header 2"),
101 MspPacketParseError::InvalidDirection => write!(f, "Invalid direction"),
102 MspPacketParseError::InvalidDataLength => write!(f, "Invalid data length"),
103 }
104 }
105}
106
107#[cfg(feature = "std")]
108impl Error for MspPacketParseError {}
109
110#[cfg(not(feature = "std"))]
111impl core::error::Error for MspPacketParseError {}
112
113#[derive(Copy, Clone, Debug, PartialEq)]
115pub enum MspPacketDirection {
116 ToFlightController,
118 FromFlightController,
120 Unsupported,
122}
123
124impl MspPacketDirection {
125 pub fn to_byte(&self) -> u8 {
127 let b = match *self {
128 MspPacketDirection::ToFlightController => '<',
129 MspPacketDirection::FromFlightController => '>',
130 MspPacketDirection::Unsupported => '!',
131 };
132 b as u8
133 }
134}
135
136#[derive(Copy, Clone, Debug, PartialEq, Eq)]
137pub enum MspParsedVersion {
138 V1,
139 V2Native,
140 V2OverV1,
141}
142
143#[derive(Copy, Clone, Debug, PartialEq, Eq)]
144pub struct MspPacketInfo {
145 pub version: MspParsedVersion,
146 pub cmd: u16,
147}
148
149#[derive(Debug, Clone, PartialEq)]
150pub struct MspPacket {
152 pub cmd: u16,
153 pub direction: MspPacketDirection,
154 pub data: MspPacketData,
155}
156
157#[derive(Copy, Clone, PartialEq, Debug)]
158enum MspParserState {
159 Header1,
160 Header2,
161 Direction,
162 FlagV2,
163 DataLength,
164 DataLengthV2,
165 Command,
166 CommandV2,
167 Data,
168 DataV2,
169 Crc,
170}
171
172#[derive(Copy, Clone, PartialEq, Debug)]
173enum MspVersion {
174 V1,
175 V2,
176}
177
178pub struct MspParser {
180 state: MspParserState,
181 packet_version: MspVersion,
182 packet_direction: MspPacketDirection,
183 packet_cmd: u16,
184 packet_data_length_remaining: usize,
185 packet_data: MspPacketData,
186 packet_crc: u8,
187 packet_crc_v2: CRCu8,
188 last_packet_info: Option<MspPacketInfo>,
189}
190
191impl MspParser {
192 pub fn new() -> MspParser {
194 Self {
195 state: MspParserState::Header1,
196 packet_version: MspVersion::V1,
197 packet_direction: MspPacketDirection::ToFlightController,
198 packet_data_length_remaining: 0,
199 packet_cmd: 0,
200 packet_data: MspPacketData::default(),
201 packet_crc: 0,
202 packet_crc_v2: CRCu8::crc8dvb_s2(),
203 last_packet_info: None,
204 }
205 }
206
207 pub fn state_is_between_packets(&self) -> bool {
209 self.state == MspParserState::Header1
210 }
211
212 pub fn last_packet_info(&self) -> Option<MspPacketInfo> {
213 self.last_packet_info
214 }
215
216 pub fn parse(&mut self, input: u8) -> Result<Option<MspPacket>, MspPacketParseError> {
219 match self.state {
220 MspParserState::Header1 => {
221 if input == b'$' {
222 self.state = MspParserState::Header2;
223 } else {
224 self.reset();
225 }
226 }
227
228 MspParserState::Header2 => {
229 self.packet_version = match input as char {
230 'M' => MspVersion::V1,
231 'X' => MspVersion::V2,
232 _ => {
233 self.reset();
234 return Err(MspPacketParseError::InvalidHeader2);
235 }
236 };
237
238 self.state = MspParserState::Direction;
239 }
240
241 MspParserState::Direction => {
242 match input {
243 60 => self.packet_direction = MspPacketDirection::ToFlightController, 62 => self.packet_direction = MspPacketDirection::FromFlightController, 33 => self.packet_direction = MspPacketDirection::Unsupported, _ => {
247 self.reset();
248 return Err(MspPacketParseError::InvalidDirection);
249 }
250 }
251
252 self.state = match self.packet_version {
253 MspVersion::V1 => MspParserState::DataLength,
254 MspVersion::V2 => MspParserState::FlagV2,
255 };
256 }
257
258 MspParserState::FlagV2 => {
259 self.state = MspParserState::CommandV2;
261 self.packet_data = MspPacketData::new();
262 self.packet_crc_v2.digest(&[input]);
263 }
264
265 MspParserState::CommandV2 => {
266 let overflow = {
267 let MspPacketData(data) = &mut self.packet_data;
268 data.push(input).is_err()
269 };
270 if overflow {
271 self.reset();
272 return Err(MspPacketParseError::InvalidDataLength);
273 }
274
275 let MspPacketData(data) = &mut self.packet_data;
276 if data.len() == 2 {
277 let mut s = [0u8; core::mem::size_of::<u16>()];
278 s.copy_from_slice(data);
279 self.packet_cmd = u16::from_le_bytes(s);
280
281 self.packet_crc_v2.digest(&data);
282 data.clear();
283 self.state = MspParserState::DataLengthV2;
284 }
285 }
286
287 MspParserState::DataLengthV2 => {
288 let overflow = {
289 let MspPacketData(data) = &mut self.packet_data;
290 data.push(input).is_err()
291 };
292 if overflow {
293 self.reset();
294 return Err(MspPacketParseError::InvalidDataLength);
295 }
296
297 let MspPacketData(data) = &mut self.packet_data;
298 if data.len() == 2 {
299 let mut s = [0u8; core::mem::size_of::<u16>()];
300 s.copy_from_slice(data);
301 self.packet_data_length_remaining = u16::from_le_bytes(s).into();
302 if self.packet_data_length_remaining > MSP_MAX_PAYLOAD_LEN {
303 self.reset();
304 return Err(MspPacketParseError::InvalidDataLength);
305 }
306 self.packet_crc_v2.digest(data);
307 data.clear();
308 if self.packet_data_length_remaining == 0 {
309 self.state = MspParserState::Crc;
310 } else {
311 self.state = MspParserState::DataV2;
312 }
313 }
314 }
315
316 MspParserState::DataV2 => {
317 let overflow = {
318 let MspPacketData(data) = &mut self.packet_data;
319 data.push(input).is_err()
320 };
321 if overflow {
322 self.reset();
323 return Err(MspPacketParseError::InvalidDataLength);
324 }
325 self.packet_data_length_remaining -= 1;
326
327 if self.packet_data_length_remaining == 0 {
328 self.state = MspParserState::Crc;
329 }
330 }
331
332 MspParserState::DataLength => {
333 let MspPacketData(data) = &mut self.packet_data;
334 self.packet_data_length_remaining = input as usize;
335 self.state = MspParserState::Command;
336 self.packet_crc ^= input;
337 data.clear();
338 }
339
340 MspParserState::Command => {
341 self.packet_cmd = input as u16;
342
343 if self.packet_data_length_remaining == 0 {
344 self.state = MspParserState::Crc;
345 } else {
346 self.state = MspParserState::Data;
347 }
348
349 self.packet_crc ^= input;
350 }
351
352 MspParserState::Data => {
353 let overflow = {
354 let MspPacketData(data) = &mut self.packet_data;
355 data.push(input).is_err()
356 };
357 if overflow {
358 self.reset();
359 return Err(MspPacketParseError::InvalidDataLength);
360 }
361 self.packet_data_length_remaining -= 1;
362
363 self.packet_crc ^= input;
364
365 if self.packet_data_length_remaining == 0 {
366 self.state = MspParserState::Crc;
367 }
368 }
369
370 MspParserState::Crc => {
371 let MspPacketData(data) = &mut self.packet_data;
372 if self.packet_version == MspVersion::V2 {
373 self.packet_crc_v2.digest(data);
374 self.packet_crc = self.packet_crc_v2.get_crc();
375 }
376
377 let packet_crc = self.packet_crc;
378 if input != packet_crc {
379 self.reset();
380 return Err(MspPacketParseError::CrcMismatch {
381 expected: input,
382 calculated: packet_crc,
383 });
384 }
385
386 let mut n = MspPacketData::new();
387 mem::swap(&mut self.packet_data, &mut n);
388
389 let packet = if self.packet_version == MspVersion::V1
390 && self.packet_cmd == u16::from(MSP_V2_FRAME_ID)
391 {
392 let raw = n.as_slice();
393 if raw.len() < 6 {
394 self.reset();
395 return Err(MspPacketParseError::InvalidDataLength);
396 }
397 let cmd = u16::from_le_bytes([raw[1], raw[2]]);
398 let payload_len = u16::from_le_bytes([raw[3], raw[4]]) as usize;
399 let v2_end = 5 + payload_len;
400 if raw.len() < v2_end + 1 {
401 self.reset();
402 return Err(MspPacketParseError::InvalidDataLength);
403 }
404 let v2_crc = raw[v2_end];
405 let mut crc = CRCu8::crc8dvb_s2();
406 crc.digest(&raw[..v2_end]);
407 let calculated = crc.get_crc();
408 if v2_crc != calculated {
409 self.reset();
410 return Err(MspPacketParseError::CrcMismatch {
411 expected: v2_crc,
412 calculated,
413 });
414 }
415 self.last_packet_info = Some(MspPacketInfo {
416 version: MspParsedVersion::V2OverV1,
417 cmd,
418 });
419 MspPacket {
420 cmd,
421 direction: self.packet_direction,
422 data: MspPacketData::from(&raw[5..v2_end]),
423 }
424 } else {
425 let version = match self.packet_version {
426 MspVersion::V1 => MspParsedVersion::V1,
427 MspVersion::V2 => MspParsedVersion::V2Native,
428 };
429 self.last_packet_info = Some(MspPacketInfo {
430 version,
431 cmd: self.packet_cmd,
432 });
433 MspPacket {
434 cmd: self.packet_cmd,
435 direction: self.packet_direction,
436 data: n,
437 }
438 };
439
440 self.reset();
441
442 return Ok(Some(packet));
443 }
444 }
445
446 Ok(None)
447 }
448
449 pub fn reset(&mut self) {
450 let MspPacketData(data) = &mut self.packet_data;
451 self.state = MspParserState::Header1;
452 self.packet_direction = MspPacketDirection::ToFlightController;
453 self.packet_data_length_remaining = 0;
454 self.packet_cmd = 0;
455 data.clear();
456 self.packet_crc = 0;
457 self.packet_crc_v2.reset();
458 }
459}
460
461impl Default for MspParser {
462 fn default() -> Self {
463 Self::new()
464 }
465}
466
467impl MspPacket {
468 pub fn packet_size_bytes(&self) -> usize {
470 if self.cmd > u16::from(MSP_V2_FRAME_ID) {
471 return self.packet_size_bytes_v2_over_v1();
472 }
473
474 let MspPacketData(data) = &self.data;
475 6 + data.len()
476 }
477
478 pub fn packet_size_bytes_v2(&self) -> usize {
480 let MspPacketData(data) = &self.data;
481 9 + data.len()
482 }
483
484 pub fn packet_size_bytes_v2_over_v1(&self) -> usize {
486 let MspPacketData(data) = &self.data;
487 12 + data.len()
488 }
489
490 pub fn serialize(&self, output: &mut [u8]) -> Result<(), MspPacketParseError> {
492 if self.cmd > u16::from(MSP_V2_FRAME_ID) {
493 return self.serialize_v2_over_v1(output);
494 }
495
496 let MspPacketData(data) = &self.data;
497 let l = output.len();
498
499 if l != self.packet_size_bytes() {
500 return Err(MspPacketParseError::OutputBufferSizeMismatch);
501 }
502
503 output[0] = b'$';
504 output[1] = b'M';
505 output[2] = self.direction.to_byte();
506 output[3] = data.len() as u8;
507 output[4] = self.cmd as u8;
508
509 output[5..l - 1].copy_from_slice(data);
510
511 let mut crc = output[3] ^ output[4];
512 for b in data {
513 crc ^= *b;
514 }
515 output[l - 1] = crc;
516
517 Ok(())
518 }
519
520 pub fn serialize_v2(&self, output: &mut [u8]) -> Result<(), MspPacketParseError> {
522 let MspPacketData(data) = &self.data;
523 let l = output.len();
524
525 if l != self.packet_size_bytes_v2() {
526 return Err(MspPacketParseError::OutputBufferSizeMismatch);
527 }
528
529 output[0] = b'$';
530 output[1] = b'X';
531 output[2] = self.direction.to_byte();
532 output[3] = 0;
533 output[4..6].copy_from_slice(&self.cmd.to_le_bytes());
534 output[6..8].copy_from_slice(&(data.len() as u16).to_le_bytes());
535
536 output[8..l - 1].copy_from_slice(data);
537
538 let mut crc = CRCu8::crc8dvb_s2();
539 crc.digest(&output[3..l - 1]);
540 output[l - 1] = crc.get_crc();
541
542 Ok(())
543 }
544
545 pub fn serialize_v2_over_v1(&self, output: &mut [u8]) -> Result<(), MspPacketParseError> {
547 let MspPacketData(data) = &self.data;
548 let l = output.len();
549
550 if l != self.packet_size_bytes_v2_over_v1() {
551 return Err(MspPacketParseError::OutputBufferSizeMismatch);
552 }
553
554 let v1_payload_len = data.len() + 6;
555 output[0] = b'$';
556 output[1] = b'M';
557 output[2] = self.direction.to_byte();
558 output[3] = v1_payload_len as u8;
559 output[4] = MSP_V2_FRAME_ID;
560
561 output[5] = 0; output[6..8].copy_from_slice(&self.cmd.to_le_bytes());
563 output[8..10].copy_from_slice(&(data.len() as u16).to_le_bytes());
564 output[10..10 + data.len()].copy_from_slice(data);
565
566 let mut crc_v2 = CRCu8::crc8dvb_s2();
567 crc_v2.digest(&output[5..10 + data.len()]);
568 output[10 + data.len()] = crc_v2.get_crc();
569
570 let mut crc = output[3] ^ output[4];
571 for b in &output[5..11 + data.len()] {
572 crc ^= *b;
573 }
574 output[l - 1] = crc;
575
576 Ok(())
577 }
578
579 pub fn decode_as<T>(&self) -> Result<T, packed_struct::PackingError>
580 where
581 T: PackedStruct,
582 for<'a> &'a T::ByteArray: TryFrom<&'a [u8]>,
583 {
584 let expected_size = <T::ByteArray as PackedByteArray>::len();
585
586 if self.data.0.len() < expected_size {
587 return Err(packed_struct::PackingError::BufferSizeMismatch {
588 expected: expected_size,
589 actual: self.data.0.len(),
590 });
591 }
592
593 let data = &self.data.0[..expected_size];
594 let byte_array: &T::ByteArray =
595 data.try_into()
596 .map_err(|_| packed_struct::PackingError::BufferSizeMismatch {
597 expected: expected_size,
598 actual: data.len(),
599 })?;
600
601 T::unpack(byte_array)
602 }
603}
604
605#[cfg(test)]
606mod tests {
607 use super::*;
608 #[test]
609 fn test_serialize() {
610 let packet = MspPacket {
611 cmd: 2,
612 direction: MspPacketDirection::ToFlightController,
613 data: MspPacketData::from(&[0xbeu8, 0xef][..]),
614 };
615
616 let size = packet.packet_size_bytes();
617 assert_eq!(8, size);
618
619 #[cfg(feature = "std")]
620 let mut output = std::vec![0; size];
621 #[cfg(not(feature = "std"))]
622 let mut output = alloc::vec![0; size];
623 packet.serialize(&mut output).unwrap();
624 let expected = [b'$', b'M', b'<', 2, 2, 0xbe, 0xef, 81];
625 assert_eq!(&expected, output.as_slice());
626
627 let mut packet_parsed = None;
628 let mut parser = MspParser::new();
629 for b in output {
630 let s = parser.parse(b);
631 if let Ok(Some(p)) = s {
632 packet_parsed = Some(p);
633 break;
634 }
635 }
636
637 assert_eq!(packet, packet_parsed.unwrap());
638 }
639
640 #[test]
641 fn test_roundtrip() {
642 fn roundtrip(packet: &MspPacket) {
643 let size = packet.packet_size_bytes();
644 #[cfg(feature = "std")]
645 let mut output = std::vec![0; size];
646 #[cfg(not(feature = "std"))]
647 let mut output = alloc::vec![0; size];
648
649 packet.serialize(&mut output).unwrap();
650 let mut parser = MspParser::new();
651 let mut packet_parsed = None;
652 for b in output {
653 let s = parser.parse(b);
654 if let Ok(Some(p)) = s {
655 packet_parsed = Some(p);
656 break;
657 }
658 }
659 assert_eq!(packet, &packet_parsed.unwrap());
660 }
661
662 {
663 let packet = MspPacket {
664 cmd: 1,
665 direction: MspPacketDirection::ToFlightController,
666 data: MspPacketData::from(&[0x00u8, 0x00, 0x00][..]),
667 };
668 roundtrip(&packet);
669 }
670
671 {
672 let packet = MspPacket {
673 cmd: 200,
674 direction: MspPacketDirection::FromFlightController,
675 data: MspPacketData::new(),
676 };
677 roundtrip(&packet);
678 }
679
680 {
681 let packet = MspPacket {
682 cmd: 100,
683 direction: MspPacketDirection::Unsupported,
684 data: MspPacketData::from(&[0x44u8, 0x20, 0x00, 0x80][..]),
685 };
686 roundtrip(&packet);
687 }
688 }
689}