1#![cfg_attr(not(feature = "std"), no_std)]
53#![cfg_attr(docsrs, feature(doc_cfg))]
54#![deny(clippy::all)]
55#![warn(clippy::use_self)]
56
57use core::result::Result;
58
59#[cfg(feature = "std")]
60use std::io::Write;
61
62pub mod utils;
63#[allow(unused_imports)]
64use utils::{RustDefault, remove_trailing_zeroes};
65
66#[cfg(feature = "serde")]
67use serde::{Deserialize, Serialize};
68
69use crate::frame_decoder::FrameRef;
70use crate::{
71 bytes::Bytes,
72 error::{EncoderError, MessageWriteError, ParserError},
73};
74
75use crc_any::CRCu16;
76
77#[doc(hidden)]
78pub mod bytes;
79#[doc(hidden)]
80pub mod bytes_mut;
81#[cfg(any(feature = "std", feature = "tokio"))]
82mod connection;
83pub mod consts;
84pub mod error;
85mod frame_decoder;
86mod reader;
87#[doc(hidden)]
88pub mod static_dialect;
89pub mod types;
90#[cfg(feature = "std")]
91pub use self::connection::{
92 Connectable, Connection, DialectConnection, MavConnection, connect, connect_with_dialect,
93};
94pub use reader::MavlinkReader;
95
96#[cfg(feature = "tokio")]
97pub use self::connection::{
98 AsyncConnectable, AsyncConnection, AsyncDialectConnection, AsyncMavConnection, connect_async,
99 connect_async_with_dialect,
100};
101
102#[cfg(feature = "tokio")]
103use tokio::io::{AsyncWrite, AsyncWriteExt};
104
105#[cfg(any(feature = "tokio", all(feature = "embedded", not(feature = "std"))))]
106mod async_reader;
107#[cfg(any(feature = "tokio", all(feature = "embedded", not(feature = "std"))))]
108pub use async_reader::AsyncMavlinkReader;
109
110#[cfg(all(feature = "embedded", not(feature = "std")))]
111pub mod embedded;
112#[cfg(all(feature = "embedded", not(feature = "std")))]
113use embedded::Write;
114
115#[cfg(not(feature = "mav2-message-signing"))]
116type SigningData = ();
117#[cfg(feature = "mav2-message-signing")]
118mod signing;
119#[cfg(feature = "mav2-message-signing")]
120pub use self::signing::{SigningConfig, SigningData};
121#[cfg(feature = "mav2-message-signing")]
122use sha2::{Digest, Sha256};
123
124#[cfg(feature = "arbitrary")]
125use arbitrary::Arbitrary;
126
127#[cfg(any(feature = "std", feature = "tokio"))]
128mod connectable;
129
130#[cfg(any(feature = "std", feature = "tokio"))]
131mod connection_shared;
132
133#[cfg(any(feature = "std", feature = "tokio"))]
134pub use connectable::ConnectionAddress;
135
136#[cfg(feature = "transport-direct-serial")]
137pub use connection::direct_serial::config::SerialConfig;
138
139#[cfg(feature = "transport-tcp")]
140pub use connection::tcp::config::{TcpConfig, TcpMode};
141
142#[cfg(feature = "transport-udp")]
143pub use connection::udp::config::{UdpConfig, UdpMode};
144
145#[cfg(feature = "std")]
146pub use connection::file::config::FileConfig;
147
148pub trait Message
153where
154 Self: Sized,
155{
156 fn message_id(&self) -> u32;
158
159 fn message_name(&self) -> &'static str;
161
162 fn target_system_id(&self) -> Option<u8>;
164
165 fn target_component_id(&self) -> Option<u8>;
167
168 fn target_system_id_mut(&mut self) -> Option<&mut u8>;
173
174 fn target_component_id_mut(&mut self) -> Option<&mut u8>;
179
180 fn ser(&self, version: MavlinkVersion, bytes: &mut [u8]) -> usize;
186
187 fn parse(version: MavlinkVersion, msgid: u32, payload: &[u8]) -> Result<Self, ParserError>;
196
197 fn message_id_from_name(name: &str) -> Option<u32>;
199 fn default_message_from_id(id: u32) -> Option<Self>;
201 #[cfg(feature = "arbitrary")]
203 fn random_message_from_id<R: rand::TryRng<Error = std::convert::Infallible>>(
204 id: u32,
205 rng: &mut R,
206 ) -> Option<Self>;
207 fn extra_crc(id: u32) -> u8;
209}
210
211pub trait MessageData: Sized {
212 type Message: Message;
213
214 const ID: u32;
215 const NAME: &'static str;
216 const EXTRA_CRC: u8;
217 const ENCODED_LEN: usize;
218
219 fn ser(&self, version: MavlinkVersion, payload: &mut [u8]) -> usize;
223 fn deser(version: MavlinkVersion, payload: &[u8]) -> Result<Self, ParserError>;
227}
228
229pub trait Dialect {
238 type Message;
240
241 type EncodeError: Into<EncoderError>;
243
244 fn message_id(&self, message: &Self::Message) -> u32;
246
247 fn extra_crc(&self, message_id: u32) -> Option<u8>;
252
253 fn decode(
255 &self,
256 version: MavlinkVersion,
257 message_id: u32,
258 payload: &[u8],
259 ) -> Result<Self::Message, ParserError>;
260
261 fn encode(
263 &self,
264 version: MavlinkVersion,
265 message: &Self::Message,
266 payload: &mut [u8],
267 ) -> Result<usize, Self::EncodeError>;
268}
269
270#[derive(Debug, Copy, Clone, PartialEq, Eq)]
272#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
273#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
274pub struct MavHeader {
275 pub system_id: u32,
277 pub component_id: u8,
279 pub sequence: u8,
281}
282
283#[derive(Debug, Copy, Clone, PartialEq, Eq)]
285#[cfg_attr(feature = "serde", derive(Serialize))]
286#[cfg_attr(feature = "serde", serde(tag = "type"))]
287#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
288pub enum MavlinkVersion {
289 V1,
291 V2,
293}
294
295pub const MAV_STX: u8 = 0xFE;
297
298pub const MAV_STX_V2: u8 = 0xFD;
300
301impl Default for MavHeader {
304 fn default() -> Self {
305 Self {
306 system_id: 255,
307 component_id: 0,
308 sequence: 0,
309 }
310 }
311}
312
313#[derive(Debug, Clone)]
317#[cfg_attr(feature = "serde", derive(Serialize))]
318#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
319pub struct MavFrame<M: Message> {
320 pub header: MavHeader,
322 pub msg: M,
324 pub protocol_version: MavlinkVersion,
326}
327
328impl<M: Message> MavFrame<M> {
329 pub fn ser(&self, buf: &mut [u8]) -> usize {
339 let mut buf = bytes_mut::BytesMut::new(buf);
340
341 if let Ok(sys_id_8) = self.header.system_id.try_into() {
360 buf.put_slice(&[self.header.sequence, sys_id_8, self.header.component_id]);
361 } else {
362 buf.put_u8(self.header.sequence);
363 match self.protocol_version {
364 MavlinkVersion::V2 => buf.put_u32_le(self.header.system_id),
365 MavlinkVersion::V1 => panic!("system IDs over 255 are MAVLink 2 only"),
366 }
367 buf.put_u8(self.header.component_id);
368 }
369
370 match self.protocol_version {
372 MavlinkVersion::V2 => buf.put_u24_le(self.msg.message_id()),
373 MavlinkVersion::V1 => buf.put_u8(
374 self.msg
375 .message_id()
376 .try_into()
377 .expect("message is MAVLink 2 only"),
378 ),
379 }
380
381 let header_len = buf.len();
382 let payload_len = self.msg.ser(self.protocol_version, &mut buf[header_len..]);
385
386 header_len + payload_len
387 }
388
389 #[deprecated = "Use deser_with_flags to support the IFLAG_SYSID32 incompatebility flag"]
399 pub fn deser(version: MavlinkVersion, input: &[u8]) -> Result<Self, ParserError> {
400 Self::deser_with_flags(version, 0, input)
401 }
402
403 pub fn deser_with_flags(
412 version: MavlinkVersion,
413 incompat_flags: u8,
414 input: &[u8],
415 ) -> Result<Self, ParserError> {
416 let mut buf = Bytes::new(input);
417
418 let sequence = buf.get_u8()?;
428 let system_id = if incompat_flags & consts::v2::IFLAG_SYSID32 != 0 {
429 buf.get_u32_le()?
430 } else {
431 buf.get_u8()? as u32
432 };
433 let component_id = buf.get_u8()?;
434 let header = MavHeader {
435 system_id,
436 component_id,
437 sequence,
438 };
439
440 let msg_id = match version {
441 MavlinkVersion::V2 => buf.get_u24_le()?,
442 MavlinkVersion::V1 => buf.get_u8()?.into(),
443 };
444
445 M::parse(version, msg_id, buf.remaining_bytes()).map(|msg| Self {
446 header,
447 msg,
448 protocol_version: version,
449 })
450 }
451
452 pub fn header(&self) -> MavHeader {
454 self.header
455 }
456}
457
458pub fn calculate_crc(data: &[u8], extra_crc: u8) -> u16 {
460 let mut crc_calculator = CRCu16::crc16mcrf4cc();
461 crc_calculator.digest(data);
462
463 crc_calculator.digest(&[extra_crc]);
464 crc_calculator.get_crc()
465}
466
467#[derive(Debug, Copy, Clone, PartialEq, Eq)]
472pub struct MAVLinkV1MessageRaw([u8; consts::v1::FRAME_SIZE]);
473
474impl MAVLinkV1MessageRaw {
475 pub const fn new() -> Self {
477 Self([0; consts::v1::FRAME_SIZE])
478 }
479
480 pub const fn from_bytes_unparsed(bytes: [u8; consts::v1::FRAME_SIZE]) -> Self {
484 Self(bytes)
485 }
486
487 #[inline]
489 pub fn as_slice(&self) -> &[u8] {
490 &self.0
491 }
492
493 #[inline]
495 pub fn as_mut_slice(&mut self) -> &mut [u8] {
496 &mut self.0
497 }
498
499 #[inline]
501 pub fn into_inner(self) -> [u8; consts::v1::FRAME_SIZE] {
502 self.0
503 }
504
505 #[inline]
507 pub fn header(&self) -> &[u8] {
508 &self.0[consts::STX_SIZE..(consts::STX_SIZE + consts::v1::HEADER_SIZE)]
509 }
510
511 #[inline]
513 fn mut_header(&mut self) -> &mut [u8] {
514 &mut self.0[consts::STX_SIZE..(consts::STX_SIZE + consts::v1::HEADER_SIZE)]
515 }
516
517 #[inline]
519 pub fn payload_length(&self) -> u8 {
520 self.0[consts::PAYLOAD_LEN_OFFSET]
521 }
522
523 #[inline]
525 pub fn sequence(&self) -> u8 {
526 self.0[2]
527 }
528
529 #[inline]
531 pub fn system_id(&self) -> u8 {
532 self.0[3]
533 }
534
535 #[inline]
537 pub fn component_id(&self) -> u8 {
538 self.0[4]
539 }
540
541 #[inline]
543 pub fn message_id(&self) -> u8 {
544 self.0[5]
545 }
546
547 #[inline]
549 pub fn payload(&self) -> &[u8] {
550 let payload_length: usize = self.payload_length().into();
551 let payload_offset = consts::STX_SIZE + consts::v1::HEADER_SIZE;
552 &self.0[payload_offset..(payload_offset + payload_length)]
553 }
554
555 #[inline]
557 pub fn checksum(&self) -> u16 {
558 let payload_length: usize = self.payload_length().into();
559 let checksum_offset = consts::STX_SIZE + consts::v1::HEADER_SIZE + payload_length;
560 u16::from_le_bytes([self.0[checksum_offset], self.0[checksum_offset + 1]])
561 }
562
563 #[inline]
565 pub fn has_valid_crc<M: Message>(&self) -> bool {
566 self.has_valid_crc_with_extra_crc(M::extra_crc(self.message_id().into()))
567 }
568
569 #[inline]
572 pub fn has_valid_crc_with_extra_crc(&self, extra_crc: u8) -> bool {
573 let payload_length: usize = self.payload_length().into();
574 let crc_end = consts::STX_SIZE + consts::v1::HEADER_SIZE + payload_length;
575 self.checksum() == calculate_crc(&self.0[consts::STX_SIZE..crc_end], extra_crc)
576 }
577
578 pub fn raw_bytes(&self) -> &[u8] {
580 let payload_length = self.payload_length() as usize;
581 let frame_len =
582 consts::STX_SIZE + consts::v1::HEADER_SIZE + payload_length + consts::CHECKSUM_SIZE;
583 &self.0[..frame_len]
584 }
585
586 fn serialize_stx_and_header_and_crc(
590 &mut self,
591 header: MavHeader,
592 msgid: u32,
593 payload_length: usize,
594 extra_crc: u8,
595 ) {
596 self.0[consts::STX_OFFSET] = MAV_STX;
597
598 let header_buf = self.mut_header();
599 header_buf.copy_from_slice(&[
600 payload_length as u8,
601 header.sequence,
602 header.system_id as u8,
603 header.component_id,
604 msgid.try_into().unwrap(),
605 ]);
606
607 let crc = calculate_crc(
608 &self.0
609 [consts::STX_SIZE..(consts::STX_SIZE + consts::v1::HEADER_SIZE + payload_length)],
610 extra_crc,
611 );
612 self.0[(consts::STX_SIZE + consts::v1::HEADER_SIZE + payload_length)
613 ..(consts::STX_SIZE
614 + consts::v1::HEADER_SIZE
615 + payload_length
616 + consts::CHECKSUM_SIZE)]
617 .copy_from_slice(&crc.to_le_bytes());
618 }
619
620 pub fn serialize_message<M: Message>(&mut self, header: MavHeader, message: &M) {
626 assert!(header.system_id <= u8::MAX as u32);
627 let payload_offset = consts::STX_SIZE + consts::v1::HEADER_SIZE;
628 let payload_buf = &mut self.0[payload_offset..(payload_offset + consts::MAX_PAYLOAD_LEN)];
629 let payload_length = message.ser(MavlinkVersion::V1, payload_buf);
630
631 let message_id = message.message_id();
632 self.serialize_stx_and_header_and_crc(
633 header,
634 message_id,
635 payload_length,
636 M::extra_crc(message_id),
637 );
638 }
639
640 pub fn serialize_message_with_dialect<D: Dialect>(
642 &mut self,
643 header: MavHeader,
644 dialect: &D,
645 message: &D::Message,
646 ) -> Result<(), EncoderError> {
647 if header.system_id > u8::MAX as u32 {
648 return Err(EncoderError::InvalidMAVLink1SystemId {
649 id: header.system_id,
650 });
651 }
652 let payload_offset = consts::STX_SIZE + consts::v1::HEADER_SIZE;
653 let payload = &mut self.0[payload_offset..(payload_offset + consts::MAX_PAYLOAD_LEN)];
654 let message_id = dialect.message_id(message);
655 let payload_length = dialect
656 .encode(MavlinkVersion::V1, message, payload)
657 .map_err(Into::into)?;
658 let extra_crc = dialect
659 .extra_crc(message_id)
660 .ok_or(EncoderError::UnknownMessage { id: message_id })?;
661 self.serialize_stx_and_header_and_crc(header, message_id, payload_length, extra_crc);
662 Ok(())
663 }
664
665 pub fn serialize_message_data<D: MessageData>(&mut self, header: MavHeader, message_data: &D) {
669 assert!(header.system_id <= u8::MAX as u32);
670 let payload_offset = consts::STX_SIZE + consts::v1::HEADER_SIZE;
671 let payload_buf = &mut self.0[payload_offset..(payload_offset + consts::MAX_PAYLOAD_LEN)];
672 let payload_length = message_data.ser(MavlinkVersion::V1, payload_buf);
673
674 self.serialize_stx_and_header_and_crc(header, D::ID, payload_length, D::EXTRA_CRC);
675 }
676}
677
678impl Default for MAVLinkV1MessageRaw {
679 fn default() -> Self {
680 Self::new()
681 }
682}
683
684impl<'a> From<FrameRef<'a>> for MAVLinkV1MessageRaw {
685 fn from(frame: FrameRef<'a>) -> Self {
686 let mut message = Self::new();
687 message.0[..frame.bytes().len()].copy_from_slice(frame.bytes());
688 message
689 }
690}
691
692#[derive(Debug, Copy, Clone, PartialEq, Eq)]
697pub struct MAVLinkV2MessageRaw([u8; consts::MAX_FRAME_SIZE]);
698
699impl MAVLinkV2MessageRaw {
700 pub const fn new() -> Self {
702 Self([0; consts::MAX_FRAME_SIZE])
703 }
704
705 pub const fn from_bytes_unparsed(bytes: [u8; consts::MAX_FRAME_SIZE]) -> Self {
709 Self(bytes)
710 }
711
712 #[inline]
714 pub fn as_slice(&self) -> &[u8] {
715 &self.0
716 }
717
718 #[inline]
720 pub fn as_mut_slice(&mut self) -> &mut [u8] {
721 &mut self.0
722 }
723
724 #[inline]
726 pub fn into_inner(self) -> [u8; consts::MAX_FRAME_SIZE] {
727 self.0
728 }
729
730 fn header_size(&self) -> usize {
731 if self.has_32bit_sysid() {
732 consts::v2::MIN_HEADER_SIZE + consts::v2::SYSID32_EXTRA_HEADER_SIZE
733 } else {
734 consts::v2::MIN_HEADER_SIZE
735 }
736 }
737
738 #[inline]
740 pub fn header(&self) -> &[u8] {
741 &self.0[consts::STX_SIZE..(consts::STX_SIZE + self.header_size())]
742 }
743
744 #[inline]
746 fn mut_header(&mut self) -> &mut [u8] {
747 let header_size = self.header_size();
748 &mut self.0[consts::STX_SIZE..(consts::STX_SIZE + header_size)]
749 }
750
751 #[inline]
753 pub fn payload_length(&self) -> u8 {
754 self.0[consts::PAYLOAD_LEN_OFFSET]
755 }
756
757 #[inline]
761 pub fn incompatibility_flags(&self) -> u8 {
762 self.0[consts::v2::INCOMPAT_FLAGS_OFFSET]
763 }
764
765 #[inline]
769 pub fn incompatibility_flags_mut(&mut self) -> &mut u8 {
770 &mut self.0[consts::v2::INCOMPAT_FLAGS_OFFSET]
771 }
772
773 fn has_32bit_sysid(&self) -> bool {
774 self.incompatibility_flags() & consts::v2::IFLAG_SYSID32 != 0
775 }
776
777 #[inline]
779 pub fn compatibility_flags(&self) -> u8 {
780 self.0[3]
781 }
782
783 #[inline]
785 pub fn sequence(&self) -> u8 {
786 self.0[4]
787 }
788
789 #[inline]
791 pub fn system_id(&self) -> u32 {
792 if self.has_32bit_sysid() {
793 u32::from_le_bytes(self.0[5..=8].try_into().unwrap())
794 } else {
795 self.0[5] as u32
796 }
797 }
798
799 #[inline]
801 pub fn component_id(&self) -> u8 {
802 if self.has_32bit_sysid() {
803 self.0[9]
804 } else {
805 self.0[6]
806 }
807 }
808
809 #[inline]
811 pub fn message_id(&self) -> u32 {
812 if self.incompatibility_flags() & consts::v2::IFLAG_SYSID32 != 0 {
813 u32::from_le_bytes([self.0[10], self.0[11], self.0[12], 0])
814 } else {
815 u32::from_le_bytes([self.0[7], self.0[8], self.0[9], 0])
816 }
817 }
818
819 fn payload_offset(&self) -> usize {
820 consts::STX_SIZE + self.header_size()
821 }
822
823 #[inline]
825 pub fn payload(&self) -> &[u8] {
826 let payload_length: usize = self.payload_length().into();
827 let payload_offset = self.payload_offset();
828 &self.0[payload_offset..(payload_offset + payload_length)]
829 }
830
831 #[inline]
833 pub fn checksum(&self) -> u16 {
834 let payload_length: usize = self.payload_length().into();
835 let checksum_offset = consts::STX_SIZE + self.header_size() + payload_length;
836 u16::from_le_bytes([self.0[checksum_offset], self.0[checksum_offset + 1]])
837 }
838
839 #[cfg(feature = "mav2-message-signing")]
841 #[inline]
842 pub fn checksum_bytes(&self) -> &[u8] {
843 let checksum_offset =
844 consts::STX_SIZE + self.header_size() + self.payload_length() as usize;
845 &self.0[checksum_offset..(checksum_offset + consts::CHECKSUM_SIZE)]
846 }
847
848 #[cfg(feature = "mav2-message-signing")]
852 #[inline]
853 pub fn signature_link_id(&self) -> u8 {
854 let payload_length: usize = self.payload_length().into();
855 self.0[consts::STX_SIZE + self.header_size() + payload_length + consts::CHECKSUM_SIZE]
856 }
857
858 #[cfg(feature = "mav2-message-signing")]
860 #[inline]
861 pub fn signature_link_id_mut(&mut self) -> &mut u8 {
862 let payload_length: usize = self.payload_length().into();
863 &mut self.0[consts::STX_SIZE + self.header_size() + payload_length + consts::CHECKSUM_SIZE]
864 }
865
866 #[cfg(feature = "mav2-message-signing")]
872 #[inline]
873 pub fn signature_timestamp(&self) -> u64 {
874 let mut timestamp_bytes = [0u8; 8];
875 timestamp_bytes[0..consts::v2::SIGNATURE_TIMESTAMP_SIZE]
876 .copy_from_slice(self.signature_timestamp_bytes());
877 u64::from_le_bytes(timestamp_bytes)
878 }
879
880 #[cfg(feature = "mav2-message-signing")]
884 #[inline]
885 pub fn signature_timestamp_bytes(&self) -> &[u8] {
886 let payload_length: usize = self.payload_length().into();
887 let timestamp_start = consts::STX_SIZE
888 + self.header_size()
889 + payload_length
890 + consts::CHECKSUM_SIZE
891 + consts::v2::SIGNATURE_LINK_ID_SIZE;
892 &self.0[timestamp_start..(timestamp_start + consts::v2::SIGNATURE_TIMESTAMP_SIZE)]
893 }
894
895 #[cfg(feature = "mav2-message-signing")]
897 #[inline]
898 pub fn signature_timestamp_bytes_mut(&mut self) -> &mut [u8] {
899 let payload_length: usize = self.payload_length().into();
900 let timestamp_start = consts::STX_SIZE
901 + self.header_size()
902 + payload_length
903 + consts::CHECKSUM_SIZE
904 + consts::v2::SIGNATURE_LINK_ID_SIZE;
905 &mut self.0[timestamp_start..(timestamp_start + consts::v2::SIGNATURE_TIMESTAMP_SIZE)]
906 }
907
908 #[cfg(feature = "mav2-message-signing")]
912 #[inline]
913 pub fn signature_value(&self) -> &[u8] {
914 let payload_length: usize = self.payload_length().into();
915 let signature_start = consts::STX_SIZE
916 + self.header_size()
917 + payload_length
918 + consts::CHECKSUM_SIZE
919 + consts::v2::SIGNATURE_LINK_ID_SIZE
920 + consts::v2::SIGNATURE_TIMESTAMP_SIZE;
921 &self.0[signature_start..(signature_start + consts::v2::SIGNATURE_VALUE_SIZE)]
922 }
923
924 #[cfg(feature = "mav2-message-signing")]
926 #[inline]
927 pub fn signature_value_mut(&mut self) -> &mut [u8] {
928 let payload_length: usize = self.payload_length().into();
929 let signature_start = consts::STX_SIZE
930 + self.header_size()
931 + payload_length
932 + consts::CHECKSUM_SIZE
933 + consts::v2::SIGNATURE_LINK_ID_SIZE
934 + consts::v2::SIGNATURE_TIMESTAMP_SIZE;
935 &mut self.0[signature_start..(signature_start + consts::v2::SIGNATURE_VALUE_SIZE)]
936 }
937
938 #[inline]
940 pub fn has_valid_crc<M: Message>(&self) -> bool {
941 self.has_valid_crc_with_extra_crc(M::extra_crc(self.message_id()))
942 }
943
944 #[inline]
947 pub fn has_valid_crc_with_extra_crc(&self, extra_crc: u8) -> bool {
948 let payload_length: usize = self.payload_length().into();
949 let crc_end = consts::STX_SIZE + self.header_size() + payload_length;
950 self.checksum() == calculate_crc(&self.0[consts::STX_SIZE..crc_end], extra_crc)
951 }
952
953 #[cfg(feature = "mav2-message-signing")]
957 pub fn calculate_signature(
958 &self,
959 secret_key: &[u8],
960 target_buffer: &mut [u8; consts::v2::SIGNATURE_VALUE_SIZE],
961 ) {
962 let mut hasher = Sha256::new();
963 hasher.update(secret_key);
964 hasher.update([MAV_STX_V2]);
965 hasher.update(self.header());
966 hasher.update(self.payload());
967 hasher.update(self.checksum_bytes());
968 hasher.update([self.signature_link_id()]);
969 hasher.update(self.signature_timestamp_bytes());
970 target_buffer.copy_from_slice(&hasher.finalize()[0..consts::v2::SIGNATURE_VALUE_SIZE]);
971 }
972
973 pub fn raw_bytes(&self) -> &[u8] {
975 let payload_length = self.payload_length() as usize;
976
977 let signature_size = if (self.incompatibility_flags() & consts::v2::IFLAG_SIGNED) == 0 {
978 0
979 } else {
980 consts::v2::SIGNATURE_SIZE
981 };
982
983 let frame_len = consts::STX_SIZE
984 + self.header_size()
985 + payload_length
986 + signature_size
987 + consts::CHECKSUM_SIZE;
988 &self.0[..frame_len]
989 }
990
991 fn serialize_stx_and_header_and_crc(
992 &mut self,
993 header: MavHeader,
994 msgid: u32,
995 payload_length: usize,
996 extra_crc: u8,
997 incompat_flags: u8,
998 ) {
999 self.0[consts::STX_OFFSET] = MAV_STX_V2;
1000 let msgid_bytes = msgid.to_le_bytes();
1001
1002 let has_32bit_sysid = self.has_32bit_sysid();
1003 let header_buf = self.mut_header();
1004 if has_32bit_sysid {
1005 let sys_id_bytes = header.system_id.to_le_bytes();
1006 header_buf.copy_from_slice(&[
1007 payload_length as u8,
1008 incompat_flags,
1009 0, header.sequence,
1011 sys_id_bytes[0],
1012 sys_id_bytes[1],
1013 sys_id_bytes[2],
1014 sys_id_bytes[3],
1015 header.component_id,
1016 msgid_bytes[0],
1017 msgid_bytes[1],
1018 msgid_bytes[2],
1019 ]);
1020 } else {
1021 header_buf.copy_from_slice(&[
1022 payload_length as u8,
1023 incompat_flags,
1024 0, header.sequence,
1026 header.system_id as u8,
1027 header.component_id,
1028 msgid_bytes[0],
1029 msgid_bytes[1],
1030 msgid_bytes[2],
1031 ]);
1032 }
1033
1034 let header_size = self.header_size();
1035
1036 let crc = calculate_crc(
1037 &self.0[consts::STX_SIZE..(consts::STX_SIZE + header_size + payload_length)],
1038 extra_crc,
1039 );
1040 self.0[(consts::STX_SIZE + header_size + payload_length)
1041 ..(consts::STX_SIZE + header_size + payload_length + consts::CHECKSUM_SIZE)]
1042 .copy_from_slice(&crc.to_le_bytes());
1043 }
1044
1045 pub fn serialize_message<M: Message>(&mut self, header: MavHeader, message: &M) {
1049 if header.system_id > u8::MAX as u32 {
1050 *self.incompatibility_flags_mut() = consts::v2::IFLAG_SYSID32;
1051 }
1052 let payload_offset = self.payload_offset();
1053 let payload_buf = &mut self.0[payload_offset..(payload_offset + consts::MAX_PAYLOAD_LEN)];
1054 let payload_length = message.ser(MavlinkVersion::V2, payload_buf);
1055
1056 let message_id = message.message_id();
1057 self.serialize_stx_and_header_and_crc(
1058 header,
1059 message_id,
1060 payload_length,
1061 M::extra_crc(message_id),
1062 self.incompatibility_flags(),
1063 );
1064 }
1065
1066 pub fn serialize_message_with_dialect<D: Dialect>(
1068 &mut self,
1069 header: MavHeader,
1070 dialect: &D,
1071 message: &D::Message,
1072 ) -> Result<(), EncoderError> {
1073 if header.system_id > u8::MAX as u32 {
1074 *self.incompatibility_flags_mut() = consts::v2::IFLAG_SYSID32;
1075 }
1076 let payload_offset = self.payload_offset();
1077 let payload = &mut self.0[payload_offset..(payload_offset + consts::MAX_PAYLOAD_LEN)];
1078 let message_id = dialect.message_id(message);
1079 let payload_length = dialect
1080 .encode(MavlinkVersion::V2, message, payload)
1081 .map_err(Into::into)?;
1082 let extra_crc = dialect
1083 .extra_crc(message_id)
1084 .ok_or(EncoderError::UnknownMessage { id: message_id })?;
1085 self.serialize_stx_and_header_and_crc(
1086 header,
1087 message_id,
1088 payload_length,
1089 extra_crc,
1090 self.incompatibility_flags(),
1091 );
1092 Ok(())
1093 }
1094
1095 #[cfg(feature = "mav2-message-signing")]
1097 pub fn serialize_message_with_dialect_for_signing<D: Dialect>(
1098 &mut self,
1099 header: MavHeader,
1100 dialect: &D,
1101 message: &D::Message,
1102 ) -> Result<(), EncoderError> {
1103 let iflags = if header.system_id > u8::MAX as u32 {
1104 consts::v2::IFLAG_SYSID32 | consts::v2::IFLAG_SIGNED
1105 } else {
1106 consts::v2::IFLAG_SIGNED
1107 };
1108 *self.incompatibility_flags_mut() = iflags;
1109 let payload_offset = self.payload_offset();
1110 let payload = &mut self.0[payload_offset..(payload_offset + consts::MAX_PAYLOAD_LEN)];
1111 let message_id = dialect.message_id(message);
1112 let payload_length = dialect
1113 .encode(MavlinkVersion::V2, message, payload)
1114 .map_err(Into::into)?;
1115 let extra_crc = dialect
1116 .extra_crc(message_id)
1117 .ok_or(EncoderError::UnknownMessage { id: message_id })?;
1118
1119 self.serialize_stx_and_header_and_crc(
1120 header,
1121 message_id,
1122 payload_length,
1123 extra_crc,
1124 iflags,
1125 );
1126 Ok(())
1127 }
1128
1129 #[cfg(feature = "mav2-message-signing")]
1134 pub fn serialize_message_for_signing<M: Message>(&mut self, header: MavHeader, message: &M) {
1135 let iflags = if header.system_id > u8::MAX as u32 {
1136 consts::v2::IFLAG_SYSID32 | consts::v2::IFLAG_SIGNED
1137 } else {
1138 consts::v2::IFLAG_SIGNED
1139 };
1140 *self.incompatibility_flags_mut() = iflags;
1141 let payload_offset = self.payload_offset();
1142 let payload_buf = &mut self.0[payload_offset..(payload_offset + consts::MAX_PAYLOAD_LEN)];
1143 let payload_length = message.ser(MavlinkVersion::V2, payload_buf);
1144
1145 let message_id = message.message_id();
1146 self.serialize_stx_and_header_and_crc(
1147 header,
1148 message_id,
1149 payload_length,
1150 M::extra_crc(message_id),
1151 iflags,
1152 );
1153 }
1154
1155 pub fn serialize_message_data<D: MessageData>(&mut self, header: MavHeader, message_data: &D) {
1156 if header.system_id > u8::MAX as u32 {
1157 *self.incompatibility_flags_mut() = consts::v2::IFLAG_SYSID32;
1158 }
1159 let payload_offset = self.payload_offset();
1160 let payload_buf = &mut self.0[payload_offset..(payload_offset + consts::MAX_PAYLOAD_LEN)];
1161 let payload_length = message_data.ser(MavlinkVersion::V2, payload_buf);
1162
1163 self.serialize_stx_and_header_and_crc(
1164 header,
1165 D::ID,
1166 payload_length,
1167 D::EXTRA_CRC,
1168 self.incompatibility_flags(),
1169 );
1170 }
1171}
1172
1173impl Default for MAVLinkV2MessageRaw {
1174 fn default() -> Self {
1175 Self::new()
1176 }
1177}
1178
1179impl<'a> From<FrameRef<'a>> for MAVLinkV2MessageRaw {
1180 fn from(frame: FrameRef<'a>) -> Self {
1181 let mut message = Self::new();
1182 message.0[..frame.bytes().len()].copy_from_slice(frame.bytes());
1183 message
1184 }
1185}
1186
1187pub enum MAVLinkMessageRaw {
1189 V1(MAVLinkV1MessageRaw),
1190 V2(MAVLinkV2MessageRaw),
1191}
1192
1193impl MAVLinkMessageRaw {
1194 pub fn payload(&self) -> &[u8] {
1195 match self {
1196 Self::V1(msg) => msg.payload(),
1197 Self::V2(msg) => msg.payload(),
1198 }
1199 }
1200 pub fn sequence(&self) -> u8 {
1201 match self {
1202 Self::V1(msg) => msg.sequence(),
1203 Self::V2(msg) => msg.sequence(),
1204 }
1205 }
1206 pub fn system_id(&self) -> u32 {
1207 match self {
1208 Self::V1(msg) => msg.system_id() as u32,
1209 Self::V2(msg) => msg.system_id(),
1210 }
1211 }
1212 pub fn component_id(&self) -> u8 {
1213 match self {
1214 Self::V1(msg) => msg.component_id(),
1215 Self::V2(msg) => msg.component_id(),
1216 }
1217 }
1218 pub fn message_id(&self) -> u32 {
1219 match self {
1220 Self::V1(msg) => u32::from(msg.message_id()),
1221 Self::V2(msg) => msg.message_id(),
1222 }
1223 }
1224 pub fn version(&self) -> MavlinkVersion {
1225 match self {
1226 Self::V1(_) => MavlinkVersion::V1,
1227 Self::V2(_) => MavlinkVersion::V2,
1228 }
1229 }
1230}
1231
1232pub fn write_versioned_msg<M: Message, W: Write>(
1238 w: &mut W,
1239 version: MavlinkVersion,
1240 header: MavHeader,
1241 data: &M,
1242) -> Result<usize, MessageWriteError> {
1243 match version {
1244 MavlinkVersion::V2 => write_v2_msg(w, header, data),
1245 MavlinkVersion::V1 => write_v1_msg(w, header, data),
1246 }
1247}
1248
1249pub fn write_versioned_msg_with_dialect<D: Dialect, W: Write>(
1251 writer: &mut W,
1252 version: MavlinkVersion,
1253 header: MavHeader,
1254 dialect: &D,
1255 message: &D::Message,
1256) -> Result<usize, MessageWriteError> {
1257 match version {
1258 MavlinkVersion::V1 => {
1259 let mut frame = MAVLinkV1MessageRaw::new();
1260 frame.serialize_message_with_dialect(header, dialect, message)?;
1261 writer.write_all(frame.raw_bytes())?;
1262 Ok(frame.raw_bytes().len())
1263 }
1264 MavlinkVersion::V2 => {
1265 let mut frame = MAVLinkV2MessageRaw::new();
1266 frame.serialize_message_with_dialect(header, dialect, message)?;
1267 writer.write_all(frame.raw_bytes())?;
1268 Ok(frame.raw_bytes().len())
1269 }
1270 }
1271}
1272
1273#[cfg(feature = "mav2-message-signing")]
1275pub fn write_versioned_msg_with_dialect_signed<D: Dialect, W: Write>(
1276 writer: &mut W,
1277 version: MavlinkVersion,
1278 header: MavHeader,
1279 dialect: &D,
1280 message: &D::Message,
1281 signing_data: Option<&SigningData>,
1282) -> Result<usize, MessageWriteError> {
1283 if version == MavlinkVersion::V1 {
1284 return write_versioned_msg_with_dialect(writer, version, header, dialect, message);
1285 }
1286
1287 let mut frame = MAVLinkV2MessageRaw::new();
1288 if let Some(signing_data) = signing_data
1289 && signing_data.config.sign_outgoing
1290 {
1291 frame.serialize_message_with_dialect_for_signing(header, dialect, message)?;
1292 signing_data.sign_message(&mut frame);
1293 } else {
1294 frame.serialize_message_with_dialect(header, dialect, message)?;
1295 }
1296 writer.write_all(frame.raw_bytes())?;
1297 Ok(frame.raw_bytes().len())
1298}
1299
1300#[cfg(feature = "mav2-message-signing")]
1308pub fn write_versioned_msg_signed<M: Message, W: Write>(
1309 w: &mut W,
1310 version: MavlinkVersion,
1311 header: MavHeader,
1312 data: &M,
1313 signing_data: Option<&SigningData>,
1314) -> Result<usize, MessageWriteError> {
1315 match version {
1316 MavlinkVersion::V2 => write_v2_msg_signed(w, header, data, signing_data),
1317 MavlinkVersion::V1 => write_v1_msg(w, header, data),
1318 }
1319}
1320
1321#[cfg(feature = "tokio")]
1327pub async fn write_versioned_msg_async<M: Message, W: AsyncWrite + Unpin>(
1328 w: &mut W,
1329 version: MavlinkVersion,
1330 header: MavHeader,
1331 data: &M,
1332) -> Result<usize, MessageWriteError> {
1333 match version {
1334 MavlinkVersion::V2 => write_v2_msg_async(w, header, data).await,
1335 MavlinkVersion::V1 => write_v1_msg_async(w, header, data).await,
1336 }
1337}
1338
1339#[cfg(feature = "tokio")]
1341pub async fn write_versioned_msg_async_with_dialect<D: Dialect, W: AsyncWrite + Unpin>(
1342 writer: &mut W,
1343 version: MavlinkVersion,
1344 header: MavHeader,
1345 dialect: &D,
1346 message: &D::Message,
1347) -> Result<usize, MessageWriteError> {
1348 let mut frame = match version {
1349 MavlinkVersion::V1 => {
1350 let mut frame = MAVLinkV1MessageRaw::new();
1351 frame.serialize_message_with_dialect(header, dialect, message)?;
1352 writer.write_all(frame.raw_bytes()).await?;
1353 return Ok(frame.raw_bytes().len());
1354 }
1355 MavlinkVersion::V2 => MAVLinkV2MessageRaw::new(),
1356 };
1357 frame.serialize_message_with_dialect(header, dialect, message)?;
1358 writer.write_all(frame.raw_bytes()).await?;
1359 Ok(frame.raw_bytes().len())
1360}
1361
1362#[cfg(all(feature = "tokio", feature = "mav2-message-signing"))]
1364pub async fn write_versioned_msg_async_with_dialect_signed<D: Dialect, W: AsyncWrite + Unpin>(
1365 writer: &mut W,
1366 version: MavlinkVersion,
1367 header: MavHeader,
1368 dialect: &D,
1369 message: &D::Message,
1370 signing_data: Option<&SigningData>,
1371) -> Result<usize, MessageWriteError> {
1372 if version == MavlinkVersion::V1 {
1373 return write_versioned_msg_async_with_dialect(writer, version, header, dialect, message)
1374 .await;
1375 }
1376 let mut frame = MAVLinkV2MessageRaw::new();
1377 if let Some(signing_data) = signing_data
1378 && signing_data.config.sign_outgoing
1379 {
1380 frame.serialize_message_with_dialect_for_signing(header, dialect, message)?;
1381 signing_data.sign_message(&mut frame);
1382 } else {
1383 frame.serialize_message_with_dialect(header, dialect, message)?;
1384 }
1385 writer.write_all(frame.raw_bytes()).await?;
1386 Ok(frame.raw_bytes().len())
1387}
1388
1389#[cfg(all(feature = "tokio", feature = "mav2-message-signing"))]
1397pub async fn write_versioned_msg_async_signed<M: Message, W: AsyncWrite + Unpin>(
1398 w: &mut W,
1399 version: MavlinkVersion,
1400 header: MavHeader,
1401 data: &M,
1402 signing_data: Option<&SigningData>,
1403) -> Result<usize, MessageWriteError> {
1404 match version {
1405 MavlinkVersion::V2 => write_v2_msg_async_signed(w, header, data, signing_data).await,
1406 MavlinkVersion::V1 => write_v1_msg_async(w, header, data).await,
1407 }
1408}
1409
1410#[cfg(all(feature = "embedded", not(feature = "std")))]
1415pub async fn write_versioned_msg_async<M: Message>(
1416 w: &mut impl embedded_io_async::Write,
1417 version: MavlinkVersion,
1418 header: MavHeader,
1419 data: &M,
1420) -> Result<usize, MessageWriteError> {
1421 match version {
1422 MavlinkVersion::V2 => write_v2_msg_async(w, header, data).await,
1423 MavlinkVersion::V1 => write_v1_msg_async(w, header, data).await,
1424 }
1425}
1426
1427pub fn write_v2_msg<M: Message, W: Write>(
1433 w: &mut W,
1434 header: MavHeader,
1435 data: &M,
1436) -> Result<usize, MessageWriteError> {
1437 let mut message_raw = MAVLinkV2MessageRaw::new();
1438 message_raw.serialize_message(header, data);
1439
1440 let header_size = message_raw.header_size();
1441 let payload_length: usize = message_raw.payload_length().into();
1442 let len = consts::STX_SIZE + header_size + payload_length + consts::CHECKSUM_SIZE;
1443
1444 w.write_all(&message_raw.0[..len])?;
1445
1446 Ok(len)
1447}
1448
1449#[cfg(feature = "mav2-message-signing")]
1455pub fn write_v2_msg_signed<M: Message, W: Write>(
1456 w: &mut W,
1457 header: MavHeader,
1458 data: &M,
1459 signing_data: Option<&SigningData>,
1460) -> Result<usize, MessageWriteError> {
1461 let mut message_raw = MAVLinkV2MessageRaw::new();
1462
1463 let signature_len = if let Some(signing_data) = signing_data
1464 && signing_data.config.sign_outgoing
1465 {
1466 message_raw.serialize_message_for_signing(header, data);
1467 signing_data.sign_message(&mut message_raw);
1468 consts::v2::SIGNATURE_SIZE
1469 } else {
1470 message_raw.serialize_message(header, data);
1471 0
1472 };
1473
1474 let header_size = message_raw.header_size();
1475 let payload_length: usize = message_raw.payload_length().into();
1476 let len =
1477 consts::STX_SIZE + header_size + payload_length + consts::CHECKSUM_SIZE + signature_len;
1478
1479 w.write_all(&message_raw.0[..len])?;
1480
1481 Ok(len)
1482}
1483
1484#[cfg(feature = "tokio")]
1490pub async fn write_v2_msg_async<M: Message, W: AsyncWrite + Unpin>(
1491 w: &mut W,
1492 header: MavHeader,
1493 data: &M,
1494) -> Result<usize, MessageWriteError> {
1495 let mut message_raw = MAVLinkV2MessageRaw::new();
1496 message_raw.serialize_message(header, data);
1497
1498 let header_size = message_raw.header_size();
1499 let payload_length: usize = message_raw.payload_length().into();
1500 let len = consts::STX_SIZE + header_size + payload_length + consts::CHECKSUM_SIZE;
1501
1502 w.write_all(&message_raw.0[..len]).await?;
1503
1504 Ok(len)
1505}
1506
1507#[cfg(all(feature = "tokio", feature = "mav2-message-signing"))]
1513pub async fn write_v2_msg_async_signed<M: Message, W: AsyncWrite + Unpin>(
1514 w: &mut W,
1515 header: MavHeader,
1516 data: &M,
1517 signing_data: Option<&SigningData>,
1518) -> Result<usize, MessageWriteError> {
1519 let mut message_raw = MAVLinkV2MessageRaw::new();
1520
1521 let signature_len = if let Some(signing_data) = signing_data
1522 && signing_data.config.sign_outgoing
1523 {
1524 message_raw.serialize_message_for_signing(header, data);
1525 signing_data.sign_message(&mut message_raw);
1526 consts::v2::SIGNATURE_SIZE
1527 } else {
1528 message_raw.serialize_message(header, data);
1529 0
1530 };
1531
1532 let header_size = message_raw.header_size();
1533 let payload_length: usize = message_raw.payload_length().into();
1534 let len =
1535 consts::STX_SIZE + header_size + payload_length + consts::CHECKSUM_SIZE + signature_len;
1536
1537 w.write_all(&message_raw.0[..len]).await?;
1538
1539 Ok(len)
1540}
1541
1542#[cfg(all(feature = "embedded", not(feature = "std")))]
1551pub async fn write_v2_msg_async<M: Message>(
1552 w: &mut impl embedded_io_async::Write,
1553 header: MavHeader,
1554 data: &M,
1555) -> Result<usize, MessageWriteError> {
1556 let mut message_raw = MAVLinkV2MessageRaw::new();
1557 message_raw.serialize_message(header, data);
1558
1559 let header_size = message_raw.header_size();
1560 let payload_length: usize = message_raw.payload_length().into();
1561 let len = consts::STX_SIZE + header_size + payload_length + consts::CHECKSUM_SIZE;
1562
1563 w.write_all(&message_raw.0[..len])
1564 .await
1565 .map_err(|_| MessageWriteError::Io)?;
1566
1567 Ok(len)
1568}
1569
1570pub fn write_v1_msg<M: Message, W: Write>(
1576 w: &mut W,
1577 header: MavHeader,
1578 data: &M,
1579) -> Result<usize, MessageWriteError> {
1580 if data.message_id() > u8::MAX.into() {
1581 return Err(MessageWriteError::MAVLink2Only);
1582 }
1583 let mut message_raw = MAVLinkV1MessageRaw::new();
1584 message_raw.serialize_message(header, data);
1585
1586 let payload_length: usize = message_raw.payload_length().into();
1587 let len = consts::STX_SIZE + consts::v1::HEADER_SIZE + payload_length + consts::CHECKSUM_SIZE;
1588
1589 w.write_all(&message_raw.0[..len])?;
1590
1591 Ok(len)
1592}
1593
1594#[cfg(feature = "tokio")]
1600pub async fn write_v1_msg_async<M: Message, W: AsyncWrite + Unpin>(
1601 w: &mut W,
1602 header: MavHeader,
1603 data: &M,
1604) -> Result<usize, MessageWriteError> {
1605 if data.message_id() > u8::MAX.into() {
1606 return Err(MessageWriteError::MAVLink2Only);
1607 }
1608 let mut message_raw = MAVLinkV1MessageRaw::new();
1609 message_raw.serialize_message(header, data);
1610
1611 let payload_length: usize = message_raw.payload_length().into();
1612 let len = consts::STX_SIZE + consts::v1::HEADER_SIZE + payload_length + consts::CHECKSUM_SIZE;
1613
1614 w.write_all(&message_raw.0[..len]).await?;
1615
1616 Ok(len)
1617}
1618
1619#[cfg(all(feature = "embedded", not(feature = "std")))]
1624pub async fn write_v1_msg_async<M: Message>(
1625 w: &mut impl embedded_io_async::Write,
1626 header: MavHeader,
1627 data: &M,
1628) -> Result<usize, MessageWriteError> {
1629 if data.message_id() > u8::MAX.into() {
1630 return Err(MessageWriteError::MAVLink2Only);
1631 }
1632 let mut message_raw = MAVLinkV1MessageRaw::new();
1633 message_raw.serialize_message(header, data);
1634
1635 let payload_length: usize = message_raw.payload_length().into();
1636 let len = consts::STX_SIZE + consts::v1::HEADER_SIZE + payload_length + consts::CHECKSUM_SIZE;
1637
1638 w.write_all(&message_raw.0[..len])
1639 .await
1640 .map_err(|_| MessageWriteError::Io)?;
1641
1642 Ok(len)
1643}