Skip to main content

mavlink_core/
lib.rs

1//! The MAVLink message set.
2//!
3//! # Message sets and the `Message` trait
4//! Each message set has its own module with corresponding data types, including a `MavMessage` enum
5//! that represents all possible messages in that message set. The [`Message`] trait is used to
6//! represent messages in an abstract way, and each `MavMessage` enum implements this trait (for
7//! example, [ardupilotmega::MavMessage]). This is then monomorphized to the specific message
8//! set you are using in your application at compile-time via type parameters. If you expect
9//! ArduPilotMega-flavored messages, then you will need a `MavConnection<ardupilotmega::MavMessage>`
10//! and you will receive `ardupilotmega::MavMessage`s from it.
11//!
12//! Some message sets include others. For example, most message sets include the
13//! common message set. These included values are not differently represented in the `MavMessage` enum: a message
14//! in the common message set received on an ArduPilotMega connection will just be an
15//! `ardupilotmega::MavMessage`.
16//!
17//! If you want to enable a given message set, you do not have to enable the
18//! feature for the message sets that it includes. For example, you can use the `dialect-ardupilotmega`
19//! feature without also using the `dialect-uavionix`, `dialect-icarous`, `dialect-common` features.
20//!
21//! [ardupilotmega::MavMessage]: https://docs.rs/mavlink/latest/mavlink/ardupilotmega/enum.MavMessage.html
22//!
23//! # Read Functions
24//!
25//! The `read_*` functions can be used to read a MAVLink message for a [`PeekReader`] wrapping a `[Read]`er.
26//!
27//! They follow the pattern `read_(v1|v2|any|versioned)_(raw_message|msg)[_async][_signed]<M, _>(..)`.
28//! All read functions check for a valid `STX` marker of the corresponding MAVLink version and verify that the message CRC checksum is correct.
29//! They attempt to read until either a whole MAVLink message is read or an error occurrs.
30//! While doing so data without STX marker, with an invalid CRC chechsum or invalid signature (if applicable) is discarded.
31//! To determine for which dialect the message CRC should be verified it must be specified
32//! by using the `Message` enum of the dialect as the generic `M`.
33//!
34//! Unless further specified all combinations of the function name components exist. The components are described bellow:
35//!
36//! - `v1` functions read only MAVLink 1 messages
37//! - `v2` functions read only MAVLink 2 messages
38//! - `any` functions read messages of either MAVLink version
39//! - `versioned` functions read messages of the version specified in an aditional `version` parameter
40//! - `raw_message` functions return an unparsed message as [`MAVLinkV1MessageRaw`], [`MAVLinkV2MessageRaw`] or [`MAVLinkMessageRaw`]
41//! - `msg` functions return a parsed message as a tupel of [`MavHeader`] and the `Message` of the specified dialect
42//! - `_async` functions, which are only enabled with the `tokio` feature, are [async](https://doc.rust-lang.org/std/keyword.async.html) and read from an [`AsyncPeekReader`] instead.
43//! - `_signed` functions, which are only enabled with the `mav2-message-signing` feature, have an `Option<&SigningData>` parameter that allows the use of MAVLink 2 message signing.
44//!   MAVLink 1 exclusive functions do not have a `_signed` variant and functions that allow both MAVLink 1 and 2 messages treat MAVLink 1 messages as unsigned.
45//!   When an invalidly signed message is received it is ignored.
46//!
47//! ## Read Errors
48//! All `read_` functions return `Result<_,` [`MessageReadError`]`>`.
49//!
50//! - All functions will return [`MessageReadError::Io`] of [`UnexpectedEof`] when EOF is encountered before a message could be read.
51//! - All functions will return [`MessageReadError::Io`] when an error occurs on the underlying [`Read`]er or [`AsyncRead`]er.
52//!   
53//! - Functions that parse the received message will return [`MessageReadError::Parse`] when the read data could
54//!   not be parsed as a MAVLink message
55//!
56//! # Write Functions
57//!
58//! The `write_` functions are used to write a MAVLink to a [`Write`]r.
59//! They follow the pattern `write_(v1|v2|versioned)_msg[_async][_signed](..)`:
60//!
61//! - `v1` functions write messages using MAVLink 1 serialisation
62//! - `v2` functions write messages using MAVLink 2 serialisation
63//! - `versioned` functions write messages using the version specified in an aditional `version` parameter
64//! - `_async` functions, which are only enabled with the `tokio` feature, are
65//!   [async](https://doc.rust-lang.org/std/keyword.async.html) and write from an [`tokio::io::AsyncWrite`]r instead.
66//! - `_signed` functions, which are only enabled with the `mav2-message-signing` feature, have an `Option<&SigningData>` parameter that allows the use of MAVLink 2 message signing.
67//!
68//! ## Write errors
69//!
70//! All `write_` functions return `Result<_,` [`MessageWriteError`]`>`.
71//!
72//! - When an error occurs on the underlying [`Write`]er or [`AsyncWrite`]er other then
73//!   [`Interrupted`] the function returns [`MessageWriteError::Io`]
74//! - When attempting to serialize a message with an ID over 255 with MAVLink 1 a [`MessageWriteError::MAVLink2Only`] is returned
75//!
76//! [`PeekReader`]: peek_reader::PeekReader
77//! [`AsyncPeekReader`]: async_peek_reader::AsyncPeekReader
78//! [`UnexpectedEof`]: std::io::ErrorKind::UnexpectedEof
79//! [`AsyncRead`]: tokio::io::AsyncRead
80//! [`AsyncWrite`]: tokio::io::AsyncWrite
81//! [`Interrupted`]: std::io::ErrorKind::Interrupted
82#![cfg_attr(not(feature = "std"), no_std)]
83#![cfg_attr(docsrs, feature(doc_cfg))]
84#![deny(clippy::all)]
85#![warn(clippy::use_self)]
86
87use core::result::Result;
88
89#[cfg(feature = "std")]
90use std::io::{Read, Write};
91
92pub mod utils;
93#[allow(unused_imports)]
94use utils::{RustDefault, remove_trailing_zeroes};
95
96#[cfg(feature = "serde")]
97use serde::{Deserialize, Serialize};
98
99pub mod peek_reader;
100use peek_reader::PeekReader;
101
102use crate::{
103    bytes::Bytes,
104    error::{MessageReadError, MessageWriteError, ParserError},
105};
106
107use crc_any::CRCu16;
108
109#[doc(hidden)]
110pub mod bytes;
111#[doc(hidden)]
112pub mod bytes_mut;
113#[cfg(any(feature = "std", feature = "tokio"))]
114mod connection;
115pub mod consts;
116pub mod error;
117pub mod types;
118#[cfg(feature = "std")]
119pub use self::connection::{Connectable, Connection, MavConnection, connect};
120
121#[cfg(feature = "tokio")]
122pub use self::connection::{AsyncConnectable, AsyncMavConnection, connect_async};
123
124#[cfg(feature = "tokio")]
125pub mod async_peek_reader;
126#[cfg(feature = "tokio")]
127use async_peek_reader::AsyncPeekReader;
128#[cfg(feature = "tokio")]
129use tokio::io::{AsyncWrite, AsyncWriteExt};
130
131#[cfg(all(feature = "embedded", not(feature = "std")))]
132pub mod embedded;
133#[cfg(all(feature = "embedded", not(feature = "std")))]
134use embedded::{Read, Write};
135
136#[cfg(not(feature = "mav2-message-signing"))]
137type SigningData = ();
138#[cfg(feature = "mav2-message-signing")]
139mod signing;
140#[cfg(feature = "mav2-message-signing")]
141pub use self::signing::{SigningConfig, SigningData};
142#[cfg(feature = "mav2-message-signing")]
143use sha2::{Digest, Sha256};
144
145#[cfg(feature = "arbitrary")]
146use arbitrary::Arbitrary;
147
148#[cfg(any(feature = "std", feature = "tokio"))]
149mod connectable;
150
151#[cfg(any(feature = "std", feature = "tokio"))]
152mod connection_shared;
153
154#[cfg(any(feature = "std", feature = "tokio"))]
155pub use connectable::ConnectionAddress;
156
157#[cfg(feature = "transport-direct-serial")]
158pub use connection::direct_serial::config::SerialConfig;
159
160#[cfg(feature = "transport-tcp")]
161pub use connection::tcp::config::{TcpConfig, TcpMode};
162
163#[cfg(feature = "transport-udp")]
164pub use connection::udp::config::{UdpConfig, UdpMode};
165
166#[cfg(feature = "std")]
167pub use connection::file::config::FileConfig;
168
169/// A MAVLink message payload
170///
171/// Each message sets `MavMessage` enum implements this trait. The [`Message`] trait is used to
172/// represent messages in an abstract way (for example, `common::MavMessage`).
173pub trait Message
174where
175    Self: Sized,
176{
177    /// MAVLink message ID
178    fn message_id(&self) -> u32;
179
180    /// MAVLink message name
181    fn message_name(&self) -> &'static str;
182
183    /// Target system ID if the message is directed to a specific system
184    fn target_system_id(&self) -> Option<u8>;
185
186    /// Target component ID if the message is directed to a specific component
187    fn target_component_id(&self) -> Option<u8>;
188
189    /// Serialize **Message** into byte slice and return count of bytes written
190    ///
191    /// # Panics
192    ///
193    /// Will panic if the buffer provided is to small to store this message
194    fn ser(&self, version: MavlinkVersion, bytes: &mut [u8]) -> usize;
195
196    /// Parse a Message from its message id and payload bytes
197    ///
198    /// # Errors
199    ///
200    /// - [`UnknownMessage`] if the given message id is not part of the dialect
201    /// - any other [`ParserError`] returned by the individual message deserialization
202    ///
203    /// [`UnknownMessage`]: ParserError::UnknownMessage
204    fn parse(version: MavlinkVersion, msgid: u32, payload: &[u8]) -> Result<Self, ParserError>;
205
206    /// Return message id of specific message name
207    fn message_id_from_name(name: &str) -> Option<u32>;
208    /// Return a default message of the speicfied message id
209    fn default_message_from_id(id: u32) -> Option<Self>;
210    /// Return random valid message of the speicfied message id
211    #[cfg(feature = "arbitrary")]
212    fn random_message_from_id<R: rand::TryRng<Error = std::convert::Infallible>>(
213        id: u32,
214        rng: &mut R,
215    ) -> Option<Self>;
216    /// Return a message types [CRC_EXTRA byte](https://mavlink.io/en/guide/serialization.html#crc_extra)
217    fn extra_crc(id: u32) -> u8;
218}
219
220pub trait MessageData: Sized {
221    type Message: Message;
222
223    const ID: u32;
224    const NAME: &'static str;
225    const EXTRA_CRC: u8;
226    const ENCODED_LEN: usize;
227
228    /// # Panics
229    ///
230    /// Will panic if the buffer provided is to small to hold the full message payload of the implementing message type
231    fn ser(&self, version: MavlinkVersion, payload: &mut [u8]) -> usize;
232    /// # Errors
233    ///
234    /// Will return [`ParserError::InvalidEnum`] on a nonexistent enum value and
235    fn deser(version: MavlinkVersion, payload: &[u8]) -> Result<Self, ParserError>;
236}
237
238/// Metadata from a MAVLink packet header
239#[derive(Debug, Copy, Clone, PartialEq, Eq)]
240#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
241#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
242pub struct MavHeader {
243    /// Sender system ID
244    pub system_id: u8,
245    /// Sender component ID
246    pub component_id: u8,
247    /// Packet sequence number
248    pub sequence: u8,
249}
250
251/// [Versions of the MAVLink](https://mavlink.io/en/guide/mavlink_version.html) protocol that we support
252#[derive(Debug, Copy, Clone, PartialEq, Eq)]
253#[cfg_attr(feature = "serde", derive(Serialize))]
254#[cfg_attr(feature = "serde", serde(tag = "type"))]
255#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
256pub enum MavlinkVersion {
257    /// Version v1.0
258    V1,
259    /// Version v2.0
260    V2,
261}
262
263/// Message framing marker for MAVLink 1
264pub const MAV_STX: u8 = 0xFE;
265
266/// Message framing marker for MAVLink 2
267pub const MAV_STX_V2: u8 = 0xFD;
268
269/// Return a default GCS header, seq is replaced by the connector
270/// so it can be ignored. Set `component_id` to your desired component ID.
271impl Default for MavHeader {
272    fn default() -> Self {
273        Self {
274            system_id: 255,
275            component_id: 0,
276            sequence: 0,
277        }
278    }
279}
280
281/// Encapsulation of the MAVLink message and the header,
282/// important to preserve information about the sender system
283/// and component id.
284#[derive(Debug, Clone)]
285#[cfg_attr(feature = "serde", derive(Serialize))]
286#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
287pub struct MavFrame<M: Message> {
288    /// Message header data
289    pub header: MavHeader,
290    /// Parsed [`Message`] payload
291    pub msg: M,
292    /// Messages MAVLink version
293    pub protocol_version: MavlinkVersion,
294}
295
296impl<M: Message> MavFrame<M> {
297    /// Serialize MavFrame into a byte slice, so it can be sent over a socket, for example.
298    /// The resulting buffer will start with the sequence field of the MAVLink frame
299    /// and will not include the initial packet marker, length field, and flags.
300    ///
301    /// # Panics
302    ///
303    /// - If the frame does not fit in the provided buffer
304    /// - When attempting to serialize a message with an id greater then 255 with MAVLink 1
305    pub fn ser(&self, buf: &mut [u8]) -> usize {
306        let mut buf = bytes_mut::BytesMut::new(buf);
307
308        // Currently expects a buffer with the sequence field at the start.
309        // If this is updated to include the initial packet marker, length field, and flags,
310        // uncomment.
311        //
312        // match self.protocol_version {
313        //     MavlinkVersion::V2 => {
314        //         buf.put_u8(MAV_STX_V2);
315        //         buf.put_u8(payload_len as u8);
316        //         but.put_u8(0); // incompatibility flags
317        //         buf.put_u8(0); // compatibility flags
318        //     }
319        //     MavlinkVersion::V1 => {
320        //         buf.put_u8(MAV_STX);
321        //         buf.put_u8(payload_len as u8);
322        //     }
323        // }
324
325        // serialize header
326        buf.put_slice(&[
327            self.header.sequence,
328            self.header.system_id,
329            self.header.component_id,
330        ]);
331
332        // message id
333        match self.protocol_version {
334            MavlinkVersion::V2 => buf.put_u24_le(self.msg.message_id()),
335            MavlinkVersion::V1 => buf.put_u8(
336                self.msg
337                    .message_id()
338                    .try_into()
339                    .expect("message is MAVLink 2 only"),
340            ),
341        }
342
343        let header_len = buf.len();
344        // Serialize the payload straight into the destination buffer right after
345        // the header and avoid an intermediate buffer and an extra copy.
346        let payload_len = self.msg.ser(self.protocol_version, &mut buf[header_len..]);
347
348        header_len + payload_len
349    }
350
351    /// Deserialize MavFrame from a slice that has been received from, for example, a socket.
352    /// The input buffer should start with the sequence field of the MAVLink frame. The
353    /// initial packet marker, length field, and flag fields should be excluded.
354    ///
355    /// # Errors
356    ///
357    /// Will return a [`ParserError`] if a message was found but could not be parsed
358    /// or the if the buffer provided does not contain a full message
359    pub fn deser(version: MavlinkVersion, input: &[u8]) -> Result<Self, ParserError> {
360        let mut buf = Bytes::new(input);
361
362        // Currently expects a buffer with the sequence field at the start.
363        // If this is updated to include the initial packet marker, length field, and flags,
364        // uncomment.
365        // <https://mavlink.io/en/guide/serialization.html#mavlink2_packet_format>
366        // match version {
367        //     MavlinkVersion::V2 => buf.get_u32_le(),
368        //     MavlinkVersion::V1 => buf.get_u16_le().into(),
369        // };
370
371        let sequence = buf.get_u8()?;
372        let system_id = buf.get_u8()?;
373        let component_id = buf.get_u8()?;
374        let header = MavHeader {
375            system_id,
376            component_id,
377            sequence,
378        };
379
380        let msg_id = match version {
381            MavlinkVersion::V2 => buf.get_u24_le()?,
382            MavlinkVersion::V1 => buf.get_u8()?.into(),
383        };
384
385        M::parse(version, msg_id, buf.remaining_bytes()).map(|msg| Self {
386            header,
387            msg,
388            protocol_version: version,
389        })
390    }
391
392    /// Return the frame header
393    pub fn header(&self) -> MavHeader {
394        self.header
395    }
396}
397
398/// Calculates the [CRC checksum](https://mavlink.io/en/guide/serialization.html#checksum) of a messages header, payload and the CRC_EXTRA byte.
399pub fn calculate_crc(data: &[u8], extra_crc: u8) -> u16 {
400    let mut crc_calculator = CRCu16::crc16mcrf4cc();
401    crc_calculator.digest(data);
402
403    crc_calculator.digest(&[extra_crc]);
404    crc_calculator.get_crc()
405}
406
407#[derive(Debug, Clone, Copy, PartialEq, Eq)]
408/// MAVLink Version selection when attempting to read
409pub enum ReadVersion {
410    /// Only attempt to read using a single MAVLink version
411    Single(MavlinkVersion),
412    /// Attempt to read messages from both MAVLink versions
413    Any,
414}
415
416impl From<MavlinkVersion> for ReadVersion {
417    fn from(value: MavlinkVersion) -> Self {
418        Self::Single(value)
419    }
420}
421
422/// Read and parse a MAVLink message of the specified version from a [`PeekReader`].
423///
424/// # Errors
425///
426/// See [`read_` function error documentation](crate#read-errors)
427pub fn read_versioned_msg<M: Message, R: Read>(
428    r: &mut PeekReader<R>,
429    version: ReadVersion,
430) -> Result<(MavHeader, M), MessageReadError> {
431    match version {
432        ReadVersion::Single(MavlinkVersion::V2) => read_v2_msg(r),
433        ReadVersion::Single(MavlinkVersion::V1) => read_v1_msg(r),
434        ReadVersion::Any => read_any_msg(r),
435    }
436}
437
438/// Read and parse a MAVLink message of the specified version from a [`PeekReader`].
439///
440/// # Errors
441///
442/// See [`read_` function error documentation](crate#read-errors)
443pub fn read_versioned_raw_message<M: Message, R: Read>(
444    r: &mut PeekReader<R>,
445    version: ReadVersion,
446) -> Result<MAVLinkMessageRaw, MessageReadError> {
447    match version {
448        ReadVersion::Single(MavlinkVersion::V2) => {
449            Ok(MAVLinkMessageRaw::V2(read_v2_raw_message::<M, _>(r)?))
450        }
451        ReadVersion::Single(MavlinkVersion::V1) => {
452            Ok(MAVLinkMessageRaw::V1(read_v1_raw_message::<M, _>(r)?))
453        }
454        ReadVersion::Any => read_any_raw_message::<M, _>(r),
455    }
456}
457
458/// Asynchronously read and parse a MAVLink message of the specified version from a [`AsyncPeekReader`].
459///
460/// # Errors
461///
462/// See [`read_` function error documentation](crate#read-errors)
463#[cfg(feature = "tokio")]
464pub async fn read_versioned_msg_async<M: Message, R: tokio::io::AsyncRead + Unpin>(
465    r: &mut AsyncPeekReader<R>,
466    version: ReadVersion,
467) -> Result<(MavHeader, M), MessageReadError> {
468    match version {
469        ReadVersion::Single(MavlinkVersion::V2) => read_v2_msg_async(r).await,
470        ReadVersion::Single(MavlinkVersion::V1) => read_v1_msg_async(r).await,
471        ReadVersion::Any => read_any_msg_async(r).await,
472    }
473}
474
475/// Asynchronously read and parse a MAVLinkMessageRaw of the specified version from a [`AsyncPeekReader`].
476///
477/// # Errors
478///
479/// See [`read_` function error documentation](crate#read-errors)
480#[cfg(feature = "tokio")]
481pub async fn read_versioned_raw_message_async<M: Message, R: tokio::io::AsyncRead + Unpin>(
482    r: &mut AsyncPeekReader<R>,
483    version: ReadVersion,
484) -> Result<MAVLinkMessageRaw, MessageReadError> {
485    match version {
486        ReadVersion::Single(MavlinkVersion::V2) => Ok(MAVLinkMessageRaw::V2(
487            read_v2_raw_message_async::<M, _>(r).await?,
488        )),
489        ReadVersion::Single(MavlinkVersion::V1) => Ok(MAVLinkMessageRaw::V1(
490            read_v1_raw_message_async::<M, _>(r).await?,
491        )),
492        ReadVersion::Any => read_any_raw_message_async::<M, _>(r).await,
493    }
494}
495
496/// Read and parse a MAVLinkMessageRaw of the specified version from a [`PeekReader`] with signing support.
497///
498/// When using [`ReadVersion::Single`]`(`[`MavlinkVersion::V1`]`)` signing is ignored.
499/// When using [`ReadVersion::Any`] MAVlink 1 messages are treated as unsigned.
500///
501/// # Errors
502///
503/// See [`read_` function error documentation](crate#read-errors)
504#[cfg(feature = "mav2-message-signing")]
505pub fn read_versioned_raw_message_signed<M: Message, R: Read>(
506    r: &mut PeekReader<R>,
507    version: ReadVersion,
508    signing_data: Option<&SigningData>,
509) -> Result<MAVLinkMessageRaw, MessageReadError> {
510    match version {
511        ReadVersion::Single(MavlinkVersion::V2) => Ok(MAVLinkMessageRaw::V2(
512            read_v2_raw_message_inner::<M, _>(r, signing_data)?,
513        )),
514        ReadVersion::Single(MavlinkVersion::V1) => {
515            Ok(MAVLinkMessageRaw::V1(read_v1_raw_message::<M, _>(r)?))
516        }
517        ReadVersion::Any => read_any_raw_message_inner::<M, _>(r, signing_data),
518    }
519}
520
521/// Read and parse a MAVLink message of the specified version from a [`PeekReader`] with signing support.
522///
523/// When using [`ReadVersion::Single`]`(`[`MavlinkVersion::V1`]`)` signing is ignored.
524/// When using [`ReadVersion::Any`] MAVlink 1 messages are treated as unsigned.
525///
526/// # Errors
527///
528/// See [`read_` function error documentation](crate#read-errors)
529#[cfg(feature = "mav2-message-signing")]
530pub fn read_versioned_msg_signed<M: Message, R: Read>(
531    r: &mut PeekReader<R>,
532    version: ReadVersion,
533    signing_data: Option<&SigningData>,
534) -> Result<(MavHeader, M), MessageReadError> {
535    match version {
536        ReadVersion::Single(MavlinkVersion::V2) => read_v2_msg_inner(r, signing_data),
537        ReadVersion::Single(MavlinkVersion::V1) => read_v1_msg(r),
538        ReadVersion::Any => read_any_msg_inner(r, signing_data),
539    }
540}
541
542/// Asynchronously read and parse a MAVLinkMessageRaw of the specified version from a [`AsyncPeekReader`] with signing support.
543///
544/// When using [`ReadVersion::Single`]`(`[`MavlinkVersion::V1`]`)` signing is ignored.
545/// When using [`ReadVersion::Any`] MAVlink 1 messages are treated as unsigned.
546///
547/// # Errors
548///
549/// See [`read_` function error documentation](crate#read-errors)
550#[cfg(all(feature = "tokio", feature = "mav2-message-signing"))]
551pub async fn read_versioned_raw_message_async_signed<
552    M: Message,
553    R: tokio::io::AsyncRead + Unpin,
554>(
555    r: &mut AsyncPeekReader<R>,
556    version: ReadVersion,
557    signing_data: Option<&SigningData>,
558) -> Result<MAVLinkMessageRaw, MessageReadError> {
559    match version {
560        ReadVersion::Single(MavlinkVersion::V2) => Ok(MAVLinkMessageRaw::V2(
561            read_v2_raw_message_async_inner::<M, _>(r, signing_data).await?,
562        )),
563        ReadVersion::Single(MavlinkVersion::V1) => Ok(MAVLinkMessageRaw::V1(
564            read_v1_raw_message_async::<M, _>(r).await?,
565        )),
566        ReadVersion::Any => read_any_raw_message_async_inner::<M, _>(r, signing_data).await,
567    }
568}
569
570/// Asynchronously read and parse a MAVLink message of the specified version from a [`AsyncPeekReader`] with signing support.
571///
572/// When using [`ReadVersion::Single`]`(`[`MavlinkVersion::V1`]`)` signing is ignored.
573/// When using [`ReadVersion::Any`] MAVlink 1 messages are treated as unsigned.
574///
575/// # Errors
576///
577/// See [`read_` function error documentation](crate#read-errors)
578#[cfg(all(feature = "tokio", feature = "mav2-message-signing"))]
579pub async fn read_versioned_msg_async_signed<M: Message, R: tokio::io::AsyncRead + Unpin>(
580    r: &mut AsyncPeekReader<R>,
581    version: ReadVersion,
582    signing_data: Option<&SigningData>,
583) -> Result<(MavHeader, M), MessageReadError> {
584    match version {
585        ReadVersion::Single(MavlinkVersion::V2) => read_v2_msg_async_inner(r, signing_data).await,
586        ReadVersion::Single(MavlinkVersion::V1) => read_v1_msg_async(r).await,
587        ReadVersion::Any => read_any_msg_async_inner(r, signing_data).await,
588    }
589}
590
591#[derive(Debug, Copy, Clone, PartialEq, Eq)]
592/// Byte buffer containing the raw representation of a MAVLink 1 message beginning with the STX marker.
593///
594/// Follow protocol definition: <https://mavlink.io/en/guide/serialization.html#v1_packet_format>.
595/// Maximum size is 263 bytes.
596pub struct MAVLinkV1MessageRaw([u8; consts::v1::FRAME_SIZE]);
597
598impl Default for MAVLinkV1MessageRaw {
599    fn default() -> Self {
600        Self::new()
601    }
602}
603
604impl MAVLinkV1MessageRaw {
605    /// Create a new raw MAVLink 1 message filled with zeros.
606    pub const fn new() -> Self {
607        Self([0; consts::v1::FRAME_SIZE])
608    }
609
610    /// Create a new raw MAVLink 1 message from a given buffer.
611    ///
612    /// Note: This method does not guarantee that the constructed MAVLink message is valid.
613    pub const fn from_bytes_unparsed(bytes: [u8; consts::v1::FRAME_SIZE]) -> Self {
614        Self(bytes)
615    }
616
617    /// Read access to its internal buffer.
618    #[inline]
619    pub fn as_slice(&self) -> &[u8] {
620        &self.0
621    }
622
623    /// Mutable reference to its internal buffer.
624    #[inline]
625    pub fn as_mut_slice(&mut self) -> &mut [u8] {
626        &mut self.0
627    }
628
629    /// Deconstruct the MAVLink message into its owned internal buffer.
630    #[inline]
631    pub fn into_inner(self) -> [u8; consts::v1::FRAME_SIZE] {
632        self.0
633    }
634
635    /// Reference to the 5 byte header slice of the message
636    #[inline]
637    pub fn header(&self) -> &[u8] {
638        &self.0[consts::STX_SIZE..(consts::STX_SIZE + consts::v1::HEADER_SIZE)]
639    }
640
641    /// Mutable reference to the 5 byte header slice of the message
642    #[inline]
643    fn mut_header(&mut self) -> &mut [u8] {
644        &mut self.0[consts::STX_SIZE..(consts::STX_SIZE + consts::v1::HEADER_SIZE)]
645    }
646
647    /// Size of the payload of the message
648    #[inline]
649    pub fn payload_length(&self) -> u8 {
650        self.0[consts::PAYLOAD_LEN_OFFSET]
651    }
652
653    /// Packet sequence number
654    #[inline]
655    pub fn sequence(&self) -> u8 {
656        self.0[2]
657    }
658
659    /// Message sender System ID
660    #[inline]
661    pub fn system_id(&self) -> u8 {
662        self.0[3]
663    }
664
665    /// Message sender Component ID
666    #[inline]
667    pub fn component_id(&self) -> u8 {
668        self.0[4]
669    }
670
671    /// Message ID
672    #[inline]
673    pub fn message_id(&self) -> u8 {
674        self.0[5]
675    }
676
677    /// Reference to the payload byte slice of the message
678    #[inline]
679    pub fn payload(&self) -> &[u8] {
680        let payload_length: usize = self.payload_length().into();
681        let payload_offset = consts::STX_SIZE + consts::v1::HEADER_SIZE;
682        &self.0[payload_offset..(payload_offset + payload_length)]
683    }
684
685    /// [CRC-16 checksum](https://mavlink.io/en/guide/serialization.html#checksum) field of the message
686    #[inline]
687    pub fn checksum(&self) -> u16 {
688        let payload_length: usize = self.payload_length().into();
689        let checksum_offset = consts::STX_SIZE + consts::v1::HEADER_SIZE + payload_length;
690        u16::from_le_bytes([self.0[checksum_offset], self.0[checksum_offset + 1]])
691    }
692
693    #[inline]
694    fn mut_payload_and_checksum(&mut self) -> &mut [u8] {
695        let payload_length: usize = self.payload_length().into();
696        let payload_offset = consts::STX_SIZE + consts::v1::HEADER_SIZE;
697        &mut self.0[payload_offset..(payload_offset + payload_length + consts::CHECKSUM_SIZE)]
698    }
699
700    /// Checks wether the message’s [CRC-16 checksum](https://mavlink.io/en/guide/serialization.html#checksum) calculation matches its checksum field.
701    #[inline]
702    pub fn has_valid_crc<M: Message>(&self) -> bool {
703        let payload_length: usize = self.payload_length().into();
704        let crc_end = consts::STX_SIZE + consts::v1::HEADER_SIZE + payload_length;
705        self.checksum()
706            == calculate_crc(
707                &self.0[consts::STX_SIZE..crc_end],
708                M::extra_crc(self.message_id().into()),
709            )
710    }
711
712    /// Raw byte slice of the message
713    pub fn raw_bytes(&self) -> &[u8] {
714        let payload_length = self.payload_length() as usize;
715        let frame_len =
716            consts::STX_SIZE + consts::v1::HEADER_SIZE + payload_length + consts::CHECKSUM_SIZE;
717        &self.0[..frame_len]
718    }
719
720    /// # Panics
721    ///
722    /// If the `msgid` parameter exceeds 255 and is therefore not supported for MAVLink 1
723    fn serialize_stx_and_header_and_crc(
724        &mut self,
725        header: MavHeader,
726        msgid: u32,
727        payload_length: usize,
728        extra_crc: u8,
729    ) {
730        self.0[consts::STX_OFFSET] = MAV_STX;
731
732        let header_buf = self.mut_header();
733        header_buf.copy_from_slice(&[
734            payload_length as u8,
735            header.sequence,
736            header.system_id,
737            header.component_id,
738            msgid.try_into().unwrap(),
739        ]);
740
741        let crc = calculate_crc(
742            &self.0
743                [consts::STX_SIZE..(consts::STX_SIZE + consts::v1::HEADER_SIZE + payload_length)],
744            extra_crc,
745        );
746        self.0[(consts::STX_SIZE + consts::v1::HEADER_SIZE + payload_length)
747            ..(consts::STX_SIZE
748                + consts::v1::HEADER_SIZE
749                + payload_length
750                + consts::CHECKSUM_SIZE)]
751            .copy_from_slice(&crc.to_le_bytes());
752    }
753
754    /// Serialize a [`Message`] with a given header into this raw message buffer.
755    ///
756    /// # Panics
757    ///
758    /// If the message's id exceeds 255 and is therefore not supported for MAVLink 1
759    pub fn serialize_message<M: Message>(&mut self, header: MavHeader, message: &M) {
760        let payload_offset = consts::STX_SIZE + consts::v1::HEADER_SIZE;
761        let payload_buf = &mut self.0[payload_offset..(payload_offset + consts::MAX_PAYLOAD_LEN)];
762        let payload_length = message.ser(MavlinkVersion::V1, payload_buf);
763
764        let message_id = message.message_id();
765        self.serialize_stx_and_header_and_crc(
766            header,
767            message_id,
768            payload_length,
769            M::extra_crc(message_id),
770        );
771    }
772
773    /// # Panics
774    ///
775    /// If the `MessageData`'s `ID` exceeds 255 and is therefore not supported for MAVLink 1
776    pub fn serialize_message_data<D: MessageData>(&mut self, header: MavHeader, message_data: &D) {
777        let payload_offset = consts::STX_SIZE + consts::v1::HEADER_SIZE;
778        let payload_buf = &mut self.0[payload_offset..(payload_offset + consts::MAX_PAYLOAD_LEN)];
779        let payload_length = message_data.ser(MavlinkVersion::V1, payload_buf);
780
781        self.serialize_stx_and_header_and_crc(header, D::ID, payload_length, D::EXTRA_CRC);
782    }
783}
784
785fn try_decode_v1<M: Message, R: Read>(
786    reader: &mut PeekReader<R>,
787) -> Result<Option<MAVLinkV1MessageRaw>, MessageReadError> {
788    let mut message = MAVLinkV1MessageRaw::new();
789    let whole_header_size = consts::STX_SIZE + consts::v1::HEADER_SIZE;
790
791    message.0[consts::STX_OFFSET] = MAV_STX;
792    let header = &reader.peek_exact(whole_header_size)?[consts::STX_SIZE..whole_header_size];
793    message.mut_header().copy_from_slice(header);
794    let packet_length = message.raw_bytes().len();
795    let payload_and_checksum = &reader.peek_exact(packet_length)?[whole_header_size..packet_length];
796    message
797        .mut_payload_and_checksum()
798        .copy_from_slice(payload_and_checksum);
799
800    // retry if CRC failed after previous STX
801    // (an STX byte may appear in the middle of a message)
802    if message.has_valid_crc::<M>() {
803        reader.consume(message.raw_bytes().len());
804        Ok(Some(message))
805    } else {
806        Ok(None)
807    }
808}
809
810#[cfg(feature = "tokio")]
811// other then the blocking version the STX is read not peeked, this changed some sizes
812async fn try_decode_v1_async<M: Message, R: tokio::io::AsyncRead + Unpin>(
813    reader: &mut AsyncPeekReader<R>,
814) -> Result<Option<MAVLinkV1MessageRaw>, MessageReadError> {
815    let mut message = MAVLinkV1MessageRaw::new();
816
817    message.0[consts::STX_OFFSET] = MAV_STX;
818    let header = &reader.peek_exact(consts::v1::HEADER_SIZE).await?[..consts::v1::HEADER_SIZE];
819    message.mut_header().copy_from_slice(header);
820    let packet_length = message.raw_bytes().len() - consts::STX_SIZE;
821    let payload_and_checksum =
822        &reader.peek_exact(packet_length).await?[consts::v1::HEADER_SIZE..packet_length];
823    message
824        .mut_payload_and_checksum()
825        .copy_from_slice(payload_and_checksum);
826
827    // retry if CRC failed after previous STX
828    // (an STX byte may appear in the middle of a message)
829    if message.has_valid_crc::<M>() {
830        reader.consume(message.raw_bytes().len() - consts::STX_SIZE);
831        Ok(Some(message))
832    } else {
833        Ok(None)
834    }
835}
836
837/// Read a raw MAVLink 1 message from a [`PeekReader`].
838///
839/// # Errors
840///
841/// See [`read_` function error documentation](crate#read-errors)
842pub fn read_v1_raw_message<M: Message, R: Read>(
843    reader: &mut PeekReader<R>,
844) -> Result<MAVLinkV1MessageRaw, MessageReadError> {
845    loop {
846        // search for the magic framing value indicating start of mavlink message
847        while reader.peek_exact(consts::STX_SIZE)?[consts::STX_OFFSET] != MAV_STX {
848            reader.consume(consts::STX_SIZE);
849        }
850
851        if let Some(msg) = try_decode_v1::<M, _>(reader)? {
852            return Ok(msg);
853        }
854
855        reader.consume(consts::STX_SIZE);
856    }
857}
858
859/// Asynchronously read a raw MAVLink 1 message from a [`AsyncPeekReader`].
860///
861/// # Errors
862///
863/// See [`read_` function error documentation](crate#read-errors)
864#[cfg(feature = "tokio")]
865pub async fn read_v1_raw_message_async<M: Message, R: tokio::io::AsyncRead + Unpin>(
866    reader: &mut AsyncPeekReader<R>,
867) -> Result<MAVLinkV1MessageRaw, MessageReadError> {
868    loop {
869        loop {
870            // search for the magic framing value indicating start of mavlink message
871            if reader.read_u8().await? == MAV_STX {
872                break;
873            }
874        }
875
876        if let Some(message) = try_decode_v1_async::<M, _>(reader).await? {
877            return Ok(message);
878        }
879    }
880}
881
882/// Async read a raw buffer with the mavlink message
883/// V1 maximum size is 263 bytes: `<https://mavlink.io/en/guide/serialization.html>`
884///
885/// # Example
886///
887/// See mavlink/examples/embedded-async-read full example for details.
888#[cfg(all(feature = "embedded", not(feature = "std")))]
889pub async fn read_v1_raw_message_async<M: Message>(
890    reader: &mut impl embedded_io_async::Read,
891) -> Result<MAVLinkV1MessageRaw, MessageReadError> {
892    loop {
893        // search for the magic framing value indicating start of mavlink message
894        let mut byte = [0u8; consts::STX_SIZE];
895        loop {
896            reader
897                .read_exact(&mut byte)
898                .await
899                .map_err(|_| MessageReadError::Io)?;
900            if byte[consts::STX_OFFSET] == MAV_STX {
901                break;
902            }
903        }
904
905        let mut message = MAVLinkV1MessageRaw::new();
906
907        message.0[consts::STX_OFFSET] = MAV_STX;
908        reader
909            .read_exact(message.mut_header())
910            .await
911            .map_err(|_| MessageReadError::Io)?;
912        reader
913            .read_exact(message.mut_payload_and_checksum())
914            .await
915            .map_err(|_| MessageReadError::Io)?;
916
917        // retry if CRC failed after previous STX
918        // (an STX byte may appear in the middle of a message)
919        if message.has_valid_crc::<M>() {
920            return Ok(message);
921        }
922    }
923}
924
925/// Read and parse a MAVLink 1 message from a [`PeekReader`].
926///
927/// # Errors
928///
929/// See [`read_` function error documentation](crate#read-errors)
930pub fn read_v1_msg<M: Message, R: Read>(
931    r: &mut PeekReader<R>,
932) -> Result<(MavHeader, M), MessageReadError> {
933    let message = read_v1_raw_message::<M, _>(r)?;
934
935    Ok((
936        MavHeader {
937            sequence: message.sequence(),
938            system_id: message.system_id(),
939            component_id: message.component_id(),
940        },
941        M::parse(
942            MavlinkVersion::V1,
943            u32::from(message.message_id()),
944            message.payload(),
945        )?,
946    ))
947}
948
949/// Asynchronously read and parse a MAVLink 1 message from a [`AsyncPeekReader`].
950///
951/// # Errors
952///
953/// See [`read_` function error documentation](crate#read-errors)
954#[cfg(feature = "tokio")]
955pub async fn read_v1_msg_async<M: Message, R: tokio::io::AsyncRead + Unpin>(
956    r: &mut AsyncPeekReader<R>,
957) -> Result<(MavHeader, M), MessageReadError> {
958    let message = read_v1_raw_message_async::<M, _>(r).await?;
959
960    Ok((
961        MavHeader {
962            sequence: message.sequence(),
963            system_id: message.system_id(),
964            component_id: message.component_id(),
965        },
966        M::parse(
967            MavlinkVersion::V1,
968            u32::from(message.message_id()),
969            message.payload(),
970        )?,
971    ))
972}
973
974/// Asynchronously read and parse a MAVLink 1 message from a [`embedded_io_async::Read`]er.
975///
976/// NOTE: it will be add ~80KB to firmware flash size because all *_DATA::deser methods will be add to firmware.
977/// Use `*_DATA::ser` methods manually to prevent it.
978#[cfg(all(feature = "embedded", not(feature = "std")))]
979pub async fn read_v1_msg_async<M: Message>(
980    r: &mut impl embedded_io_async::Read,
981) -> Result<(MavHeader, M), MessageReadError> {
982    let message = read_v1_raw_message_async::<M>(r).await?;
983
984    Ok((
985        MavHeader {
986            sequence: message.sequence(),
987            system_id: message.system_id(),
988            component_id: message.component_id(),
989        },
990        M::parse(
991            MavlinkVersion::V1,
992            u32::from(message.message_id()),
993            message.payload(),
994        )?,
995    ))
996}
997
998#[derive(Debug, Copy, Clone, PartialEq, Eq)]
999/// Byte buffer containing the raw representation of a MAVLink 2 message beginning with the STX marker.
1000///
1001/// Follow protocol definition: <https://mavlink.io/en/guide/serialization.html#mavlink2_packet_format>.
1002/// Maximum size is [280 bytes](consts::MAX_FRAME_SIZE).
1003pub struct MAVLinkV2MessageRaw([u8; consts::MAX_FRAME_SIZE]);
1004
1005impl Default for MAVLinkV2MessageRaw {
1006    fn default() -> Self {
1007        Self::new()
1008    }
1009}
1010
1011impl MAVLinkV2MessageRaw {
1012    /// Create a new raw MAVLink 2 message filled with zeros.
1013    pub const fn new() -> Self {
1014        Self([0; consts::MAX_FRAME_SIZE])
1015    }
1016
1017    /// Create a new raw MAVLink 2 message from a given buffer.
1018    ///
1019    /// Note: This method does not guarantee that the constructed MAVLink message is valid.
1020    pub const fn from_bytes_unparsed(bytes: [u8; consts::MAX_FRAME_SIZE]) -> Self {
1021        Self(bytes)
1022    }
1023
1024    /// Read access to its internal buffer.
1025    #[inline]
1026    pub fn as_slice(&self) -> &[u8] {
1027        &self.0
1028    }
1029
1030    /// Mutable reference to its internal buffer.
1031    #[inline]
1032    pub fn as_mut_slice(&mut self) -> &mut [u8] {
1033        &mut self.0
1034    }
1035
1036    /// Deconstruct the MAVLink message into its owned internal buffer.
1037    #[inline]
1038    pub fn into_inner(self) -> [u8; consts::MAX_FRAME_SIZE] {
1039        self.0
1040    }
1041
1042    /// Reference to the 9 byte header slice of the message
1043    #[inline]
1044    pub fn header(&self) -> &[u8] {
1045        &self.0[consts::STX_SIZE..(consts::STX_SIZE + consts::v2::HEADER_SIZE)]
1046    }
1047
1048    /// Mutable reference to the header byte slice of the message
1049    #[inline]
1050    fn mut_header(&mut self) -> &mut [u8] {
1051        &mut self.0[consts::STX_SIZE..(consts::STX_SIZE + consts::v2::HEADER_SIZE)]
1052    }
1053
1054    /// Size of the payload of the message
1055    #[inline]
1056    pub fn payload_length(&self) -> u8 {
1057        self.0[consts::PAYLOAD_LEN_OFFSET]
1058    }
1059
1060    /// [Incompatiblity flags](https://mavlink.io/en/guide/serialization.html#incompat_flags) of the message
1061    ///
1062    /// Currently the only supported incompatebility flag is `consts::v2::IFLAG_SIGNED`.
1063    #[inline]
1064    pub fn incompatibility_flags(&self) -> u8 {
1065        self.0[consts::v2::INCOMPAT_FLAGS_OFFSET]
1066    }
1067
1068    /// Mutable reference to the [incompatiblity flags](https://mavlink.io/en/guide/serialization.html#incompat_flags) of the message
1069    ///
1070    /// Currently the only supported incompatebility flag is `consts::v2::IFLAG_SIGNED`.
1071    #[inline]
1072    pub fn incompatibility_flags_mut(&mut self) -> &mut u8 {
1073        &mut self.0[consts::v2::INCOMPAT_FLAGS_OFFSET]
1074    }
1075
1076    /// [Compatibility Flags](https://mavlink.io/en/guide/serialization.html#compat_flags) of the message
1077    #[inline]
1078    pub fn compatibility_flags(&self) -> u8 {
1079        self.0[3]
1080    }
1081
1082    /// Packet sequence number
1083    #[inline]
1084    pub fn sequence(&self) -> u8 {
1085        self.0[4]
1086    }
1087
1088    /// Message sender System ID
1089    #[inline]
1090    pub fn system_id(&self) -> u8 {
1091        self.0[5]
1092    }
1093
1094    /// Message sender Component ID
1095    #[inline]
1096    pub fn component_id(&self) -> u8 {
1097        self.0[6]
1098    }
1099
1100    /// Message ID
1101    #[inline]
1102    pub fn message_id(&self) -> u32 {
1103        u32::from_le_bytes([self.0[7], self.0[8], self.0[9], 0])
1104    }
1105
1106    /// Reference to the payload byte slice of the message
1107    #[inline]
1108    pub fn payload(&self) -> &[u8] {
1109        let payload_length: usize = self.payload_length().into();
1110        let payload_offset = consts::STX_SIZE + consts::v2::HEADER_SIZE;
1111        &self.0[payload_offset..(payload_offset + payload_length)]
1112    }
1113
1114    /// [CRC-16 checksum](https://mavlink.io/en/guide/serialization.html#checksum) field of the message
1115    #[inline]
1116    pub fn checksum(&self) -> u16 {
1117        let payload_length: usize = self.payload_length().into();
1118        let checksum_offset = consts::STX_SIZE + consts::v2::HEADER_SIZE + payload_length;
1119        u16::from_le_bytes([self.0[checksum_offset], self.0[checksum_offset + 1]])
1120    }
1121
1122    /// Reference to the 2 checksum bytes of the message
1123    #[cfg(feature = "mav2-message-signing")]
1124    #[inline]
1125    pub fn checksum_bytes(&self) -> &[u8] {
1126        let checksum_offset =
1127            consts::STX_SIZE + consts::v2::HEADER_SIZE + self.payload_length() as usize;
1128        &self.0[checksum_offset..(checksum_offset + consts::CHECKSUM_SIZE)]
1129    }
1130
1131    /// Signature [Link ID](https://mavlink.io/en/guide/message_signing.html#link_ids)
1132    ///
1133    /// If the message is not signed this 0.
1134    #[cfg(feature = "mav2-message-signing")]
1135    #[inline]
1136    pub fn signature_link_id(&self) -> u8 {
1137        let payload_length: usize = self.payload_length().into();
1138        self.0[consts::STX_SIZE + consts::v2::HEADER_SIZE + payload_length + consts::CHECKSUM_SIZE]
1139    }
1140
1141    /// Mutable reference to the signature [Link ID](https://mavlink.io/en/guide/message_signing.html#link_ids)
1142    #[cfg(feature = "mav2-message-signing")]
1143    #[inline]
1144    pub fn signature_link_id_mut(&mut self) -> &mut u8 {
1145        let payload_length: usize = self.payload_length().into();
1146        &mut self.0
1147            [consts::STX_SIZE + consts::v2::HEADER_SIZE + payload_length + consts::CHECKSUM_SIZE]
1148    }
1149
1150    /// Message [signature timestamp](https://mavlink.io/en/guide/message_signing.html#timestamp)
1151    ///
1152    /// The timestamp is a 48 bit number with units of 10 microseconds since 1st January 2015 GMT.
1153    /// The offset since 1st January 1970 (the unix epoch) is 1420070400 seconds.
1154    /// Since all timestamps generated must be at least 1 more than the previous timestamp this timestamp may get ahead of GMT time if there is a burst of packets at a rate of more than 100000 packets per second.
1155    #[cfg(feature = "mav2-message-signing")]
1156    #[inline]
1157    pub fn signature_timestamp(&self) -> u64 {
1158        let mut timestamp_bytes = [0u8; 8];
1159        timestamp_bytes[0..consts::v2::SIGNATURE_TIMESTAMP_SIZE]
1160            .copy_from_slice(self.signature_timestamp_bytes());
1161        u64::from_le_bytes(timestamp_bytes)
1162    }
1163
1164    /// 48 bit [signature timestamp](https://mavlink.io/en/guide/message_signing.html#timestamp) byte slice
1165    ///
1166    /// If the message is not signed this contains zeros.
1167    #[cfg(feature = "mav2-message-signing")]
1168    #[inline]
1169    pub fn signature_timestamp_bytes(&self) -> &[u8] {
1170        let payload_length: usize = self.payload_length().into();
1171        let timestamp_start = consts::STX_SIZE
1172            + consts::v2::HEADER_SIZE
1173            + payload_length
1174            + consts::CHECKSUM_SIZE
1175            + consts::v2::SIGNATURE_LINK_ID_SIZE;
1176        &self.0[timestamp_start..(timestamp_start + consts::v2::SIGNATURE_TIMESTAMP_SIZE)]
1177    }
1178
1179    /// Mutable reference to the 48 bit signature timestams byte slice
1180    #[cfg(feature = "mav2-message-signing")]
1181    #[inline]
1182    pub fn signature_timestamp_bytes_mut(&mut self) -> &mut [u8] {
1183        let payload_length: usize = self.payload_length().into();
1184        let timestamp_start = consts::STX_SIZE
1185            + consts::v2::HEADER_SIZE
1186            + payload_length
1187            + consts::CHECKSUM_SIZE
1188            + consts::v2::SIGNATURE_LINK_ID_SIZE;
1189        &mut self.0[timestamp_start..(timestamp_start + consts::v2::SIGNATURE_TIMESTAMP_SIZE)]
1190    }
1191
1192    /// Reference to the 48 bit [message signature](https://mavlink.io/en/guide/message_signing.html#signature) byte slice
1193    ///
1194    /// If the message is not signed this contains zeros.
1195    #[cfg(feature = "mav2-message-signing")]
1196    #[inline]
1197    pub fn signature_value(&self) -> &[u8] {
1198        let payload_length: usize = self.payload_length().into();
1199        let signature_start = consts::STX_SIZE
1200            + consts::v2::HEADER_SIZE
1201            + payload_length
1202            + consts::CHECKSUM_SIZE
1203            + consts::v2::SIGNATURE_LINK_ID_SIZE
1204            + consts::v2::SIGNATURE_TIMESTAMP_SIZE;
1205        &self.0[signature_start..(signature_start + consts::v2::SIGNATURE_VALUE_SIZE)]
1206    }
1207
1208    /// Mutable reference to the 48 bit [message signature](https://mavlink.io/en/guide/message_signing.html#signature) byte slice
1209    #[cfg(feature = "mav2-message-signing")]
1210    #[inline]
1211    pub fn signature_value_mut(&mut self) -> &mut [u8] {
1212        let payload_length: usize = self.payload_length().into();
1213        let signature_start = consts::STX_SIZE
1214            + consts::v2::HEADER_SIZE
1215            + payload_length
1216            + consts::CHECKSUM_SIZE
1217            + consts::v2::SIGNATURE_LINK_ID_SIZE
1218            + consts::v2::SIGNATURE_TIMESTAMP_SIZE;
1219        &mut self.0[signature_start..(signature_start + consts::v2::SIGNATURE_VALUE_SIZE)]
1220    }
1221
1222    fn mut_payload_and_checksum_and_sign(&mut self) -> &mut [u8] {
1223        let payload_length: usize = self.payload_length().into();
1224
1225        // Signature to ensure the link is tamper-proof.
1226        let signature_size = if (self.incompatibility_flags() & consts::v2::IFLAG_SIGNED) == 0 {
1227            0
1228        } else {
1229            consts::v2::SIGNATURE_SIZE
1230        };
1231
1232        let payload_offset = consts::STX_SIZE + consts::v2::HEADER_SIZE;
1233        &mut self.0[payload_offset
1234            ..(payload_offset + payload_length + signature_size + consts::CHECKSUM_SIZE)]
1235    }
1236
1237    /// Checks wether the message's [CRC-16 checksum](https://mavlink.io/en/guide/serialization.html#checksum) calculation matches its checksum field.
1238    #[inline]
1239    pub fn has_valid_crc<M: Message>(&self) -> bool {
1240        let payload_length: usize = self.payload_length().into();
1241        let crc_end = consts::STX_SIZE + consts::v2::HEADER_SIZE + payload_length;
1242        self.checksum()
1243            == calculate_crc(
1244                &self.0[consts::STX_SIZE..crc_end],
1245                M::extra_crc(self.message_id()),
1246            )
1247    }
1248
1249    /// Calculates the messages sha256_48 signature.
1250    ///
1251    /// This calculates the [SHA-256](https://en.wikipedia.org/wiki/SHA-2) checksum of messages appended to the 32 byte secret key and copies the first 6 bytes of the result into the target buffer.
1252    #[cfg(feature = "mav2-message-signing")]
1253    pub fn calculate_signature(
1254        &self,
1255        secret_key: &[u8],
1256        target_buffer: &mut [u8; consts::v2::SIGNATURE_VALUE_SIZE],
1257    ) {
1258        let mut hasher = Sha256::new();
1259        hasher.update(secret_key);
1260        hasher.update([MAV_STX_V2]);
1261        hasher.update(self.header());
1262        hasher.update(self.payload());
1263        hasher.update(self.checksum_bytes());
1264        hasher.update([self.signature_link_id()]);
1265        hasher.update(self.signature_timestamp_bytes());
1266        target_buffer.copy_from_slice(&hasher.finalize()[0..consts::v2::SIGNATURE_VALUE_SIZE]);
1267    }
1268
1269    /// Raw byte slice of the message
1270    pub fn raw_bytes(&self) -> &[u8] {
1271        let payload_length = self.payload_length() as usize;
1272
1273        let signature_size = if (self.incompatibility_flags() & consts::v2::IFLAG_SIGNED) == 0 {
1274            0
1275        } else {
1276            consts::v2::SIGNATURE_SIZE
1277        };
1278
1279        let frame_len = consts::STX_SIZE
1280            + consts::v2::HEADER_SIZE
1281            + payload_length
1282            + signature_size
1283            + consts::CHECKSUM_SIZE;
1284        &self.0[..frame_len]
1285    }
1286
1287    fn serialize_stx_and_header_and_crc(
1288        &mut self,
1289        header: MavHeader,
1290        msgid: u32,
1291        payload_length: usize,
1292        extra_crc: u8,
1293        incompat_flags: u8,
1294    ) {
1295        self.0[consts::STX_OFFSET] = MAV_STX_V2;
1296        let msgid_bytes = msgid.to_le_bytes();
1297
1298        let header_buf = self.mut_header();
1299        header_buf.copy_from_slice(&[
1300            payload_length as u8,
1301            incompat_flags,
1302            0, //compat_flags
1303            header.sequence,
1304            header.system_id,
1305            header.component_id,
1306            msgid_bytes[0],
1307            msgid_bytes[1],
1308            msgid_bytes[2],
1309        ]);
1310
1311        let crc = calculate_crc(
1312            &self.0
1313                [consts::STX_SIZE..(consts::STX_SIZE + consts::v2::HEADER_SIZE + payload_length)],
1314            extra_crc,
1315        );
1316        self.0[(consts::STX_SIZE + consts::v2::HEADER_SIZE + payload_length)
1317            ..(consts::STX_SIZE
1318                + consts::v2::HEADER_SIZE
1319                + payload_length
1320                + consts::CHECKSUM_SIZE)]
1321            .copy_from_slice(&crc.to_le_bytes());
1322    }
1323
1324    /// Serialize a [Message] with a given header into this raw message buffer.
1325    ///
1326    /// This does not set any compatiblity or incompatiblity flags.
1327    pub fn serialize_message<M: Message>(&mut self, header: MavHeader, message: &M) {
1328        let payload_offset = consts::STX_SIZE + consts::v2::HEADER_SIZE;
1329        let payload_buf = &mut self.0[payload_offset..(payload_offset + consts::MAX_PAYLOAD_LEN)];
1330        let payload_length = message.ser(MavlinkVersion::V2, payload_buf);
1331
1332        let message_id = message.message_id();
1333        self.serialize_stx_and_header_and_crc(
1334            header,
1335            message_id,
1336            payload_length,
1337            M::extra_crc(message_id),
1338            0,
1339        );
1340    }
1341
1342    /// Serialize a [Message] with a given header into this raw message buffer and sets the `consts::v2::IFLAG_SIGNED` incompatiblity flag.
1343    ///
1344    /// This does not update the message's signature fields.
1345    /// This does not set any compatiblity flags.
1346    pub fn serialize_message_for_signing<M: Message>(&mut self, header: MavHeader, message: &M) {
1347        let payload_offset = consts::STX_SIZE + consts::v2::HEADER_SIZE;
1348        let payload_buf = &mut self.0[payload_offset..(payload_offset + consts::MAX_PAYLOAD_LEN)];
1349        let payload_length = message.ser(MavlinkVersion::V2, payload_buf);
1350
1351        let message_id = message.message_id();
1352        self.serialize_stx_and_header_and_crc(
1353            header,
1354            message_id,
1355            payload_length,
1356            M::extra_crc(message_id),
1357            consts::v2::IFLAG_SIGNED,
1358        );
1359    }
1360
1361    pub fn serialize_message_data<D: MessageData>(&mut self, header: MavHeader, message_data: &D) {
1362        let payload_offset = consts::STX_SIZE + consts::v2::HEADER_SIZE;
1363        let payload_buf = &mut self.0[payload_offset..(payload_offset + consts::MAX_PAYLOAD_LEN)];
1364        let payload_length = message_data.ser(MavlinkVersion::V2, payload_buf);
1365
1366        self.serialize_stx_and_header_and_crc(header, D::ID, payload_length, D::EXTRA_CRC, 0);
1367    }
1368}
1369
1370#[allow(unused_variables)]
1371fn try_decode_v2<M: Message, R: Read>(
1372    reader: &mut PeekReader<R>,
1373    signing_data: Option<&SigningData>,
1374) -> Result<Option<MAVLinkV2MessageRaw>, MessageReadError> {
1375    let mut message = MAVLinkV2MessageRaw::new();
1376    let whole_header_size = consts::STX_SIZE + consts::v2::HEADER_SIZE;
1377
1378    message.0[consts::STX_OFFSET] = MAV_STX_V2;
1379    let header = &reader.peek_exact(whole_header_size)?[consts::STX_SIZE..whole_header_size];
1380    message.mut_header().copy_from_slice(header);
1381
1382    if message.incompatibility_flags() & !consts::v2::SUPPORTED_IFLAGS > 0 {
1383        // if there are incompatibility flags set that we do not know discard the message
1384        reader.consume(consts::STX_SIZE);
1385        return Ok(None);
1386    }
1387
1388    let packet_length = message.raw_bytes().len();
1389    let payload_and_checksum_and_sign =
1390        &reader.peek_exact(packet_length)?[whole_header_size..packet_length];
1391    message
1392        .mut_payload_and_checksum_and_sign()
1393        .copy_from_slice(payload_and_checksum_and_sign);
1394
1395    if message.has_valid_crc::<M>() {
1396        // even if the signature turn out to be invalid the valid crc shows that the received data presents a valid message as opposed to random bytes
1397        reader.consume(message.raw_bytes().len());
1398    } else {
1399        reader.consume(consts::STX_SIZE);
1400        return Ok(None);
1401    }
1402
1403    #[cfg(feature = "mav2-message-signing")]
1404    if let Some(signing_data) = signing_data {
1405        if !signing_data.verify_signature(&message) {
1406            return Ok(None);
1407        }
1408    }
1409
1410    Ok(Some(message))
1411}
1412
1413#[cfg(feature = "tokio")]
1414#[allow(unused_variables)]
1415// other then the blocking version the STX is read not peeked, this changed some sizes
1416async fn try_decode_v2_async<M: Message, R: tokio::io::AsyncRead + Unpin>(
1417    reader: &mut AsyncPeekReader<R>,
1418    signing_data: Option<&SigningData>,
1419) -> Result<Option<MAVLinkV2MessageRaw>, MessageReadError> {
1420    let mut message = MAVLinkV2MessageRaw::new();
1421
1422    message.0[consts::STX_OFFSET] = MAV_STX_V2;
1423    let header = &reader.peek_exact(consts::v2::HEADER_SIZE).await?[..consts::v2::HEADER_SIZE];
1424    message.mut_header().copy_from_slice(header);
1425
1426    if message.incompatibility_flags() & !consts::v2::SUPPORTED_IFLAGS > 0 {
1427        // if there are incompatibility flags set that we do not know discard the message
1428        return Ok(None);
1429    }
1430
1431    let packet_length = message.raw_bytes().len() - consts::STX_SIZE;
1432    let payload_and_checksum_and_sign =
1433        &reader.peek_exact(packet_length).await?[consts::v2::HEADER_SIZE..packet_length];
1434    message
1435        .mut_payload_and_checksum_and_sign()
1436        .copy_from_slice(payload_and_checksum_and_sign);
1437
1438    if message.has_valid_crc::<M>() {
1439        // even if the signature turn out to be invalid the valid crc shows that the received data presents a valid message as opposed to random bytes
1440        reader.consume(message.raw_bytes().len() - consts::STX_SIZE);
1441    } else {
1442        return Ok(None);
1443    }
1444
1445    #[cfg(feature = "mav2-message-signing")]
1446    if let Some(signing_data) = signing_data {
1447        if !signing_data.verify_signature(&message) {
1448            return Ok(None);
1449        }
1450    }
1451
1452    Ok(Some(message))
1453}
1454
1455/// Read a raw MAVLink 2 message from a [`PeekReader`].
1456///
1457/// # Errors
1458///
1459/// See [`read_` function error documentation](crate#read-errors)
1460#[inline]
1461pub fn read_v2_raw_message<M: Message, R: Read>(
1462    reader: &mut PeekReader<R>,
1463) -> Result<MAVLinkV2MessageRaw, MessageReadError> {
1464    read_v2_raw_message_inner::<M, R>(reader, None)
1465}
1466
1467/// Read a raw MAVLink 2 message with signing support from a [`PeekReader`].
1468///
1469/// # Errors
1470///
1471/// See [`read_` function error documentation](crate#read-errors)
1472#[cfg(feature = "mav2-message-signing")]
1473#[inline]
1474pub fn read_v2_raw_message_signed<M: Message, R: Read>(
1475    reader: &mut PeekReader<R>,
1476    signing_data: Option<&SigningData>,
1477) -> Result<MAVLinkV2MessageRaw, MessageReadError> {
1478    read_v2_raw_message_inner::<M, R>(reader, signing_data)
1479}
1480
1481#[allow(unused_variables)]
1482fn read_v2_raw_message_inner<M: Message, R: Read>(
1483    reader: &mut PeekReader<R>,
1484    signing_data: Option<&SigningData>,
1485) -> Result<MAVLinkV2MessageRaw, MessageReadError> {
1486    loop {
1487        // search for the magic framing value indicating start of mavlink message
1488        while reader.peek_exact(consts::STX_SIZE)?[consts::STX_OFFSET] != MAV_STX_V2 {
1489            reader.consume(consts::STX_SIZE);
1490        }
1491
1492        if let Some(message) = try_decode_v2::<M, _>(reader, signing_data)? {
1493            return Ok(message);
1494        }
1495    }
1496}
1497
1498/// Asynchronously read a raw MAVLink 2 message from a [`AsyncPeekReader`].
1499///
1500/// # Errors
1501///
1502/// See [`read_` function error documentation](crate#read-errors)
1503#[cfg(feature = "tokio")]
1504pub async fn read_v2_raw_message_async<M: Message, R: tokio::io::AsyncRead + Unpin>(
1505    reader: &mut AsyncPeekReader<R>,
1506) -> Result<MAVLinkV2MessageRaw, MessageReadError> {
1507    read_v2_raw_message_async_inner::<M, R>(reader, None).await
1508}
1509
1510#[cfg(feature = "tokio")]
1511#[allow(unused_variables)]
1512async fn read_v2_raw_message_async_inner<M: Message, R: tokio::io::AsyncRead + Unpin>(
1513    reader: &mut AsyncPeekReader<R>,
1514    signing_data: Option<&SigningData>,
1515) -> Result<MAVLinkV2MessageRaw, MessageReadError> {
1516    loop {
1517        loop {
1518            // search for the magic framing value indicating start of mavlink message
1519            if reader.read_u8().await? == MAV_STX_V2 {
1520                break;
1521            }
1522        }
1523
1524        if let Some(message) = try_decode_v2_async::<M, _>(reader, signing_data).await? {
1525            return Ok(message);
1526        }
1527    }
1528}
1529
1530/// Asynchronously read a raw MAVLink 2 message with signing support from a [`AsyncPeekReader`]
1531///
1532/// # Errors
1533///
1534/// See [`read_` function error documentation](crate#read-errors)
1535#[cfg(all(feature = "tokio", feature = "mav2-message-signing"))]
1536pub async fn read_v2_raw_message_async_signed<M: Message, R: tokio::io::AsyncRead + Unpin>(
1537    reader: &mut AsyncPeekReader<R>,
1538    signing_data: Option<&SigningData>,
1539) -> Result<MAVLinkV2MessageRaw, MessageReadError> {
1540    read_v2_raw_message_async_inner::<M, R>(reader, signing_data).await
1541}
1542
1543/// Asynchronously read a raw MAVLink 2 message with signing support from a [`embedded_io_async::Read`]er.
1544///
1545/// # Example
1546///
1547/// See mavlink/examples/embedded-async-read full example for details.
1548#[cfg(all(feature = "embedded", not(feature = "std")))]
1549pub async fn read_v2_raw_message_async<M: Message>(
1550    reader: &mut impl embedded_io_async::Read,
1551) -> Result<MAVLinkV2MessageRaw, MessageReadError> {
1552    loop {
1553        // search for the magic framing value indicating start of mavlink message
1554        let mut byte = [0u8; consts::STX_SIZE];
1555        loop {
1556            reader
1557                .read_exact(&mut byte)
1558                .await
1559                .map_err(|_| MessageReadError::Io)?;
1560            if byte[consts::STX_OFFSET] == MAV_STX_V2 {
1561                break;
1562            }
1563        }
1564
1565        let mut message = MAVLinkV2MessageRaw::new();
1566
1567        message.0[consts::STX_OFFSET] = MAV_STX_V2;
1568        reader
1569            .read_exact(message.mut_header())
1570            .await
1571            .map_err(|_| MessageReadError::Io)?;
1572
1573        if message.incompatibility_flags() & !consts::v2::SUPPORTED_IFLAGS > 0 {
1574            // if there are incompatibility flags set that we do not know discard the message
1575            continue;
1576        }
1577
1578        reader
1579            .read_exact(message.mut_payload_and_checksum_and_sign())
1580            .await
1581            .map_err(|_| MessageReadError::Io)?;
1582
1583        // retry if CRC failed after previous STX
1584        // (an STX byte may appear in the middle of a message)
1585        if message.has_valid_crc::<M>() {
1586            return Ok(message);
1587        }
1588    }
1589}
1590
1591/// Read and parse a MAVLink 2 message from a [`PeekReader`].
1592///
1593/// # Errors
1594///
1595/// See [`read_` function error documentation](crate#read-errors)
1596#[inline]
1597pub fn read_v2_msg<M: Message, R: Read>(
1598    read: &mut PeekReader<R>,
1599) -> Result<(MavHeader, M), MessageReadError> {
1600    read_v2_msg_inner(read, None)
1601}
1602
1603/// Read and parse a MAVLink 2 message from a [`PeekReader`].
1604///
1605/// # Errors
1606///
1607/// See [`read_` function error documentation](crate#read-errors)
1608#[cfg(feature = "mav2-message-signing")]
1609#[inline]
1610pub fn read_v2_msg_signed<M: Message, R: Read>(
1611    read: &mut PeekReader<R>,
1612    signing_data: Option<&SigningData>,
1613) -> Result<(MavHeader, M), MessageReadError> {
1614    read_v2_msg_inner(read, signing_data)
1615}
1616
1617fn read_v2_msg_inner<M: Message, R: Read>(
1618    read: &mut PeekReader<R>,
1619    signing_data: Option<&SigningData>,
1620) -> Result<(MavHeader, M), MessageReadError> {
1621    let message = read_v2_raw_message_inner::<M, _>(read, signing_data)?;
1622
1623    Ok((
1624        MavHeader {
1625            sequence: message.sequence(),
1626            system_id: message.system_id(),
1627            component_id: message.component_id(),
1628        },
1629        M::parse(MavlinkVersion::V2, message.message_id(), message.payload())?,
1630    ))
1631}
1632
1633/// Asynchronously read and parse a MAVLink 2 message from a [`AsyncPeekReader`].
1634///  
1635/// # Errors
1636///
1637/// See [`read_` function error documentation](crate#read-errors)
1638#[cfg(feature = "tokio")]
1639pub async fn read_v2_msg_async<M: Message, R: tokio::io::AsyncRead + Unpin>(
1640    read: &mut AsyncPeekReader<R>,
1641) -> Result<(MavHeader, M), MessageReadError> {
1642    read_v2_msg_async_inner(read, None).await
1643}
1644
1645/// Asynchronously read and parse a MAVLink 2 message with signing support from a [`AsyncPeekReader`].
1646///
1647/// # Errors
1648///
1649/// See [`read_` function error documentation](crate#read-errors)
1650#[cfg(all(feature = "tokio", feature = "mav2-message-signing"))]
1651pub async fn read_v2_msg_async_signed<M: Message, R: tokio::io::AsyncRead + Unpin>(
1652    read: &mut AsyncPeekReader<R>,
1653    signing_data: Option<&SigningData>,
1654) -> Result<(MavHeader, M), MessageReadError> {
1655    read_v2_msg_async_inner(read, signing_data).await
1656}
1657
1658#[cfg(feature = "tokio")]
1659async fn read_v2_msg_async_inner<M: Message, R: tokio::io::AsyncRead + Unpin>(
1660    read: &mut AsyncPeekReader<R>,
1661    signing_data: Option<&SigningData>,
1662) -> Result<(MavHeader, M), MessageReadError> {
1663    let message = read_v2_raw_message_async_inner::<M, _>(read, signing_data).await?;
1664
1665    Ok((
1666        MavHeader {
1667            sequence: message.sequence(),
1668            system_id: message.system_id(),
1669            component_id: message.component_id(),
1670        },
1671        M::parse(MavlinkVersion::V2, message.message_id(), message.payload())?,
1672    ))
1673}
1674
1675/// Asynchronously and parse read a MAVLink 2 message from a [`embedded_io_async::Read`]er.
1676///
1677/// NOTE: it will be add ~80KB to firmware flash size because all *_DATA::deser methods will be add to firmware.
1678/// Use `*_DATA::deser` methods manually to prevent it.
1679#[cfg(all(feature = "embedded", not(feature = "std")))]
1680pub async fn read_v2_msg_async<M: Message, R: embedded_io_async::Read>(
1681    r: &mut R,
1682) -> Result<(MavHeader, M), MessageReadError> {
1683    let message = read_v2_raw_message_async::<M>(r).await?;
1684
1685    Ok((
1686        MavHeader {
1687            sequence: message.sequence(),
1688            system_id: message.system_id(),
1689            component_id: message.component_id(),
1690        },
1691        M::parse(
1692            MavlinkVersion::V2,
1693            u32::from(message.message_id()),
1694            message.payload(),
1695        )?,
1696    ))
1697}
1698
1699/// Raw byte representation of a MAVLink message of either version
1700pub enum MAVLinkMessageRaw {
1701    V1(MAVLinkV1MessageRaw),
1702    V2(MAVLinkV2MessageRaw),
1703}
1704
1705impl MAVLinkMessageRaw {
1706    pub fn payload(&self) -> &[u8] {
1707        match self {
1708            Self::V1(msg) => msg.payload(),
1709            Self::V2(msg) => msg.payload(),
1710        }
1711    }
1712    pub fn sequence(&self) -> u8 {
1713        match self {
1714            Self::V1(msg) => msg.sequence(),
1715            Self::V2(msg) => msg.sequence(),
1716        }
1717    }
1718    pub fn system_id(&self) -> u8 {
1719        match self {
1720            Self::V1(msg) => msg.system_id(),
1721            Self::V2(msg) => msg.system_id(),
1722        }
1723    }
1724    pub fn component_id(&self) -> u8 {
1725        match self {
1726            Self::V1(msg) => msg.component_id(),
1727            Self::V2(msg) => msg.component_id(),
1728        }
1729    }
1730    pub fn message_id(&self) -> u32 {
1731        match self {
1732            Self::V1(msg) => u32::from(msg.message_id()),
1733            Self::V2(msg) => msg.message_id(),
1734        }
1735    }
1736    pub fn version(&self) -> MavlinkVersion {
1737        match self {
1738            Self::V1(_) => MavlinkVersion::V1,
1739            Self::V2(_) => MavlinkVersion::V2,
1740        }
1741    }
1742}
1743
1744/// Read a raw MAVLink 1 or 2 message from a [`PeekReader`].
1745///
1746/// # Errors
1747///
1748/// See [`read_` function error documentation](crate#read-errors)
1749#[inline]
1750pub fn read_any_raw_message<M: Message, R: Read>(
1751    reader: &mut PeekReader<R>,
1752) -> Result<MAVLinkMessageRaw, MessageReadError> {
1753    read_any_raw_message_inner::<M, R>(reader, None)
1754}
1755
1756/// Read a raw MAVLink 1 or 2 message from a [`PeekReader`] with signing support.
1757///
1758/// # Errors
1759///
1760/// See [`read_` function error documentation](crate#read-errors)
1761#[cfg(feature = "mav2-message-signing")]
1762#[inline]
1763pub fn read_any_raw_message_signed<M: Message, R: Read>(
1764    reader: &mut PeekReader<R>,
1765    signing_data: Option<&SigningData>,
1766) -> Result<MAVLinkMessageRaw, MessageReadError> {
1767    read_any_raw_message_inner::<M, R>(reader, signing_data)
1768}
1769
1770#[allow(unused_variables)]
1771fn read_any_raw_message_inner<M: Message, R: Read>(
1772    reader: &mut PeekReader<R>,
1773    signing_data: Option<&SigningData>,
1774) -> Result<MAVLinkMessageRaw, MessageReadError> {
1775    loop {
1776        // search for the magic framing value indicating start of MAVLink message
1777        let version = loop {
1778            let byte = reader.peek_exact(consts::STX_SIZE)?[consts::STX_OFFSET];
1779            if byte == MAV_STX {
1780                break MavlinkVersion::V1;
1781            }
1782            if byte == MAV_STX_V2 {
1783                break MavlinkVersion::V2;
1784            }
1785            reader.consume(consts::STX_SIZE);
1786        };
1787        match version {
1788            MavlinkVersion::V1 => {
1789                if let Some(message) = try_decode_v1::<M, _>(reader)? {
1790                    // With signing enabled and unsigned messages not allowed do not further process V1
1791                    #[cfg(feature = "mav2-message-signing")]
1792                    if let Some(signing) = signing_data {
1793                        if signing.config.allow_unsigned {
1794                            return Ok(MAVLinkMessageRaw::V1(message));
1795                        }
1796                    } else {
1797                        return Ok(MAVLinkMessageRaw::V1(message));
1798                    }
1799                    #[cfg(not(feature = "mav2-message-signing"))]
1800                    return Ok(MAVLinkMessageRaw::V1(message));
1801                }
1802
1803                reader.consume(consts::STX_SIZE);
1804            }
1805            MavlinkVersion::V2 => {
1806                if let Some(message) = try_decode_v2::<M, _>(reader, signing_data)? {
1807                    return Ok(MAVLinkMessageRaw::V2(message));
1808                }
1809            }
1810        }
1811    }
1812}
1813
1814/// Asynchronously read a raw MAVLink 1 or 2 message from a [`AsyncPeekReader`].
1815///
1816/// # Errors
1817///
1818/// See [`read_` function error documentation](crate#read-errors)
1819#[cfg(feature = "tokio")]
1820pub async fn read_any_raw_message_async<M: Message, R: tokio::io::AsyncRead + Unpin>(
1821    reader: &mut AsyncPeekReader<R>,
1822) -> Result<MAVLinkMessageRaw, MessageReadError> {
1823    read_any_raw_message_async_inner::<M, R>(reader, None).await
1824}
1825
1826/// Asynchronously read a raw MAVLink 1 or 2 message from a [`AsyncPeekReader`] with signing support.
1827///
1828/// This will attempt to read until encounters a valid message or an error.
1829///
1830/// # Errors
1831///
1832/// See [`read_` function error documentation](crate#read-errors)
1833#[cfg(all(feature = "tokio", feature = "mav2-message-signing"))]
1834pub async fn read_any_raw_message_async_signed<M: Message, R: tokio::io::AsyncRead + Unpin>(
1835    reader: &mut AsyncPeekReader<R>,
1836    signing_data: Option<&SigningData>,
1837) -> Result<MAVLinkMessageRaw, MessageReadError> {
1838    read_any_raw_message_async_inner::<M, R>(reader, signing_data).await
1839}
1840
1841#[cfg(feature = "tokio")]
1842#[allow(unused_variables)]
1843async fn read_any_raw_message_async_inner<M: Message, R: tokio::io::AsyncRead + Unpin>(
1844    reader: &mut AsyncPeekReader<R>,
1845    signing_data: Option<&SigningData>,
1846) -> Result<MAVLinkMessageRaw, MessageReadError> {
1847    loop {
1848        // search for the magic framing value indicating start of MAVLink 1 or 2 message
1849        let version = loop {
1850            let read = reader.read_u8().await?;
1851            if read == MAV_STX {
1852                break MavlinkVersion::V1;
1853            }
1854            if read == MAV_STX_V2 {
1855                break MavlinkVersion::V2;
1856            }
1857        };
1858
1859        match version {
1860            MavlinkVersion::V1 => {
1861                if let Some(message) = try_decode_v1_async::<M, _>(reader).await? {
1862                    // With signing enabled and unsigned messages not allowed do not further process them
1863                    #[cfg(feature = "mav2-message-signing")]
1864                    if let Some(signing) = signing_data {
1865                        if signing.config.allow_unsigned {
1866                            return Ok(MAVLinkMessageRaw::V1(message));
1867                        }
1868                    } else {
1869                        return Ok(MAVLinkMessageRaw::V1(message));
1870                    }
1871                    #[cfg(not(feature = "mav2-message-signing"))]
1872                    return Ok(MAVLinkMessageRaw::V1(message));
1873                }
1874            }
1875            MavlinkVersion::V2 => {
1876                if let Some(message) = try_decode_v2_async::<M, _>(reader, signing_data).await? {
1877                    return Ok(MAVLinkMessageRaw::V2(message));
1878                }
1879            }
1880        }
1881    }
1882}
1883
1884/// Read and parse a MAVLink 1 or 2 message from a [`PeekReader`].
1885///
1886/// # Errors
1887///
1888/// See [`read_` function error documentation](crate#read-errors)
1889#[inline]
1890pub fn read_any_msg<M: Message, R: Read>(
1891    read: &mut PeekReader<R>,
1892) -> Result<(MavHeader, M), MessageReadError> {
1893    read_any_msg_inner(read, None)
1894}
1895
1896/// Read and parse a MAVLink 1 or 2 message from a [`PeekReader`] with signing support.
1897///
1898/// MAVLink 1 messages a treated as unsigned.
1899///
1900/// # Errors
1901///
1902/// See [`read_` function error documentation](crate#read-errors)
1903#[cfg(feature = "mav2-message-signing")]
1904#[inline]
1905pub fn read_any_msg_signed<M: Message, R: Read>(
1906    read: &mut PeekReader<R>,
1907    signing_data: Option<&SigningData>,
1908) -> Result<(MavHeader, M), MessageReadError> {
1909    read_any_msg_inner(read, signing_data)
1910}
1911
1912fn read_any_msg_inner<M: Message, R: Read>(
1913    read: &mut PeekReader<R>,
1914    signing_data: Option<&SigningData>,
1915) -> Result<(MavHeader, M), MessageReadError> {
1916    let message = read_any_raw_message_inner::<M, _>(read, signing_data)?;
1917    Ok((
1918        MavHeader {
1919            sequence: message.sequence(),
1920            system_id: message.system_id(),
1921            component_id: message.component_id(),
1922        },
1923        M::parse(message.version(), message.message_id(), message.payload())?,
1924    ))
1925}
1926
1927/// Asynchronously read and parse a MAVLink 1 or 2 message from a [`AsyncPeekReader`].
1928///
1929/// # Errors
1930///
1931/// See [`read_` function error documentation](crate#read-errors)
1932#[cfg(feature = "tokio")]
1933pub async fn read_any_msg_async<M: Message, R: tokio::io::AsyncRead + Unpin>(
1934    read: &mut AsyncPeekReader<R>,
1935) -> Result<(MavHeader, M), MessageReadError> {
1936    read_any_msg_async_inner(read, None).await
1937}
1938
1939/// Asynchronously read and parse a MAVLink 1 or 2 message from a [`AsyncPeekReader`] with signing support.
1940///
1941/// MAVLink 1 messages a treated as unsigned.
1942///
1943/// # Errors
1944///
1945/// See [`read_` function error documentation](crate#read-errors)
1946#[cfg(all(feature = "tokio", feature = "mav2-message-signing"))]
1947#[inline]
1948pub async fn read_any_msg_async_signed<M: Message, R: tokio::io::AsyncRead + Unpin>(
1949    read: &mut AsyncPeekReader<R>,
1950    signing_data: Option<&SigningData>,
1951) -> Result<(MavHeader, M), MessageReadError> {
1952    read_any_msg_async_inner(read, signing_data).await
1953}
1954
1955#[cfg(feature = "tokio")]
1956async fn read_any_msg_async_inner<M: Message, R: tokio::io::AsyncRead + Unpin>(
1957    read: &mut AsyncPeekReader<R>,
1958    signing_data: Option<&SigningData>,
1959) -> Result<(MavHeader, M), MessageReadError> {
1960    let message = read_any_raw_message_async_inner::<M, _>(read, signing_data).await?;
1961
1962    Ok((
1963        MavHeader {
1964            sequence: message.sequence(),
1965            system_id: message.system_id(),
1966            component_id: message.component_id(),
1967        },
1968        M::parse(message.version(), message.message_id(), message.payload())?,
1969    ))
1970}
1971
1972/// Write a MAVLink message using the given mavlink version to a [`Write`]r.
1973///
1974/// # Errors
1975///
1976/// See [`write_` function error documentation](crate#write-errors).
1977pub fn write_versioned_msg<M: Message, W: Write>(
1978    w: &mut W,
1979    version: MavlinkVersion,
1980    header: MavHeader,
1981    data: &M,
1982) -> Result<usize, MessageWriteError> {
1983    match version {
1984        MavlinkVersion::V2 => write_v2_msg(w, header, data),
1985        MavlinkVersion::V1 => write_v1_msg(w, header, data),
1986    }
1987}
1988
1989/// Write a MAVLink message using the given mavlink version to a [`Write`]r with signing support.
1990///
1991/// When using [`MavlinkVersion::V1`] signing is ignored.
1992///
1993/// # Errors
1994///
1995/// See [`write_` function error documentation](crate#write-errors).
1996#[cfg(feature = "mav2-message-signing")]
1997pub fn write_versioned_msg_signed<M: Message, W: Write>(
1998    w: &mut W,
1999    version: MavlinkVersion,
2000    header: MavHeader,
2001    data: &M,
2002    signing_data: Option<&SigningData>,
2003) -> Result<usize, MessageWriteError> {
2004    match version {
2005        MavlinkVersion::V2 => write_v2_msg_signed(w, header, data, signing_data),
2006        MavlinkVersion::V1 => write_v1_msg(w, header, data),
2007    }
2008}
2009
2010/// Asynchronously write a MAVLink message using the given MAVLink version to a [`AsyncWrite`]r.
2011///
2012/// # Errors
2013///
2014/// See [`write_` function error documentation](crate#write-errors).
2015#[cfg(feature = "tokio")]
2016pub async fn write_versioned_msg_async<M: Message, W: AsyncWrite + Unpin>(
2017    w: &mut W,
2018    version: MavlinkVersion,
2019    header: MavHeader,
2020    data: &M,
2021) -> Result<usize, MessageWriteError> {
2022    match version {
2023        MavlinkVersion::V2 => write_v2_msg_async(w, header, data).await,
2024        MavlinkVersion::V1 => write_v1_msg_async(w, header, data).await,
2025    }
2026}
2027
2028/// Asynchronously write a MAVLink message using the given MAVLink version to a [`AsyncWrite`]r with signing support.
2029///
2030/// When using [`MavlinkVersion::V1`] signing is ignored.
2031///
2032/// # Errors
2033///
2034/// See [`write_` function error documentation](crate#write-errors).
2035#[cfg(all(feature = "tokio", feature = "mav2-message-signing"))]
2036pub async fn write_versioned_msg_async_signed<M: Message, W: AsyncWrite + Unpin>(
2037    w: &mut W,
2038    version: MavlinkVersion,
2039    header: MavHeader,
2040    data: &M,
2041    signing_data: Option<&SigningData>,
2042) -> Result<usize, MessageWriteError> {
2043    match version {
2044        MavlinkVersion::V2 => write_v2_msg_async_signed(w, header, data, signing_data).await,
2045        MavlinkVersion::V1 => write_v1_msg_async(w, header, data).await,
2046    }
2047}
2048
2049/// Asynchronously write a MAVLink message using the given MAVLink version to a [`embedded_io_async::Write`]r.
2050///
2051/// NOTE: it will be add ~70KB to firmware flash size because all *_DATA::ser methods will be add to firmware.
2052/// Use `*_DATA::ser` methods manually to prevent it.
2053#[cfg(all(feature = "embedded", not(feature = "std")))]
2054pub async fn write_versioned_msg_async<M: Message>(
2055    w: &mut impl embedded_io_async::Write,
2056    version: MavlinkVersion,
2057    header: MavHeader,
2058    data: &M,
2059) -> Result<usize, MessageWriteError> {
2060    match version {
2061        MavlinkVersion::V2 => write_v2_msg_async(w, header, data).await,
2062        MavlinkVersion::V1 => write_v1_msg_async(w, header, data).await,
2063    }
2064}
2065
2066/// Write a MAVLink 2 message to a [`Write`]r.
2067///
2068/// # Errors
2069///
2070/// See [`write_` function error documentation](crate#write-errors).
2071pub fn write_v2_msg<M: Message, W: Write>(
2072    w: &mut W,
2073    header: MavHeader,
2074    data: &M,
2075) -> Result<usize, MessageWriteError> {
2076    let mut message_raw = MAVLinkV2MessageRaw::new();
2077    message_raw.serialize_message(header, data);
2078
2079    let payload_length: usize = message_raw.payload_length().into();
2080    let len = consts::STX_SIZE + consts::v2::HEADER_SIZE + payload_length + consts::CHECKSUM_SIZE;
2081
2082    w.write_all(&message_raw.0[..len])?;
2083
2084    Ok(len)
2085}
2086
2087/// Write a MAVLink 2 message to a [`Write`]r with signing support.
2088///
2089/// # Errors
2090///
2091/// See [`write_` function error documentation](crate#write-errors).
2092#[cfg(feature = "mav2-message-signing")]
2093pub fn write_v2_msg_signed<M: Message, W: Write>(
2094    w: &mut W,
2095    header: MavHeader,
2096    data: &M,
2097    signing_data: Option<&SigningData>,
2098) -> Result<usize, MessageWriteError> {
2099    let mut message_raw = MAVLinkV2MessageRaw::new();
2100
2101    let signature_len = if let Some(signing_data) = signing_data {
2102        if signing_data.config.sign_outgoing {
2103            message_raw.serialize_message_for_signing(header, data);
2104            signing_data.sign_message(&mut message_raw);
2105            consts::v2::SIGNATURE_SIZE
2106        } else {
2107            message_raw.serialize_message(header, data);
2108            0
2109        }
2110    } else {
2111        message_raw.serialize_message(header, data);
2112        0
2113    };
2114
2115    let payload_length: usize = message_raw.payload_length().into();
2116    let len = consts::STX_SIZE
2117        + consts::v2::HEADER_SIZE
2118        + payload_length
2119        + consts::CHECKSUM_SIZE
2120        + signature_len;
2121
2122    w.write_all(&message_raw.0[..len])?;
2123
2124    Ok(len)
2125}
2126
2127/// Asynchronously write a MAVLink 2 message to a [`AsyncWrite`]r.
2128///
2129/// # Errors
2130///
2131/// See [`write_` function error documentation](crate#write-errors).
2132#[cfg(feature = "tokio")]
2133pub async fn write_v2_msg_async<M: Message, W: AsyncWrite + Unpin>(
2134    w: &mut W,
2135    header: MavHeader,
2136    data: &M,
2137) -> Result<usize, MessageWriteError> {
2138    let mut message_raw = MAVLinkV2MessageRaw::new();
2139    message_raw.serialize_message(header, data);
2140
2141    let payload_length: usize = message_raw.payload_length().into();
2142    let len = consts::STX_SIZE + consts::v2::HEADER_SIZE + payload_length + consts::CHECKSUM_SIZE;
2143
2144    w.write_all(&message_raw.0[..len]).await?;
2145
2146    Ok(len)
2147}
2148
2149/// Write a MAVLink 2 message to a [`AsyncWrite`]r with signing support.
2150///
2151/// # Errors
2152///
2153/// See [`write_` function error documentation](crate#write-errors).
2154#[cfg(feature = "mav2-message-signing")]
2155#[cfg(feature = "tokio")]
2156pub async fn write_v2_msg_async_signed<M: Message, W: AsyncWrite + Unpin>(
2157    w: &mut W,
2158    header: MavHeader,
2159    data: &M,
2160    signing_data: Option<&SigningData>,
2161) -> Result<usize, MessageWriteError> {
2162    let mut message_raw = MAVLinkV2MessageRaw::new();
2163
2164    let signature_len = if let Some(signing_data) = signing_data {
2165        if signing_data.config.sign_outgoing {
2166            message_raw.serialize_message_for_signing(header, data);
2167            signing_data.sign_message(&mut message_raw);
2168            consts::v2::SIGNATURE_SIZE
2169        } else {
2170            message_raw.serialize_message(header, data);
2171            0
2172        }
2173    } else {
2174        message_raw.serialize_message(header, data);
2175        0
2176    };
2177
2178    let payload_length: usize = message_raw.payload_length().into();
2179    let len = consts::STX_SIZE
2180        + consts::v2::HEADER_SIZE
2181        + payload_length
2182        + consts::CHECKSUM_SIZE
2183        + signature_len;
2184
2185    w.write_all(&message_raw.0[..len]).await?;
2186
2187    Ok(len)
2188}
2189
2190/// Asynchronously write a MAVLink 2 message to a [`embedded_io_async::Write`]r.
2191///
2192/// NOTE: it will be add ~70KB to firmware flash size because all *_DATA::ser methods will be add to firmware.
2193/// Use `*_DATA::ser` methods manually to prevent it.
2194///
2195/// # Errors
2196///
2197/// Returns the first error that occurs when writing to the [`embedded_io_async::Write`]r.
2198#[cfg(all(feature = "embedded", not(feature = "std")))]
2199pub async fn write_v2_msg_async<M: Message>(
2200    w: &mut impl embedded_io_async::Write,
2201    header: MavHeader,
2202    data: &M,
2203) -> Result<usize, MessageWriteError> {
2204    let mut message_raw = MAVLinkV2MessageRaw::new();
2205    message_raw.serialize_message(header, data);
2206
2207    let payload_length: usize = message_raw.payload_length().into();
2208    let len = consts::STX_SIZE + consts::v2::HEADER_SIZE + payload_length + consts::CHECKSUM_SIZE;
2209
2210    w.write_all(&message_raw.0[..len])
2211        .await
2212        .map_err(|_| MessageWriteError::Io)?;
2213
2214    Ok(len)
2215}
2216
2217/// Write a MAVLink 1 message to a [`Write`]r.
2218///
2219/// # Errors
2220///
2221/// See [`write_` function error documentation](crate#write-errors).
2222pub fn write_v1_msg<M: Message, W: Write>(
2223    w: &mut W,
2224    header: MavHeader,
2225    data: &M,
2226) -> Result<usize, MessageWriteError> {
2227    if data.message_id() > u8::MAX.into() {
2228        return Err(MessageWriteError::MAVLink2Only);
2229    }
2230    let mut message_raw = MAVLinkV1MessageRaw::new();
2231    message_raw.serialize_message(header, data);
2232
2233    let payload_length: usize = message_raw.payload_length().into();
2234    let len = consts::STX_SIZE + consts::v1::HEADER_SIZE + payload_length + consts::CHECKSUM_SIZE;
2235
2236    w.write_all(&message_raw.0[..len])?;
2237
2238    Ok(len)
2239}
2240
2241/// Asynchronously write a MAVLink 1 message to a [`AsyncWrite`]r.
2242///
2243/// # Errors
2244///
2245/// Returns the first error that occurs when writing to the [`AsyncWrite`]r.
2246#[cfg(feature = "tokio")]
2247pub async fn write_v1_msg_async<M: Message, W: AsyncWrite + Unpin>(
2248    w: &mut W,
2249    header: MavHeader,
2250    data: &M,
2251) -> Result<usize, MessageWriteError> {
2252    if data.message_id() > u8::MAX.into() {
2253        return Err(MessageWriteError::MAVLink2Only);
2254    }
2255    let mut message_raw = MAVLinkV1MessageRaw::new();
2256    message_raw.serialize_message(header, data);
2257
2258    let payload_length: usize = message_raw.payload_length().into();
2259    let len = consts::STX_SIZE + consts::v1::HEADER_SIZE + payload_length + consts::CHECKSUM_SIZE;
2260
2261    w.write_all(&message_raw.0[..len]).await?;
2262
2263    Ok(len)
2264}
2265
2266/// Write a MAVLink 1 message to a [`embedded_io_async::Write`]r.
2267///
2268/// NOTE: it will be add ~70KB to firmware flash size because all *_DATA::ser methods will be add to firmware.
2269/// Use `*_DATA::ser` methods manually to prevent it.
2270#[cfg(all(feature = "embedded", not(feature = "std")))]
2271pub async fn write_v1_msg_async<M: Message>(
2272    w: &mut impl embedded_io_async::Write,
2273    header: MavHeader,
2274    data: &M,
2275) -> Result<usize, MessageWriteError> {
2276    if data.message_id() > u8::MAX.into() {
2277        return Err(MessageWriteError::MAVLink2Only);
2278    }
2279    let mut message_raw = MAVLinkV1MessageRaw::new();
2280    message_raw.serialize_message(header, data);
2281
2282    let payload_length: usize = message_raw.payload_length().into();
2283    let len = consts::STX_SIZE + consts::v1::HEADER_SIZE + payload_length + consts::CHECKSUM_SIZE;
2284
2285    w.write_all(&message_raw.0[..len])
2286        .await
2287        .map_err(|_| MessageWriteError::Io)?;
2288
2289    Ok(len)
2290}