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