1use crate::constants;
2use crate::error::CrsfParsingError;
3use crate::parser::RawCrsfPacket;
4use crc;
5
6mod airspeed;
7mod attitude;
8mod baro_altitude;
9mod battery;
10mod commands;
11mod device_information;
12mod device_ping;
13mod esp_now;
14mod flight_mode;
15mod game;
16mod gps;
17mod gps_extended;
18mod gps_time;
19mod heartbeat;
20mod link_statistics;
21mod link_statistics_rx;
22mod link_statistics_tx;
23mod logging;
24mod mavlink_envelope;
25mod mavlink_fc;
26mod mavlink_sensor;
27mod rc_channels_packed;
28mod remote;
29mod rpm;
30mod temp;
31mod vario;
32mod voltages;
33mod vtx_telemetry;
34
35pub use airspeed::AirSpeed;
36pub use attitude::Attitude;
37pub use baro_altitude::BaroAltitude;
38pub use battery::Battery;
39pub use commands::DirectCommands;
40pub use device_information::DeviceInformation;
41pub use device_ping::DevicePing;
42pub use esp_now::EspNow;
43pub use flight_mode::FlightMode;
44pub use game::Game;
45pub use gps::Gps;
46pub use gps_extended::GpsExtended;
47pub use gps_time::GpsTime;
48pub use heartbeat::Heartbeat;
49pub use link_statistics::LinkStatistics;
50pub use link_statistics_rx::LinkStatisticsRx;
51pub use link_statistics_tx::LinkStatisticsTx;
52pub use logging::Logging;
53pub use mavlink_envelope::MavlinkEnvelope;
54pub use mavlink_fc::MavLinkFc;
55pub use mavlink_sensor::MavLinkSensor;
56pub use rc_channels_packed::RcChannelsPacked;
57pub use remote::Remote;
58pub use rpm::Rpm;
59pub use temp::Temp;
60pub use vario::VariometerSensor;
61pub use voltages::Voltages;
62pub use vtx_telemetry::VtxTelemetry;
63
64use num_enum::TryFromPrimitive;
65
66pub trait CrsfPacket: Sized {
68 const PACKET_TYPE: PacketType;
70
71 const MIN_PAYLOAD_SIZE: usize;
74
75 fn from_bytes(data: &[u8]) -> Result<Self, CrsfParsingError>;
78 fn to_bytes(&self, buffer: &mut [u8]) -> Result<usize, CrsfParsingError>;
79
80 fn validate_buffer_size(&self, buffer: &[u8]) -> Result<(), CrsfParsingError> {
81 if buffer.len() < Self::MIN_PAYLOAD_SIZE {
82 return Err(CrsfParsingError::BufferOverflow);
83 }
84 Ok(())
85 }
86}
87
88#[derive(Clone, Debug, PartialEq)]
89#[cfg_attr(feature = "defmt", derive(defmt::Format))]
90pub enum Packet {
91 LinkStatistics(LinkStatistics),
92 LinkStatisticsRx(LinkStatisticsRx),
93 LinkStatisticsTx(LinkStatisticsTx),
94 RCChannels(RcChannelsPacked),
95 Gps(Gps),
96 GpsTime(GpsTime),
97 GpsExtended(GpsExtended),
98 Vario(VariometerSensor),
99 Battery(Battery),
100 AirSpeed(AirSpeed),
101 BaroAltitude(BaroAltitude),
102 Rpm(Rpm),
103 Temp(Temp),
104 Voltages(Voltages),
105 VtxTelemetry(VtxTelemetry),
106 FlightMode(FlightMode),
107 Heartbeat(Heartbeat),
108 EspNow(EspNow),
109 MavlinkEnvelope(MavlinkEnvelope),
110 MavLinkFc(MavLinkFc),
111 MavLinkSensor(MavLinkSensor),
112 Remote(Remote),
113 Attitude(Attitude),
114 DeviceInformation(DeviceInformation),
115 DevicePing(DevicePing),
116 Game(Game),
117 NotImlemented(PacketType, usize),
118 Commands(DirectCommands),
119 Logging(Logging),
120}
121
122impl Packet {
123 pub fn parse(raw_packet: &RawCrsfPacket<'_>) -> Result<Packet, CrsfParsingError> {
124 let packet_type = PacketType::try_from_primitive(raw_packet.raw_packet_type())
125 .map_err(|_| CrsfParsingError::UnexpectedPacketType(raw_packet.raw_packet_type()))?;
126
127 let data = raw_packet.payload();
128 match packet_type {
129 LinkStatistics::PACKET_TYPE => {
130 Ok(Self::LinkStatistics(LinkStatistics::from_bytes(data)?))
131 }
132 LinkStatisticsTx::PACKET_TYPE => {
133 Ok(Self::LinkStatisticsTx(LinkStatisticsTx::from_bytes(data)?))
134 }
135 LinkStatisticsRx::PACKET_TYPE => {
136 Ok(Self::LinkStatisticsRx(LinkStatisticsRx::from_bytes(data)?))
137 }
138 RcChannelsPacked::PACKET_TYPE => {
139 Ok(Self::RCChannels(RcChannelsPacked::from_bytes(data)?))
140 }
141 Gps::PACKET_TYPE => Ok(Self::Gps(Gps::from_bytes(data)?)),
142 GpsTime::PACKET_TYPE => Ok(Self::GpsTime(GpsTime::from_bytes(data)?)),
143 GpsExtended::PACKET_TYPE => Ok(Self::GpsExtended(GpsExtended::from_bytes(data)?)),
144 AirSpeed::PACKET_TYPE => Ok(Self::AirSpeed(AirSpeed::from_bytes(data)?)),
145 BaroAltitude::PACKET_TYPE => Ok(Self::BaroAltitude(BaroAltitude::from_bytes(data)?)),
146 Battery::PACKET_TYPE => Ok(Self::Battery(Battery::from_bytes(data)?)),
147 FlightMode::PACKET_TYPE => Ok(Self::FlightMode(FlightMode::from_bytes(data)?)),
148 Rpm::PACKET_TYPE => Ok(Self::Rpm(Rpm::from_bytes(data)?)),
149 Temp::PACKET_TYPE => Ok(Self::Temp(Temp::from_bytes(data)?)),
150 Voltages::PACKET_TYPE => Ok(Self::Voltages(Voltages::from_bytes(data)?)),
151 VtxTelemetry::PACKET_TYPE => Ok(Self::VtxTelemetry(VtxTelemetry::from_bytes(data)?)),
152 VariometerSensor::PACKET_TYPE => Ok(Self::Vario(VariometerSensor::from_bytes(data)?)),
153 EspNow::PACKET_TYPE => Ok(Self::EspNow(EspNow::from_bytes(data)?)),
154 Heartbeat::PACKET_TYPE => Ok(Self::Heartbeat(Heartbeat::from_bytes(data)?)),
155 MavLinkFc::PACKET_TYPE => Ok(Self::MavLinkFc(MavLinkFc::from_bytes(data)?)),
156 MavLinkSensor::PACKET_TYPE => Ok(Self::MavLinkSensor(MavLinkSensor::from_bytes(data)?)),
157 Remote::PACKET_TYPE => Ok(Self::Remote(Remote::from_bytes(data)?)),
158 Attitude::PACKET_TYPE => Ok(Self::Attitude(Attitude::from_bytes(data)?)),
159 DevicePing::PACKET_TYPE => Ok(Self::DevicePing(DevicePing::from_bytes(data)?)),
160 Game::PACKET_TYPE => Ok(Self::Game(Game::from_bytes(data)?)),
161 DeviceInformation::PACKET_TYPE => Ok(Self::DeviceInformation(
162 DeviceInformation::from_bytes(data)?,
163 )),
164
165 MavlinkEnvelope::PACKET_TYPE => {
166 Ok(Self::MavlinkEnvelope(MavlinkEnvelope::from_bytes(data)?))
167 }
168 DirectCommands::PACKET_TYPE => Ok(Self::Commands(DirectCommands::from_bytes(data)?)),
169 Logging::PACKET_TYPE => Ok(Self::Logging(Logging::from_bytes(data)?)),
170 _ => Ok(Packet::NotImlemented(
171 packet_type,
172 raw_packet.payload().len(),
173 )),
174 }
175 }
176}
177
178#[non_exhaustive]
179#[derive(Clone, Copy, Debug, PartialEq, Eq, num_enum::TryFromPrimitive)]
180#[cfg_attr(feature = "defmt", derive(defmt::Format))]
181#[repr(u8)]
182pub enum PacketType {
183 Gps = 0x02,
184 GpsTime = 0x03,
185 GpsExtended = 0x06,
186 Vario = 0x07,
187 BatterySensor = 0x08,
188 BaroAltitude = 0x09,
189 AirSpeed = 0x0A,
190 Rpm = 0x0C,
191 Temp = 0x0D,
192 Voltages = 0x0E,
193 VtxTelemetry = 0x10,
194 Heartbeat = 0x0B,
195 LinkStatistics = 0x14,
196 RcChannelsPacked = 0x16,
197 SubsetRcChannelsPacked = 0x17,
198 LinkStatisticsRx = 0x1C,
199 LinkStatisticsTx = 0x1D,
200 Attitude = 0x1E,
201 MavLinkFc = 0x1F,
202 FlightMode = 0x21,
203 EspNow = 0x22,
204 DevicePing = 0x28,
205 DeviceInfo = 0x29,
206 ParameterSettingsEntry = 0x2B,
207 ParameterRead = 0x2C,
208 ParameterWrite = 0x2D,
209 ElrsStatus = 0x2E,
210 Command = 0x32,
211 Logging = 0x34,
212 RadioId = 0x3A,
213 KissRequest = 0x78,
214 KissResponse = 0x79,
215 MspRequest = 0x7A,
216 MspResponse = 0x7B,
217 MspWrite = 0x7C,
218 ArdupilotResponse = 0x80,
219 MavlinkEnvelope = 0xAA,
220 MavLinkSensor = 0xAC,
221 Game = 0x3C,
222}
223
224impl PacketType {
225 pub fn is_extended(self) -> bool {
226 self as u8 >= 0x28
227 }
228}
229
230#[non_exhaustive]
232#[derive(Clone, Copy, Debug, PartialEq, Eq, TryFromPrimitive)]
233#[cfg_attr(feature = "defmt", derive(defmt::Format))]
234#[repr(u8)]
235pub enum PacketAddress {
236 Broadcast = 0x00,
237 Cloud = 0x0E,
238 Usb = 0x10,
239 Bluetooth = 0x12,
240 WifiReceiver = 0x13,
241 VideoReceiver = 0x14,
242 TbsCorePnpPro = 0x80,
243 Esc1 = 0x90,
244 Esc2 = 0x91,
245 Esc3 = 0x92,
246 Esc4 = 0x93,
247 Esc5 = 0x94,
248 Esc6 = 0x95,
249 Esc7 = 0x96,
250 Esc8 = 0x97,
251 CurrentSensor = 0xC0,
252 Gps = 0xC2,
253 TbsBlackbox = 0xC4,
254 FlightController = 0xC8,
255 RaceTag = 0xCC,
256 VTX = 0xCE,
257 Handset = 0xEA,
258 Receiver = 0xEC,
259 Transmitter = 0xEE,
260}
261
262pub fn write_packet_to_buffer<T: CrsfPacket>(
263 buffer: &mut [u8],
264 dest: PacketAddress,
265 packet: &T,
266) -> Result<usize, CrsfParsingError> {
267 const MAX_PAYLOAD_SIZE: usize = constants::CRSF_MAX_PACKET_SIZE - 4;
268 static CRC8_DVB_S2: crc::Crc<u8> = crc::Crc::<u8>::new(&crc::CRC_8_DVB_S2);
269 let mut payload_buf = [0u8; MAX_PAYLOAD_SIZE];
270
271 let payload_size = packet.to_bytes(&mut payload_buf)?;
272
273 let total_frame_size = payload_size + 4;
274 if buffer.len() < total_frame_size {
275 return Err(CrsfParsingError::BufferOverflow);
276 }
277
278 let length_byte = (payload_size + 2) as u8;
280
281 buffer[0] = dest as u8;
282 buffer[1] = length_byte;
283 buffer[2] = T::PACKET_TYPE as u8;
284 buffer[3..3 + payload_size].copy_from_slice(&payload_buf[..payload_size]);
285
286 let crc_payload = &buffer[2..3 + payload_size];
288 let mut digest = CRC8_DVB_S2.digest();
289 digest.update(crc_payload);
290 let calculated_crc = digest.finalize();
291
292 buffer[3 + payload_size] = calculated_crc;
293
294 Ok(total_frame_size)
295}
296
297#[cfg(test)]
298mod tests {
299 use super::*;
300
301 struct MockPacket {
302 payload: [u8; 2],
303 }
304
305 impl CrsfPacket for MockPacket {
306 const PACKET_TYPE: PacketType = PacketType::Command;
307 const MIN_PAYLOAD_SIZE: usize = 2;
308
309 fn from_bytes(data: &[u8]) -> Result<Self, CrsfParsingError> {
310 Ok(Self {
312 payload: [data[0], data[1]],
313 })
314 }
315
316 fn to_bytes(&self, buffer: &mut [u8]) -> Result<usize, CrsfParsingError> {
317 buffer[..2].copy_from_slice(&self.payload);
318 Ok(2)
319 }
320 }
321
322 #[test]
323 fn test_write_packet_to_buffer() {
324 let mut buffer = [0u8; 64];
325 let packet = MockPacket {
326 payload: [0xAA, 0xBB],
327 };
328 let dest = PacketAddress::FlightController;
329
330 let result = write_packet_to_buffer(&mut buffer, dest, &packet);
331
332 assert!(result.is_ok());
333 let bytes_written = result.unwrap();
334 assert_eq!(bytes_written, 6);
335
336 let expected_packet: [u8; 6] = [
340 dest as u8,
341 4,
342 MockPacket::PACKET_TYPE as u8,
343 0xAA,
344 0xBB,
345 0x32,
346 ];
347 assert_eq!(&buffer[..bytes_written], &expected_packet[..]);
348 }
349
350 #[test]
351 fn test_write_packet_to_buffer_overflow() {
352 let mut buffer = [0u8; 5]; let packet = MockPacket {
354 payload: [0xAA, 0xBB],
355 };
356 let dest = PacketAddress::FlightController;
357
358 let result = write_packet_to_buffer(&mut buffer, dest, &packet);
359
360 assert!(result.is_err());
361 assert_eq!(result.unwrap_err(), CrsfParsingError::BufferOverflow);
362 }
363}