cu_msp_lib/
lib.rs

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