rm_frame/lib.rs
1//! A lightweight binary framing protocol library.
2//!
3//! This crate provides a minimal, allocation-free framing layer
4//! designed for embedded and resource-constrained environments.
5//! It focuses on deterministic binary encoding, checksum validation,
6//! and zero-copy decoding.
7//!
8//! # Architecture Overview
9//!
10//! - **[`Validator`]**
11//! Defines CRC algorithms used to validate frames.
12//!
13//! - **[`DjiValidator`]**
14//! A concrete validator using DJI-compatible CRC8 and CRC16.
15//!
16//! - **[`Marshaler`]**
17//! Describes how a typed payload is serialized into bytes and
18//! deserialized from raw payload data.
19//!
20//! - **[`Messager`]**
21//! Implements frame packing and unpacking, combining framing,
22//! validation, and payload marshaling.
23//!
24//! - **[`RawFrame`]**
25//! A validated, zero-copy view of a decoded frame.
26//!
27//! - **[`RemoteControl`]** (optional)
28//! A helper for encoding and decoding remote control messages.
29//!
30//! # Typical Usage
31//!
32//! 1. Implement [`Marshaler`] for your message types
33//! 2. Create a [`Messager`] with a chosen [`Validator`]
34//! 3. Use [`pack`](Messager::pack) to encode frames
35//! 4. Use [`unpack`](Messager::unpack) to decode frames into [`RawFrame`]
36//! 5. Use [`Marshaler`] to convert [`RawFrame`] payloads to typed messages
37//! 6. Optionally, use [`RemoteControl`] for handling remote control data
38//!
39//! # Example
40//!
41//! ```rust
42//! # use rm_frame::{Marshaler, MarshalerError, Messager, DjiValidator};
43//! # type Error = Box<dyn core::error::Error>;
44//! // Define a custom message type and implement Marshaler for it
45//! struct MyMessage {
46//! value: u32,
47//! }
48//!
49//! impl Marshaler for MyMessage {
50//! const CMD_ID: u16 = 0x1234;
51//! const PAYLOAD_SIZE: u16 = 4;
52//!
53//! // If called manually, the caller must ensure that the destination
54//! // buffer is large enough to hold the serialized payload.
55//! fn marshal(&self, buf: &mut [u8]) -> Result<usize, MarshalerError> {
56//! buf[..4].copy_from_slice(&self.value.to_le_bytes());
57//! Ok(4)
58//! }
59//!
60//! fn unmarshal(buf: &[u8]) -> Result<Self, MarshalerError> {
61//! Ok(Self {
62//! value: u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]),
63//! })
64//! }
65//! }
66//!
67//! # fn main() -> Result<(), Error> {
68//! let mut buffer = [0u8; 64];
69//! let mut msger = Messager::<DjiValidator>::new( /* seq: */ 0 );
70//!
71//! let msg = MyMessage { value: 38 };
72//!
73//! let packed_len = msger.pack(&msg, &mut buffer)?; // seq++
74//! println!("Packed Frame Length: {}", packed_len);
75//!
76//! let (decoded_msg, consumed): (MyMessage, _) = msger.unmarshal(&buffer)?;
77//! assert_eq!(consumed, packed_len);
78//! assert_eq!(decoded_msg.value, 38);
79//!
80//! let msg = MyMessage { value: 42 };
81//!
82//! let packed_len = msger.pack(&msg, &mut buffer)?; // seq++
83//! println!("Packed Frame Length: {}", packed_len);
84//!
85//! let (raw_frame, consumed) = msger.unpack(&buffer)?;
86//! assert_eq!(consumed, packed_len);
87//! assert_eq!(raw_frame.cmd_id(), MyMessage::CMD_ID);
88//! assert_eq!(raw_frame.sequence(), 1);
89//!
90//! let decoded_msg = MyMessage::unmarshal(raw_frame.payload())?;
91//! assert_eq!(decoded_msg.value, 42);
92//! # Ok(())
93//! # }
94//! ```
95//!
96//! > [`RemoteControl`]'s Example See [README.md][README] for details.
97//!
98//! ---
99//!
100//! # Frame Layout
101//!
102//! ```text
103//! +--------+--------+--------+--------+--------+---------+--------+
104//! | SOF | LEN | SEQ | CRC8 | CMD_ID | DATA | CRC16 |
105//! +--------+--------+--------+--------+--------+---------+--------+
106//! | 1 byte | 2 byte | 1 byte | 1 byte | 2 byte | N bytes | 2 byte |
107//! +--------+--------+--------+--------+--------+---------+--------+
108//! ```
109//!
110//! # Protocol Source
111//!
112//! See RoboMaster Resources Hub for official documentation and protocol details:
113//! [RMU Communication Protocol](https://bbs.robomaster.com/wiki/20204847/811363)
114//!
115//! # Features
116//!
117//! - `defmt`: Derives `defmt::Format` on error types and
118//! key enums for embedded structured logging.
119//! - `remote`: Adds support for encoding and decoding remote control messages,
120//! including switch states and joystick positions.
121//!
122//! ---
123//!
124//! # License
125//!
126//! This crate is licensed under the MIT License or the Apache License (Version 2.0).
127//! See `LICENSE-MIT` and `LICENSE-APACHE` files in the repository for details.
128//!
129//! [README]: https://github.com/Salfa-04/rm-proto
130//!
131
132#![cfg_attr(not(test), no_std)]
133#![cfg_attr(docsrs, feature(doc_cfg))]
134#![warn(rustdoc::broken_intra_doc_links)]
135#![warn(rustdoc::unescaped_backticks)]
136#![warn(rustdoc::invalid_html_tags)]
137#![warn(missing_docs)]
138
139pub use crc8_dji::calculate as calc_dji8;
140pub use crc16_dji::calculate as calc_dji16;
141pub use error::{MarshalerError, PackError, UnPackError};
142pub use frame::{Messager, RawFrame};
143pub use marshaler::{ImplCommandMsg, ImplMarshal, ImplUnMarshal, Marshaler};
144pub use validator::{DjiValidator, Validator};
145
146#[cfg(feature = "remote")]
147pub use remote::{RemoteControl, Switch};
148
149mod crc16_dji;
150mod crc8_dji;
151mod error;
152mod frame;
153mod marshaler;
154mod validator;
155
156#[cfg(feature = "remote")]
157mod remote;
158
159#[cfg(test)]
160mod tests;