Skip to main content

cu_msp_lib/
structs.rs

1//! MSP structures
2
3use packed_struct::derive::{PackedStruct, PrimitiveEnum};
4use serde::{Deserialize, Serialize};
5
6use crate::MspPacketDirection::{FromFlightController, ToFlightController};
7use crate::commands::MspCommandCode;
8use crate::{MSP_MAX_PAYLOAD_LEN, MspPacket, MspPacketData, MspPacketDataBuffer};
9
10use alloc::{borrow::ToOwned, string::String, vec::Vec};
11
12#[cfg(feature = "bincode")]
13use bincode::{Decode, Encode};
14use packed_struct::types::bits::ByteArray;
15use packed_struct::{PackedStruct, PackingError, PrimitiveEnum};
16
17#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
18#[cfg_attr(feature = "bincode", bincode(describe))]
19#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))]
20#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
21pub struct MspApiVersion {
22    pub protocol_version: u8,
23    pub api_version_major: u8,
24    pub api_version_minor: u8,
25}
26
27#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
28#[cfg_attr(feature = "bincode", bincode(describe))]
29#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
30pub struct MspFlightControllerVariant {
31    pub identifier: [u8; 4],
32}
33
34#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
35#[cfg_attr(feature = "bincode", bincode(describe))]
36#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))]
37#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
38pub struct MspFlightControllerVersion {
39    pub major: u8,
40    pub minor: u8,
41    pub patch: u8,
42}
43
44#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
45#[cfg_attr(feature = "bincode", bincode(describe))]
46#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
47#[packed_struct(endian = "lsb")]
48pub struct MspBoardInfo {
49    pub board_id: [u8; 4],
50    pub hardware_revision: u16,
51    pub fc_type: u8,
52}
53
54#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
55#[cfg_attr(feature = "bincode", bincode(describe))]
56#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
57pub struct MspBuildInfo {
58    pub date_str: [u8; 11],
59    pub time_str: [u8; 8],
60    pub git_str: [u8; 7],
61}
62
63#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
64#[cfg_attr(feature = "bincode", bincode(describe))]
65#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
66pub struct MspUniqueId {
67    pub uid: [u8; 12],
68}
69
70#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
71#[cfg_attr(feature = "bincode", bincode(describe))]
72#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
73#[packed_struct(bytes = "1", endian = "lsb", bit_numbering = "msb0")]
74pub struct MspAvailableSensors {
75    #[packed_field(bits = "2")]
76    pub sonar: bool,
77    #[packed_field(bits = "4")]
78    pub gps: bool,
79    #[packed_field(bits = "5")]
80    pub mag: bool,
81    #[packed_field(bits = "6")]
82    pub baro: bool,
83    #[packed_field(bits = "7")]
84    pub acc: bool,
85}
86
87#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
88#[cfg_attr(feature = "bincode", bincode(describe))]
89#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))]
90#[derive(Serialize, Deserialize, Debug, Copy, Clone, Default, PartialEq, Eq)]
91pub struct MspStatusSensors {
92    pub acc: bool,
93    pub baro: bool,
94    pub mag: bool,
95    pub gps: bool,
96    pub rangefinder: bool,
97    pub gyro: bool,
98    pub optical_flow: bool,
99}
100
101impl MspStatusSensors {
102    pub fn from_bits(bits: u16) -> Self {
103        Self {
104            acc: bits & (1 << 0) != 0,
105            baro: bits & (1 << 1) != 0,
106            mag: bits & (1 << 2) != 0,
107            gps: bits & (1 << 3) != 0,
108            rangefinder: bits & (1 << 4) != 0,
109            gyro: bits & (1 << 5) != 0,
110            optical_flow: bits & (1 << 6) != 0,
111        }
112    }
113
114    pub fn to_bits(self) -> u16 {
115        (self.acc as u16)
116            | (self.baro as u16) << 1
117            | (self.mag as u16) << 2
118            | (self.gps as u16) << 3
119            | (self.rangefinder as u16) << 4
120            | (self.gyro as u16) << 5
121            | (self.optical_flow as u16) << 6
122    }
123}
124
125impl From<u16> for MspStatusSensors {
126    fn from(bits: u16) -> Self {
127        Self::from_bits(bits)
128    }
129}
130
131impl From<MspStatusSensors> for u16 {
132    fn from(value: MspStatusSensors) -> Self {
133        value.to_bits()
134    }
135}
136
137#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
138#[cfg_attr(feature = "bincode", bincode(describe))]
139#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))]
140#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
141pub struct MspStatus {
142    pub cycle_time: u16,
143    pub i2c_errors: u16,
144    pub sensors: MspStatusSensors,
145    pub flight_mode_flags: u32,
146    pub current_pid_profile_index: u8,
147    pub average_system_load_percent: u16,
148    pub gyro_cycle_time: u16,
149    pub extra_flight_mode_flags: Vec<u8>,
150    pub arming_disable_flags_count: u8,
151    pub arming_disable_flags: u32,
152    pub config_state_flags: u8,
153    pub core_temp_celsius: u16,
154    pub control_rate_profile_count: u8,
155}
156
157#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
158#[cfg_attr(feature = "bincode", bincode(describe))]
159#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))]
160#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq)]
161pub struct MspStatusEx {
162    pub cycle_time: u16,
163    pub i2c_errors: u16,
164    pub sensors: MspStatusSensors,
165    pub flight_mode_flags: u32,
166    pub current_pid_profile_index: u8,
167    pub average_system_load_percent: u16,
168    pub max_profile_count: u8,
169    pub current_control_rate_profile_index: u8,
170    pub extra_flight_mode_flags: Vec<u8>,
171    pub arming_disable_flags_count: u8,
172    pub arming_disable_flags: u32,
173    pub config_state_flags: u8,
174    pub core_temp_celsius: u16,
175    pub control_rate_profile_count: u8,
176}
177
178impl MspStatus {
179    pub fn from_bytes(data: &[u8]) -> Result<Self, PackingError> {
180        let mut offset = 0;
181        let cycle_time = read_u16(data, &mut offset)?;
182        let i2c_errors = read_u16(data, &mut offset)?;
183        let sensors = MspStatusSensors::from(read_u16(data, &mut offset)?);
184        let flight_mode_flags = read_u32(data, &mut offset)?;
185        let current_pid_profile_index = read_u8(data, &mut offset)?;
186        let average_system_load_percent = read_u16(data, &mut offset)?;
187        let gyro_cycle_time = read_u16(data, &mut offset)?;
188        let extra_flight_mode_flags_len = read_u8(data, &mut offset)? as usize;
189        let extra_flight_mode_flags = read_bytes(data, &mut offset, extra_flight_mode_flags_len)?;
190        let arming_disable_flags_count = read_u8(data, &mut offset)?;
191        let arming_disable_flags = read_u32(data, &mut offset)?;
192        let config_state_flags = read_u8(data, &mut offset)?;
193        let core_temp_celsius = read_u16(data, &mut offset)?;
194        let control_rate_profile_count = read_u8(data, &mut offset)?;
195
196        Ok(Self {
197            cycle_time,
198            i2c_errors,
199            sensors,
200            flight_mode_flags,
201            current_pid_profile_index,
202            average_system_load_percent,
203            gyro_cycle_time,
204            extra_flight_mode_flags,
205            arming_disable_flags_count,
206            arming_disable_flags,
207            config_state_flags,
208            core_temp_celsius,
209            control_rate_profile_count,
210        })
211    }
212
213    pub fn to_packet_data(&self) -> Result<MspPacketData, PackingError> {
214        if self.extra_flight_mode_flags.len() > 15 {
215            return Err(PackingError::InvalidValue);
216        }
217        let mut data = MspPacketDataBuffer::new();
218        extend_payload(&mut data, &self.cycle_time.to_le_bytes())?;
219        extend_payload(&mut data, &self.i2c_errors.to_le_bytes())?;
220        extend_payload(&mut data, &u16::from(self.sensors).to_le_bytes())?;
221        extend_payload(&mut data, &self.flight_mode_flags.to_le_bytes())?;
222        push_payload(&mut data, self.current_pid_profile_index)?;
223        extend_payload(&mut data, &self.average_system_load_percent.to_le_bytes())?;
224        extend_payload(&mut data, &self.gyro_cycle_time.to_le_bytes())?;
225        push_payload(&mut data, self.extra_flight_mode_flags.len() as u8)?;
226        extend_payload(&mut data, &self.extra_flight_mode_flags)?;
227        push_payload(&mut data, self.arming_disable_flags_count)?;
228        extend_payload(&mut data, &self.arming_disable_flags.to_le_bytes())?;
229        push_payload(&mut data, self.config_state_flags)?;
230        extend_payload(&mut data, &self.core_temp_celsius.to_le_bytes())?;
231        push_payload(&mut data, self.control_rate_profile_count)?;
232
233        Ok(MspPacketData(data))
234    }
235}
236
237impl MspStatusEx {
238    pub fn from_bytes(data: &[u8]) -> Result<Self, PackingError> {
239        let mut offset = 0;
240        let cycle_time = read_u16(data, &mut offset)?;
241        let i2c_errors = read_u16(data, &mut offset)?;
242        let sensors = MspStatusSensors::from(read_u16(data, &mut offset)?);
243        let flight_mode_flags = read_u32(data, &mut offset)?;
244        let current_pid_profile_index = read_u8(data, &mut offset)?;
245        let average_system_load_percent = read_u16(data, &mut offset)?;
246        let max_profile_count = read_u8(data, &mut offset)?;
247        let current_control_rate_profile_index = read_u8(data, &mut offset)?;
248        let extra_flight_mode_flags_len = read_u8(data, &mut offset)? as usize;
249        let extra_flight_mode_flags = read_bytes(data, &mut offset, extra_flight_mode_flags_len)?;
250        let arming_disable_flags_count = read_u8(data, &mut offset)?;
251        let arming_disable_flags = read_u32(data, &mut offset)?;
252        let config_state_flags = read_u8(data, &mut offset)?;
253        let core_temp_celsius = read_u16(data, &mut offset)?;
254        let control_rate_profile_count = read_u8(data, &mut offset)?;
255
256        Ok(Self {
257            cycle_time,
258            i2c_errors,
259            sensors,
260            flight_mode_flags,
261            current_pid_profile_index,
262            average_system_load_percent,
263            max_profile_count,
264            current_control_rate_profile_index,
265            extra_flight_mode_flags,
266            arming_disable_flags_count,
267            arming_disable_flags,
268            config_state_flags,
269            core_temp_celsius,
270            control_rate_profile_count,
271        })
272    }
273
274    pub fn to_packet_data(&self) -> Result<MspPacketData, PackingError> {
275        if self.extra_flight_mode_flags.len() > 15 {
276            return Err(PackingError::InvalidValue);
277        }
278        let mut data = MspPacketDataBuffer::new();
279        extend_payload(&mut data, &self.cycle_time.to_le_bytes())?;
280        extend_payload(&mut data, &self.i2c_errors.to_le_bytes())?;
281        extend_payload(&mut data, &u16::from(self.sensors).to_le_bytes())?;
282        extend_payload(&mut data, &self.flight_mode_flags.to_le_bytes())?;
283        push_payload(&mut data, self.current_pid_profile_index)?;
284        extend_payload(&mut data, &self.average_system_load_percent.to_le_bytes())?;
285        push_payload(&mut data, self.max_profile_count)?;
286        push_payload(&mut data, self.current_control_rate_profile_index)?;
287        push_payload(&mut data, self.extra_flight_mode_flags.len() as u8)?;
288        extend_payload(&mut data, &self.extra_flight_mode_flags)?;
289        push_payload(&mut data, self.arming_disable_flags_count)?;
290        extend_payload(&mut data, &self.arming_disable_flags.to_le_bytes())?;
291        push_payload(&mut data, self.config_state_flags)?;
292        extend_payload(&mut data, &self.core_temp_celsius.to_le_bytes())?;
293        push_payload(&mut data, self.control_rate_profile_count)?;
294
295        Ok(MspPacketData(data))
296    }
297}
298
299fn push_payload(data: &mut MspPacketDataBuffer, value: u8) -> Result<(), PackingError> {
300    let next_len = data.len() + 1;
301    if next_len > MSP_MAX_PAYLOAD_LEN {
302        return Err(PackingError::BufferSizeMismatch {
303            expected: MSP_MAX_PAYLOAD_LEN,
304            actual: next_len,
305        });
306    }
307    data.push(value)
308        .map_err(|_| PackingError::BufferSizeMismatch {
309            expected: MSP_MAX_PAYLOAD_LEN,
310            actual: next_len,
311        })?;
312    Ok(())
313}
314
315fn extend_payload(data: &mut MspPacketDataBuffer, bytes: &[u8]) -> Result<(), PackingError> {
316    let next_len = data.len() + bytes.len();
317    if next_len > MSP_MAX_PAYLOAD_LEN {
318        return Err(PackingError::BufferSizeMismatch {
319            expected: MSP_MAX_PAYLOAD_LEN,
320            actual: next_len,
321        });
322    }
323    data.extend_from_slice(bytes)
324        .map_err(|_| PackingError::BufferSizeMismatch {
325            expected: MSP_MAX_PAYLOAD_LEN,
326            actual: next_len,
327        })?;
328    Ok(())
329}
330
331fn read_u8(data: &[u8], offset: &mut usize) -> Result<u8, PackingError> {
332    if *offset + 1 > data.len() {
333        return Err(PackingError::BufferSizeMismatch {
334            expected: *offset + 1,
335            actual: data.len(),
336        });
337    }
338    let value = data[*offset];
339    *offset += 1;
340    Ok(value)
341}
342
343fn read_u16(data: &[u8], offset: &mut usize) -> Result<u16, PackingError> {
344    if *offset + 2 > data.len() {
345        return Err(PackingError::BufferSizeMismatch {
346            expected: *offset + 2,
347            actual: data.len(),
348        });
349    }
350    let value = u16::from_le_bytes([data[*offset], data[*offset + 1]]);
351    *offset += 2;
352    Ok(value)
353}
354
355fn read_u32(data: &[u8], offset: &mut usize) -> Result<u32, PackingError> {
356    if *offset + 4 > data.len() {
357        return Err(PackingError::BufferSizeMismatch {
358            expected: *offset + 4,
359            actual: data.len(),
360        });
361    }
362    let value = u32::from_le_bytes([
363        data[*offset],
364        data[*offset + 1],
365        data[*offset + 2],
366        data[*offset + 3],
367    ]);
368    *offset += 4;
369    Ok(value)
370}
371
372fn read_bytes(data: &[u8], offset: &mut usize, len: usize) -> Result<Vec<u8>, PackingError> {
373    if *offset + len > data.len() {
374        return Err(PackingError::BufferSizeMismatch {
375            expected: *offset + len,
376            actual: data.len(),
377        });
378    }
379    let bytes = data[*offset..*offset + len].to_vec();
380    *offset += len;
381    Ok(bytes)
382}
383
384#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
385#[cfg_attr(feature = "bincode", bincode(describe))]
386#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
387#[packed_struct(endian = "lsb")]
388pub struct MspBfConfig {
389    pub mixer_configuration: u8,
390    pub features: u32,
391    pub serial_rx_provider: u8,
392    pub board_align_roll: i16,
393    pub board_align_pitch: i16,
394    pub board_align_yaw: i16,
395    pub current_scale: i16,
396    pub current_offset: i16,
397}
398
399#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
400#[cfg_attr(feature = "bincode", bincode(describe))]
401#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))]
402#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
403#[packed_struct(endian = "lsb")]
404pub struct MspRawImu {
405    pub acc_x: i16,
406    pub acc_y: i16,
407    pub acc_z: i16,
408    pub gyro_x: i16,
409    pub gyro_y: i16,
410    pub gyro_z: i16,
411    pub mag_x: i16,
412    pub mag_y: i16,
413    pub mag_z: i16,
414}
415
416#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
417#[cfg_attr(feature = "bincode", bincode(describe))]
418#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
419#[packed_struct(bytes = "1", endian = "lsb", bit_numbering = "msb0")]
420pub struct MspDataFlashSummaryReply {
421    #[packed_field(bits = "6")]
422    pub supported: bool,
423    #[packed_field(bits = "7")]
424    pub ready: bool,
425    pub sectors: u32,
426    pub total_size_bytes: u32,
427    pub used_size_bytes: u32,
428}
429
430#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
431#[cfg_attr(feature = "bincode", bincode(describe))]
432#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
433#[packed_struct(bytes = "1", endian = "lsb", bit_numbering = "msb0")]
434pub struct MspDataFlashReply {
435    pub read_address: u32,
436    // pub payload: Vec<u8>, // TODO: packed_struct should support dynamic size too the end
437}
438
439#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
440#[cfg_attr(feature = "bincode", bincode(describe))]
441#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
442#[packed_struct(bytes = "6", endian = "lsb", bit_numbering = "msb0")]
443pub struct MspDataFlashRead {
444    pub read_address: u32,
445    pub read_length: u16,
446}
447
448#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
449#[cfg_attr(feature = "bincode", bincode(describe))]
450#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
451#[packed_struct(endian = "lsb")]
452pub struct MspAccTrim {
453    pub pitch: u16,
454    pub roll: u16,
455}
456
457#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
458#[cfg_attr(feature = "bincode", bincode(describe))]
459#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
460#[packed_struct(endian = "lsb")]
461pub struct MspIdent {
462    pub version: u8,
463    pub mixer_mode: u8,
464    pub protocol_version: u8,
465    pub capability: u32,
466}
467
468#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
469#[cfg_attr(feature = "bincode", bincode(describe))]
470#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
471#[packed_struct(endian = "lsb")]
472pub struct MspMisc {
473    pub rx_mid_rc: u16,
474    pub min_throttle: u16,
475    pub max_throttle: u16,
476    pub min_command: u16,
477    pub failsafe_throttle: u16,
478
479    pub gps_type: u8,
480    pub gps_baudrate: u8,
481    pub gps_sbas_mode: u8,
482
483    pub current_meter_output: u8,
484    pub rssi_channel: u8,
485    pub null1: u8,
486
487    pub compass_mag_declination: u16,
488}
489
490#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
491#[cfg_attr(feature = "bincode", bincode(describe))]
492#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
493#[packed_struct(endian = "lsb")]
494pub struct MspAttitude {
495    pub roll: i16,
496    pub pitch: i16,
497    pub yaw: i16,
498}
499
500#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
501#[cfg_attr(feature = "bincode", bincode(describe))]
502#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
503#[packed_struct(endian = "lsb")]
504pub struct MspAltitude {
505    /// [centimeters]
506    pub altitude: i32,
507    /// variometer [cm/s]
508    pub vario: i16,
509}
510
511#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
512#[cfg_attr(feature = "bincode", bincode(describe))]
513#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))]
514#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
515#[packed_struct(endian = "lsb")]
516pub struct MspSensorRangefinder {
517    pub quality: u8,
518    pub distance_mm: i32,
519}
520
521#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
522#[cfg_attr(feature = "bincode", bincode(describe))]
523#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))]
524#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
525#[packed_struct(endian = "lsb")]
526pub struct MspSensorOpticFlow {
527    pub quality: u8,
528    pub motion_x: i32,
529    pub motion_y: i32,
530}
531
532#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
533#[cfg_attr(feature = "bincode", bincode(describe))]
534#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))]
535#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
536#[packed_struct(endian = "lsb")]
537pub struct MspBatteryConfig {
538    pub vbat_min_cell_voltage: u8,
539    pub vbat_max_cell_voltage: u8,
540    pub vbat_warning_cell_voltage: u8,
541    pub battery_capacity: u16,
542    pub voltage_meter_source: u8,
543    pub current_meter_source: u8,
544    pub vbat_min_cell_voltage_mv: u16,
545    pub vbat_max_cell_voltage_mv: u16,
546    pub vbat_warning_cell_voltage_mv: u16,
547}
548
549#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
550#[cfg_attr(feature = "bincode", bincode(describe))]
551#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))]
552#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
553#[packed_struct(endian = "lsb")]
554pub struct MspVoltageMeterConfig {
555    pub sensor_count: u8,
556    pub subframe_len: u8,
557    pub id: u8,
558    pub sensor_type: u8,
559    pub vbat_scale: u8,
560    pub vbat_res_div_val: u8,
561    pub vbat_res_div_mult: u8,
562}
563
564#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
565#[cfg_attr(feature = "bincode", bincode(describe))]
566#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))]
567#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
568#[packed_struct(endian = "lsb")]
569pub struct MspAnalog {
570    pub battery_voltage: u8,
571    pub mah_drawn: u16,
572    pub rssi: u16,
573    /// Current in 0.01A steps, range is -320A to 320A
574    pub amperage: i16,
575    /// Battery voltage in 0.01V steps
576    pub battery_voltage_mv: u16,
577}
578
579#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
580#[cfg_attr(feature = "bincode", bincode(describe))]
581#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
582#[packed_struct(endian = "lsb")]
583pub struct MspRssiConfig {
584    pub rssi_channel: u8,
585}
586
587#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
588#[cfg_attr(feature = "bincode", bincode(describe))]
589#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))]
590#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
591pub struct MspVoltageMeter {
592    pub id: u8,
593    pub value: u8,
594}
595
596#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
597#[cfg_attr(feature = "bincode", bincode(describe))]
598#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
599#[packed_struct(endian = "lsb")]
600pub struct MspCurrentMeter {
601    pub id: u8,
602    pub mah_drawn: u16,
603    /// In 0.001A steps (mA)
604    pub amperage: u16,
605}
606
607#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
608#[cfg_attr(feature = "bincode", bincode(describe))]
609#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))]
610#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
611#[packed_struct(endian = "lsb")]
612pub struct MspBatteryState {
613    pub battery_cell_count: u8,
614    /// mAh
615    pub battery_capacity: u16,
616
617    pub battery_voltage: u8,
618    pub mah_drawn: u16,
619    /// 0.01A
620    pub amperage: i16,
621
622    pub alerts: u8,
623    /// Battery voltage in 0.01V steps
624    pub battery_voltage_mv: u16,
625}
626
627impl MspBatteryState {
628    pub fn cell_voltage(&self) -> f32 {
629        self.battery_voltage as f32 / (10 * self.battery_cell_count) as f32
630    }
631}
632
633#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
634#[cfg_attr(feature = "bincode", bincode(describe))]
635#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
636#[packed_struct(endian = "lsb")]
637pub struct MspRcTuning {
638    pub rc_rate8: u8,
639    pub rc_expo8: u8,
640
641    pub rate_roll: u8,
642    pub rate_pitch: u8,
643    pub rate_yaw: u8,
644
645    pub dyn_thr_pid: u8,
646    pub thr_mid8: u8,
647    pub thr_expo8: u8,
648    pub tpa_breakpoint: u16,
649    pub rc_yaw_expo8: u8,
650    pub rc_yaw_rate8: u8,
651}
652
653#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
654#[cfg_attr(feature = "bincode", bincode(describe))]
655#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
656#[packed_struct(endian = "lsb")]
657pub struct MspRxConfig {
658    pub serialrx_provider: u8,
659    pub maxcheck: u16,
660    pub midrc: u16,
661    pub mincheck: u16,
662    pub spektrum_sat_bind: u8,
663    pub rx_min_usec: u16,
664    pub rx_max_usec: u16,
665    pub rc_interpolation: u8,
666    pub rc_interpolation_interval: u8,
667    pub air_mode_activate_threshold: u16,
668    pub rx_spi_protocol: u8,
669    pub rx_spi_id: u32,
670    pub rx_spi_rf_channel_count: u8,
671    pub fpv_cam_angle_degrees: u8,
672}
673
674#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
675#[cfg_attr(feature = "bincode", bincode(describe))]
676#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
677#[packed_struct(endian = "lsb")]
678pub struct MspRcChannelValue {
679    pub value: u16,
680}
681
682#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
683#[cfg_attr(feature = "bincode", bincode(describe))]
684#[derive(PrimitiveEnum, Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Default)]
685pub enum MspRcChannel {
686    /// Ailerons
687    #[default]
688    Roll = 0,
689    /// Elevators
690    Pitch = 1,
691    /// Rudder
692    Yaw = 2,
693    Throttle = 3,
694    Aux1 = 4,
695    Aux2 = 5,
696    Aux3 = 6,
697    Aux4 = 7,
698    Aux5 = 8,
699    Aux6 = 9,
700    Aux7 = 10,
701    Aux8 = 11,
702    Aux9 = 12,
703    Aux10 = 13,
704    Aux11 = 14,
705    Aux12 = 15,
706    Aux13 = 16,
707    Aux14 = 17,
708    Aux15 = 18,
709    Aux16 = 19,
710}
711
712#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
713#[cfg_attr(feature = "bincode", bincode(describe))]
714#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
715pub struct MspRcMappedChannel {
716    #[packed_field(size_bits = "8", ty = "enum")]
717    pub channel: MspRcChannel,
718}
719
720#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
721#[cfg_attr(feature = "bincode", bincode(describe))]
722#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
723pub struct MspFeatures {
724    pub features: [bool; 32],
725}
726
727#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
728#[cfg_attr(feature = "bincode", bincode(describe))]
729#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
730#[packed_struct(endian = "lsb")]
731pub struct MspMotor {
732    pub motors: [u16; 8],
733}
734
735#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
736#[cfg_attr(feature = "bincode", bincode(describe))]
737#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
738#[packed_struct(endian = "lsb")]
739pub struct MspMotor3DConfig {
740    pub deadband_3d_low: u16,
741    pub deadband_3d_high: u16,
742    pub neutral_3d: u16,
743}
744
745#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
746#[cfg_attr(feature = "bincode", bincode(describe))]
747#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
748#[packed_struct(endian = "lsb")]
749pub struct MspMotorConfig {
750    pub min_throttle: u16,
751    pub max_throttle: u16,
752    pub min_command: u16,
753}
754
755#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
756#[cfg_attr(feature = "bincode", bincode(describe))]
757#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
758#[packed_struct(endian = "lsb")]
759pub struct MspRcDeadband {
760    pub deadband: u8,
761    pub yaw_deadband: u8,
762    pub alt_hold_deadband: u8,
763    pub deadband_3d_throttle: u16,
764}
765
766#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
767#[cfg_attr(feature = "bincode", bincode(describe))]
768#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
769#[packed_struct(endian = "lsb")]
770pub struct MspSensorAlignment {
771    pub gyro_alignment: u8,
772    pub acc_alignment: u8,
773    pub mag_alignment: u8,
774}
775
776#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
777#[cfg_attr(feature = "bincode", bincode(describe))]
778#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
779#[packed_struct(endian = "lsb")]
780pub struct MspAdvancedConfig {
781    pub gyro_sync_denom: u8,
782    pub pid_process_denom: u8,
783    pub use_unsynced_pwm: u8,
784    pub motor_pwm_protocol: u8,
785    pub motor_pwm_rate: u16,
786    pub digital_idle_offset_percent: u16,
787    pub gyro_use_32khz: u8,
788    pub motor_pwm_inversion: u8,
789}
790
791#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
792#[cfg_attr(feature = "bincode", bincode(describe))]
793#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
794#[packed_struct(endian = "lsb")]
795pub struct MspFilterConfig {
796    pub gyro_soft_lpf_hz: u8,
797    pub dterm_lpf_hz: u16,
798    pub yaw_lpf_hz: u16,
799    pub gyro_soft_notch_hz_1: u16,
800    pub gyro_soft_notch_cutoff_1: u16,
801    pub dterm_notch_hz: u16,
802    pub dterm_notch_cutoff: u16,
803    pub gyro_soft_notch_hz_2: u16,
804    pub gyro_soft_notch_cutoff_2: u16,
805}
806
807#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
808#[cfg_attr(feature = "bincode", bincode(describe))]
809#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
810#[packed_struct(endian = "lsb")]
811pub struct MspPidAdvanced {
812    pub _r1: u16,
813    pub _r2: u16,
814    pub _r3: u16,
815    pub _r4: u8,
816    pub vbat_pid_compensation: u8,
817    pub setpoint_relax_ratio: u8,
818    pub dterm_setpoint_weight: u8,
819    pub _r5: u8,
820    pub _r6: u8,
821    pub _r7: u8,
822    pub rate_accel_limit: u16,
823    pub yaw_rate_accel_limit: u16,
824    pub level_angle_limit: u8,
825    pub level_sensitivity: u8,
826}
827
828#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
829#[cfg_attr(feature = "bincode", bincode(describe))]
830#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
831#[packed_struct(endian = "lsb")]
832pub struct MspSensorConfig {
833    pub acc_hardware: u8,
834    pub baro_hardware: u8,
835    pub mag_hardware: u8,
836}
837
838#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
839#[cfg_attr(feature = "bincode", bincode(describe))]
840#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
841#[packed_struct(endian = "lsb")]
842pub struct MspServos {
843    pub servos: [u16; 8],
844}
845
846#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
847#[cfg_attr(feature = "bincode", bincode(describe))]
848#[derive(PackedStruct, Debug, Copy, Clone, Default)]
849#[packed_struct(bytes = "14", endian = "lsb", bit_numbering = "msb0")]
850pub struct MspServoConfig {
851    pub min: u16,
852    pub max: u16,
853    pub middle: u16,
854    pub rate: i8,
855    pub unused1: u8,
856    pub unused2: u8,
857    pub forward_from_channel: u8, // Deprecated, set to 255 for backward compatibility
858    pub reverse_input: u32, // Deprecated, Input reversing is not required since it can be done on mixer level
859}
860
861#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
862#[cfg_attr(feature = "bincode", bincode(describe))]
863#[derive(PackedStruct, Debug, Copy, Clone, Default)]
864#[packed_struct(bytes = "1", endian = "lsb", bit_numbering = "msb0")]
865pub struct MspSetServoConfig {
866    pub index: u8,
867    #[packed_field(size_bytes = "14")]
868    pub servo_config: MspServoConfig,
869}
870
871#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
872#[cfg_attr(feature = "bincode", bincode(describe))]
873#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
874#[packed_struct(endian = "lsb")]
875pub struct MspMixerConfig {
876    #[packed_field(size_bits = "8", ty = "enum")]
877    pub mixer_mode: MixerMode,
878}
879
880#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
881#[cfg_attr(feature = "bincode", bincode(describe))]
882#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
883#[packed_struct(bytes = "4", endian = "lsb", bit_numbering = "msb0")]
884pub struct MspModeRange {
885    pub box_id: u8,
886    #[packed_field(size_bits = "8", ty = "enum")]
887    pub aux_channel_index: MspRcChannel,
888    pub start_step: u8,
889    pub end_step: u8,
890}
891
892#[cfg_attr(feature = "bincode", derive(Decode, Encode, Default))]
893#[cfg_attr(feature = "bincode", bincode(describe))]
894#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone)]
895#[packed_struct(bytes = "5", endian = "lsb", bit_numbering = "msb0")]
896pub struct MspSetModeRange {
897    pub index: u8,
898    #[packed_field(size_bytes = "4")]
899    pub mode_range: MspModeRange,
900}
901
902#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
903#[cfg_attr(feature = "bincode", bincode(describe))]
904#[derive(PrimitiveEnum, Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Default)]
905pub enum MixerMode {
906    Tri = 1,
907    QuadPlus = 2,
908    #[default]
909    QuadX = 3,
910    Bicopter = 4,
911    Gimbal = 5,
912    Y6 = 6,
913    Hex6 = 7,
914    FlyingWing = 8,
915    Y4 = 9,
916    Hex6X = 10,
917    OctoX8 = 11,
918}
919
920#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
921#[cfg_attr(feature = "bincode", bincode(describe))]
922#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
923#[packed_struct(bytes = "8", endian = "lsb", bit_numbering = "msb0")]
924pub struct MspMotorMixer {
925    pub throttle: u16,
926    pub roll: u16,
927    pub pitch: u16,
928    pub yaw: u16,
929}
930
931#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
932#[cfg_attr(feature = "bincode", bincode(describe))]
933#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
934#[packed_struct(bytes = "9", endian = "lsb", bit_numbering = "msb0")]
935pub struct MspSetMotorMixer {
936    pub index: u8,
937    #[packed_field(size_bytes = "8")]
938    pub motor_mixer: MspMotorMixer,
939}
940
941pub const MSP_DP_HEARTBEAT: u8 = 0;
942pub const MSP_DP_RELEASE: u8 = 1;
943pub const MSP_DP_CLEAR_SCREEN: u8 = 2;
944pub const MSP_DP_WRITE_STRING: u8 = 3;
945pub const MSP_DP_DRAW_SCREEN: u8 = 4;
946pub const MSP_DP_OPTIONS: u8 = 5;
947pub const MSP_DP_SYS: u8 = 6;
948pub const MSP_DP_FONTCHAR_WRITE: u8 = 7;
949
950#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
951#[cfg_attr(feature = "bincode", bincode(describe))]
952#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))]
953#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq)]
954pub struct MspDisplayPort {
955    pub payload: Vec<u8>,
956}
957
958impl MspDisplayPort {
959    pub fn new(payload: Vec<u8>) -> Self {
960        Self { payload }
961    }
962
963    pub fn heartbeat() -> Self {
964        Self {
965            payload: Vec::from([MSP_DP_HEARTBEAT]),
966        }
967    }
968
969    pub fn release() -> Self {
970        Self {
971            payload: Vec::from([MSP_DP_RELEASE]),
972        }
973    }
974
975    pub fn clear_screen() -> Self {
976        Self {
977            payload: Vec::from([MSP_DP_CLEAR_SCREEN]),
978        }
979    }
980
981    pub fn draw_screen() -> Self {
982        Self {
983            payload: Vec::from([MSP_DP_DRAW_SCREEN]),
984        }
985    }
986
987    pub fn write_string(row: u8, col: u8, attr: u8, text: &str) -> Self {
988        let mut payload = Vec::with_capacity(4 + text.len());
989        payload.push(MSP_DP_WRITE_STRING);
990        payload.push(row);
991        payload.push(col);
992        payload.push(attr);
993        payload.extend_from_slice(text.as_bytes());
994        Self { payload }
995    }
996
997    pub fn write_bytes(row: u8, col: u8, attr: u8, bytes: &[u8]) -> Self {
998        let mut payload = Vec::with_capacity(4 + bytes.len());
999        payload.push(MSP_DP_WRITE_STRING);
1000        payload.push(row);
1001        payload.push(col);
1002        payload.push(attr);
1003        payload.extend_from_slice(bytes);
1004        Self { payload }
1005    }
1006
1007    pub fn sys(row: u8, col: u8, element: u8) -> Self {
1008        Self {
1009            payload: Vec::from([MSP_DP_SYS, row, col, element]),
1010        }
1011    }
1012
1013    pub fn as_bytes(&self) -> &[u8] {
1014        &self.payload
1015    }
1016}
1017
1018#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
1019#[cfg_attr(feature = "bincode", bincode(describe))]
1020#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
1021#[packed_struct(bytes = "13", endian = "lsb", bit_numbering = "msb0")]
1022pub struct MspOsdConfig {
1023    pub video_system: u8,
1024    pub units: u8,
1025    pub rssi_alarm: u8,
1026    pub capacity_warning: u16,
1027    pub time_alarm: u16,
1028    pub alt_alarm: u16,
1029    pub dist_alarm: u16,
1030    pub neg_alt_alarm: u16,
1031}
1032
1033#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
1034#[cfg_attr(feature = "bincode", bincode(describe))]
1035#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
1036#[packed_struct(bytes = "1", endian = "lsb", bit_numbering = "msb0")]
1037pub struct MspSetGetOsdConfig {
1038    pub item_index: u8,
1039    #[packed_field(size_bytes = "13")]
1040    pub config: MspOsdConfig,
1041}
1042
1043#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
1044#[cfg_attr(feature = "bincode", bincode(describe))]
1045#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
1046#[packed_struct(bytes = "2", endian = "lsb", bit_numbering = "msb0")]
1047pub struct MspOsdItemPosition {
1048    pub col: u8,
1049    pub row: u8,
1050}
1051
1052#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
1053#[cfg_attr(feature = "bincode", bincode(describe))]
1054#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
1055#[packed_struct(bytes = "1", endian = "lsb", bit_numbering = "msb0")]
1056pub struct MspSetOsdLayout {
1057    pub item_index: u8,
1058    #[packed_field(size_bytes = "2")]
1059    pub item: MspOsdItemPosition,
1060}
1061
1062// inav msp layout item
1063#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
1064#[cfg_attr(feature = "bincode", bincode(describe))]
1065#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
1066#[packed_struct(bytes = "1", endian = "lsb", bit_numbering = "msb0")]
1067pub struct MspSetOsdLayoutItem {
1068    pub layout_index: u8,
1069    #[packed_field(size_bytes = "3")]
1070    pub item: MspSetOsdLayout,
1071}
1072
1073#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
1074#[cfg_attr(feature = "bincode", bincode(describe))]
1075#[derive(Debug, Serialize, Deserialize, Clone, Default)]
1076pub struct MspOsdSettings {
1077    pub osd_support: u8,
1078    pub config: MspOsdConfig,
1079    pub item_positions: Vec<MspOsdItemPosition>,
1080}
1081
1082#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
1083#[cfg_attr(feature = "bincode", bincode(describe))]
1084#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
1085#[packed_struct(bytes = "2", endian = "lsb", bit_numbering = "msb0")]
1086pub struct MspOsdLayouts {
1087    pub layout_count: u8,
1088    pub item_count: u8,
1089}
1090
1091#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
1092#[cfg_attr(feature = "bincode", bincode(describe))]
1093#[derive(PrimitiveEnum, Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Default)]
1094pub enum SerialIdentifier {
1095    #[default]
1096    None = 255,
1097    USART1 = 0,
1098    USART2 = 1,
1099    USART3 = 2,
1100    USART4 = 3,
1101    USART5 = 4,
1102    USART6 = 5,
1103    USART7 = 6,
1104    USART8 = 7,
1105    UsbVcp = 20,
1106    SoftSerial1 = 30,
1107    SoftSerial2 = 31,
1108}
1109
1110impl TryFrom<u8> for SerialIdentifier {
1111    type Error = &'static str;
1112
1113    fn try_from(value: u8) -> Result<Self, Self::Error> {
1114        let serial = match value {
1115            255 => SerialIdentifier::None,
1116            0 => SerialIdentifier::USART1,
1117            1 => SerialIdentifier::USART2,
1118            2 => SerialIdentifier::USART3,
1119            3 => SerialIdentifier::USART4,
1120            4 => SerialIdentifier::USART5,
1121            5 => SerialIdentifier::USART6,
1122            6 => SerialIdentifier::USART7,
1123            7 => SerialIdentifier::USART8,
1124            20 => SerialIdentifier::UsbVcp,
1125            30 => SerialIdentifier::SoftSerial1,
1126            31 => SerialIdentifier::SoftSerial2,
1127            _ => return Err("Serial identifier not found"),
1128        };
1129
1130        Ok(serial)
1131    }
1132}
1133
1134#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
1135#[cfg_attr(feature = "bincode", bincode(describe))]
1136#[derive(PrimitiveEnum, Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Default)]
1137pub enum Baudrate {
1138    #[default]
1139    BaudAuto = 0,
1140    Baud1200 = 1,
1141    Baud2400 = 2,
1142    Baud4800 = 3,
1143    Baud9600 = 4,
1144    Baud19200 = 5,
1145    Baud38400 = 6,
1146    Baud57600 = 7,
1147    Baud115200 = 8,
1148    Baud230400 = 9,
1149    Baud250000 = 10,
1150    Baud460800 = 11,
1151    Baud921600 = 12,
1152    Baud1000000 = 13,
1153    Baud1500000 = 14,
1154    Baud2000000 = 15,
1155    Baud2470000 = 16,
1156}
1157
1158impl TryFrom<&str> for Baudrate {
1159    type Error = &'static str;
1160
1161    fn try_from(value: &str) -> Result<Self, Self::Error> {
1162        let baudrate = match value {
1163            "0" => Baudrate::BaudAuto,
1164            "1200" => Baudrate::Baud1200,
1165            "2400" => Baudrate::Baud2400,
1166            "4800" => Baudrate::Baud4800,
1167            "9600" => Baudrate::Baud9600,
1168            "19200" => Baudrate::Baud19200,
1169            "38400" => Baudrate::Baud38400,
1170            "57600" => Baudrate::Baud57600,
1171            "115200" => Baudrate::Baud115200,
1172            "230400" => Baudrate::Baud230400,
1173            "250000" => Baudrate::Baud250000,
1174            "460800" => Baudrate::Baud460800,
1175            "921600" => Baudrate::Baud921600,
1176            "1000000" => Baudrate::Baud1000000,
1177            "1500000" => Baudrate::Baud1500000,
1178            "2000000" => Baudrate::Baud2000000,
1179            "2470000" => Baudrate::Baud2470000,
1180            _ => return Err("Baudrate identifier not found"),
1181        };
1182
1183        Ok(baudrate)
1184    }
1185}
1186
1187impl From<Baudrate> for String {
1188    fn from(value: Baudrate) -> Self {
1189        match value {
1190            Baudrate::BaudAuto => "0",
1191            Baudrate::Baud1200 => "1200",
1192            Baudrate::Baud2400 => "2400",
1193            Baudrate::Baud4800 => "4800",
1194            Baudrate::Baud9600 => "9600",
1195            Baudrate::Baud19200 => "19200",
1196            Baudrate::Baud38400 => "38400",
1197            Baudrate::Baud57600 => "57600",
1198            Baudrate::Baud115200 => "115200",
1199            Baudrate::Baud230400 => "230400",
1200            Baudrate::Baud250000 => "250000",
1201            Baudrate::Baud460800 => "460800",
1202            Baudrate::Baud921600 => "921600",
1203            Baudrate::Baud1000000 => "1000000",
1204            Baudrate::Baud1500000 => "1500000",
1205            Baudrate::Baud2000000 => "2000000",
1206            Baudrate::Baud2470000 => "2470000",
1207        }
1208        .to_owned()
1209    }
1210}
1211
1212#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
1213#[cfg_attr(feature = "bincode", bincode(describe))]
1214#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
1215#[packed_struct(endian = "lsb", bit_numbering = "msb0")]
1216pub struct MspSerialSetting {
1217    #[packed_field(size_bits = "8", ty = "enum")]
1218    pub identifier: SerialIdentifier,
1219    pub function_mask: u32,
1220    #[packed_field(size_bits = "8", ty = "enum")]
1221    pub msp_baudrate_index: Baudrate,
1222    #[packed_field(size_bits = "8", ty = "enum")]
1223    pub gps_baudrate_index: Baudrate,
1224    #[packed_field(size_bits = "8", ty = "enum")]
1225    pub telemetry_baudrate_index: Baudrate,
1226    #[packed_field(size_bits = "8", ty = "enum")]
1227    pub peripheral_baudrate_index: Baudrate,
1228}
1229
1230#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
1231#[cfg_attr(feature = "bincode", bincode(describe))]
1232#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
1233#[packed_struct(bytes = "1", endian = "lsb", bit_numbering = "msb0")]
1234pub struct MspSetServoMixRule {
1235    pub index: u8,
1236    #[packed_field(size_bytes = "8")]
1237    pub servo_rule: MspServoMixRule,
1238}
1239
1240#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
1241#[cfg_attr(feature = "bincode", bincode(describe))]
1242#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
1243#[packed_struct(bytes = "8", endian = "lsb", bit_numbering = "msb0")]
1244pub struct MspServoMixRule {
1245    pub target_channel: u8,
1246    pub input_source: u8,
1247    pub rate: u16,
1248    pub speed: u8,
1249    pub min: u8,
1250    pub max: u8,
1251    pub box_id: u8,
1252}
1253
1254#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
1255#[cfg_attr(feature = "bincode", bincode(describe))]
1256#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
1257#[packed_struct(bytes = "1", endian = "lsb", bit_numbering = "msb0")]
1258pub struct MspSetServoMixer {
1259    pub index: u8,
1260    #[packed_field(size_bytes = "6")]
1261    pub servo_rule: MspServoMixer,
1262}
1263
1264#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
1265#[cfg_attr(feature = "bincode", bincode(describe))]
1266#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
1267#[packed_struct(bytes = "6", endian = "lsb", bit_numbering = "msb0")]
1268pub struct MspServoMixer {
1269    pub target_channel: u8,
1270    pub input_source: u8,
1271    pub rate: i16,
1272    pub speed: u8,
1273    pub condition_id: i8,
1274}
1275
1276#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
1277#[cfg_attr(feature = "bincode", bincode(describe))]
1278#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
1279#[packed_struct(endian = "lsb", bit_numbering = "msb0")]
1280pub struct MspRxMap {
1281    pub map: [u8; 4], // MAX_MAPPABLE_RX_INPUTS
1282}
1283
1284#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
1285#[cfg_attr(feature = "bincode", bincode(describe))]
1286#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
1287#[packed_struct(endian = "lsb", bit_numbering = "msb0")]
1288pub struct MspSettingGroup {
1289    pub group_id: u16,
1290    pub start_id: u16,
1291    pub end_id: u16,
1292}
1293
1294#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
1295#[cfg_attr(feature = "bincode", bincode(describe))]
1296#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
1297#[packed_struct(endian = "lsb", bit_numbering = "msb0")]
1298pub struct MspSettingInfoRequest {
1299    pub null: u8,
1300    pub id: u16,
1301}
1302
1303#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
1304#[cfg_attr(feature = "bincode", bincode(describe))]
1305#[derive(PrimitiveEnum, Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Default)]
1306pub enum SettingMode {
1307    #[default]
1308    ModeDirect = 0,
1309    ModeLookup = 0x40,
1310}
1311
1312#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
1313#[cfg_attr(feature = "bincode", bincode(describe))]
1314#[derive(PrimitiveEnum, Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Default)]
1315pub enum SettingType {
1316    #[default]
1317    VarUint8 = 0,
1318    VarInt8,
1319    VarUint16,
1320    VarInt16,
1321    VarUint32,
1322    VarInt32,
1323    VarFloat,
1324    VarString,
1325}
1326
1327#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
1328#[cfg_attr(feature = "bincode", bincode(describe))]
1329#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone, Default)]
1330#[packed_struct(bytes = "15", endian = "lsb", bit_numbering = "msb0")]
1331pub struct MspSettingInfo {
1332    // pub name: [u8; ?], null terminated strings
1333
1334    // Parameter Group ID
1335    pub group_id: u16,
1336
1337    // Type, section and mode
1338    #[packed_field(size_bits = "8", ty = "enum")]
1339    pub setting_type: SettingType,
1340    pub setting_section: u8,
1341    #[packed_field(size_bits = "8", ty = "enum")]
1342    pub setting_mode: SettingMode,
1343
1344    pub min: u32,
1345    pub max: u32,
1346
1347    // Absolute setting index
1348    pub absolute_index: u16,
1349
1350    // If the setting is profile based, send the current one
1351    // and the count, both as uint8_t. For MASTER_VALUE, we
1352    // send two zeroes, so the MSP client can assume there
1353    pub profile_id: u8,
1354    pub profile_count: u8,
1355    // if setting uses enum values, it will be written here
1356    // pub enum_names: [String; ?] // TODO: packed_struct should support null terminated string parsing
1357    // pub value: [u8; ?]
1358}
1359
1360#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
1361#[cfg_attr(feature = "bincode", bincode(describe))]
1362#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))]
1363#[derive(PackedStruct, Serialize, Deserialize, Debug, Copy, Clone)]
1364#[packed_struct(endian = "lsb")]
1365pub struct MspRc {
1366    pub channels: [u16; 16], // 16 RC channels
1367}
1368
1369impl Default for MspRc {
1370    fn default() -> Self {
1371        Self::new()
1372    }
1373}
1374
1375impl MspRc {
1376    pub fn new() -> Self {
1377        MspRc { channels: [0; 16] }
1378    }
1379
1380    pub fn set_roll(&mut self, value: u16) {
1381        self.channels[0] = value;
1382    }
1383
1384    pub fn set_pitch(&mut self, value: u16) {
1385        self.channels[1] = value;
1386    }
1387
1388    pub fn set_throttle(&mut self, value: u16) {
1389        self.channels[2] = value;
1390    }
1391
1392    pub fn set_yaw(&mut self, value: u16) {
1393        self.channels[3] = value;
1394    }
1395
1396    pub fn set_aux1(&mut self, value: u16) {
1397        self.channels[4] = value;
1398    }
1399    pub fn set_aux2(&mut self, value: u16) {
1400        self.channels[5] = value;
1401    }
1402    pub fn set_aux3(&mut self, value: u16) {
1403        self.channels[6] = value;
1404    }
1405    pub fn set_aux4(&mut self, value: u16) {
1406        self.channels[7] = value;
1407    }
1408}
1409
1410// Gather all the commands in a common enum we can use as a higher level protocol
1411#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
1412#[cfg_attr(feature = "bincode", bincode(describe))]
1413#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))]
1414#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1415pub enum MspRequest {
1416    #[default]
1417    Unknown,
1418    MspApiVersionRequest,
1419    MspApiVersion(MspApiVersion),
1420    MspFcVersionRequest,
1421    MspFlightControllerVersion(MspFlightControllerVersion),
1422    MspBatteryConfigRequest,
1423    MspBatteryConfig(MspBatteryConfig),
1424    MspBatteryStateRequest,
1425    MspBatteryState(MspBatteryState),
1426    MspAnalogRequest,
1427    MspAnalog(MspAnalog),
1428    MspVoltageMeterConfigRequest,
1429    MspVoltageMeterConfig(MspVoltageMeterConfig),
1430    MspVoltageMetersRequest,
1431    MspVoltageMeter(MspVoltageMeter),
1432    MspRc,
1433    MspSetRawRc(MspRc),
1434    MspRawImu,
1435    MspStatus(MspStatus),
1436    MspStatusEx(MspStatusEx),
1437    MspDisplayPort(MspDisplayPort),
1438    MspSensorRangefinder(MspSensorRangefinder),
1439    MspSensorOpticFlow(MspSensorOpticFlow),
1440}
1441
1442impl MspRequest {
1443    pub fn command_code(&self) -> MspCommandCode {
1444        match self {
1445            MspRequest::MspApiVersionRequest => MspCommandCode::MSP_API_VERSION,
1446            MspRequest::MspApiVersion(_) => MspCommandCode::MSP_API_VERSION,
1447            MspRequest::MspFcVersionRequest => MspCommandCode::MSP_FC_VERSION,
1448            MspRequest::MspFlightControllerVersion(_) => MspCommandCode::MSP_FC_VERSION,
1449            MspRequest::MspBatteryConfigRequest => MspCommandCode::MSP_BATTERY_CONFIG,
1450            MspRequest::MspBatteryConfig(_) => MspCommandCode::MSP_BATTERY_CONFIG,
1451            MspRequest::MspBatteryStateRequest => MspCommandCode::MSP_BATTERY_STATE,
1452            MspRequest::MspBatteryState(_) => MspCommandCode::MSP_BATTERY_STATE,
1453            MspRequest::MspAnalogRequest => MspCommandCode::MSP_ANALOG,
1454            MspRequest::MspAnalog(_) => MspCommandCode::MSP_ANALOG,
1455            MspRequest::MspVoltageMeterConfigRequest => MspCommandCode::MSP_VOLTAGE_METER_CONFIG,
1456            MspRequest::MspVoltageMeterConfig(_) => MspCommandCode::MSP_VOLTAGE_METER_CONFIG,
1457            MspRequest::MspVoltageMetersRequest => MspCommandCode::MSP_VOLTAGE_METERS,
1458            MspRequest::MspVoltageMeter(_) => MspCommandCode::MSP_VOLTAGE_METERS,
1459            MspRequest::MspRc => MspCommandCode::MSP_RC,
1460            MspRequest::MspSetRawRc(_) => MspCommandCode::MSP_SET_RAW_RC,
1461            MspRequest::MspRawImu => MspCommandCode::MSP_RAW_IMU,
1462            MspRequest::MspStatus(_) => MspCommandCode::MSP_STATUS,
1463            MspRequest::MspStatusEx(_) => MspCommandCode::MSP_STATUS_EX,
1464            MspRequest::MspDisplayPort(_) => MspCommandCode::MSP_DISPLAYPORT,
1465            MspRequest::MspSensorRangefinder(_) => MspCommandCode::MSP2_SENSOR_RANGEFINDER,
1466            MspRequest::MspSensorOpticFlow(_) => MspCommandCode::MSP2_SENSOR_OPTIC_FLOW,
1467            _ => MspCommandCode::MSP_API_VERSION,
1468        }
1469    }
1470
1471    pub fn from_command_code(cmd: MspCommandCode) -> Option<Self> {
1472        match cmd {
1473            MspCommandCode::MSP_API_VERSION => Some(MspRequest::MspApiVersionRequest),
1474            MspCommandCode::MSP_FC_VERSION => Some(MspRequest::MspFcVersionRequest),
1475            MspCommandCode::MSP_BATTERY_CONFIG => Some(MspRequest::MspBatteryConfigRequest),
1476            MspCommandCode::MSP_BATTERY_STATE => Some(MspRequest::MspBatteryStateRequest),
1477            MspCommandCode::MSP_ANALOG => Some(MspRequest::MspAnalogRequest),
1478            MspCommandCode::MSP_VOLTAGE_METER_CONFIG => {
1479                Some(MspRequest::MspVoltageMeterConfigRequest)
1480            }
1481            MspCommandCode::MSP_VOLTAGE_METERS => Some(MspRequest::MspVoltageMetersRequest),
1482            MspCommandCode::MSP_RC => Some(MspRequest::MspRc),
1483            MspCommandCode::MSP_RAW_IMU => Some(MspRequest::MspRawImu),
1484            _ => None,
1485        }
1486    }
1487
1488    pub fn from_command_id(cmd: u16) -> Option<Self> {
1489        let cmd = MspCommandCode::from_primitive(cmd)?;
1490        Self::from_command_code(cmd)
1491    }
1492
1493    pub fn from_packet(packet: &MspPacket) -> Option<Self> {
1494        let cmd = MspCommandCode::from_primitive(packet.cmd)?;
1495        match cmd {
1496            MspCommandCode::MSP2_SENSOR_RANGEFINDER => packet
1497                .decode_as::<MspSensorRangefinder>()
1498                .ok()
1499                .map(MspRequest::MspSensorRangefinder),
1500            MspCommandCode::MSP2_SENSOR_OPTIC_FLOW => packet
1501                .decode_as::<MspSensorOpticFlow>()
1502                .ok()
1503                .map(MspRequest::MspSensorOpticFlow),
1504            _ => Self::from_command_code(cmd),
1505        }
1506    }
1507}
1508
1509fn pack_or_empty<T: PackedStruct>(value: Option<&T>) -> Result<MspPacketData, PackingError> {
1510    if let Some(value) = value {
1511        let packed = value.pack()?;
1512        Ok(MspPacketData::from(packed.as_bytes_slice()))
1513    } else {
1514        Ok(MspPacketData::default())
1515    }
1516}
1517
1518impl From<MspRequest> for MspPacket {
1519    fn from(request: MspRequest) -> Self {
1520        match request {
1521            MspRequest::MspApiVersionRequest => MspPacket {
1522                cmd: MspCommandCode::MSP_API_VERSION.to_primitive(),
1523                direction: ToFlightController,
1524                data: MspPacketData::new(),
1525            },
1526            MspRequest::MspApiVersion(version) => MspPacket {
1527                cmd: MspCommandCode::MSP_API_VERSION.to_primitive(),
1528                direction: FromFlightController,
1529                data: pack_or_empty(Some(&version)).unwrap_or_default(),
1530            },
1531            MspRequest::MspFcVersionRequest => MspPacket {
1532                cmd: MspCommandCode::MSP_FC_VERSION.to_primitive(),
1533                direction: ToFlightController,
1534                data: MspPacketData::new(),
1535            },
1536            MspRequest::MspFlightControllerVersion(version) => MspPacket {
1537                cmd: MspCommandCode::MSP_FC_VERSION.to_primitive(),
1538                direction: FromFlightController,
1539                data: pack_or_empty(Some(&version)).unwrap_or_default(),
1540            },
1541            MspRequest::MspBatteryConfigRequest => MspPacket {
1542                cmd: MspCommandCode::MSP_BATTERY_CONFIG.to_primitive(),
1543                direction: ToFlightController,
1544                data: MspPacketData::new(),
1545            },
1546            MspRequest::MspBatteryConfig(config) => MspPacket {
1547                cmd: MspCommandCode::MSP_BATTERY_CONFIG.to_primitive(),
1548                direction: FromFlightController,
1549                data: pack_or_empty(Some(&config)).unwrap_or_default(),
1550            },
1551            MspRequest::MspBatteryStateRequest => MspPacket {
1552                cmd: MspCommandCode::MSP_BATTERY_STATE.to_primitive(),
1553                direction: ToFlightController,
1554                data: MspPacketData::new(), // empty
1555            },
1556            MspRequest::MspBatteryState(state) => MspPacket {
1557                cmd: MspCommandCode::MSP_BATTERY_STATE.to_primitive(),
1558                direction: FromFlightController,
1559                data: pack_or_empty(Some(&state)).unwrap_or_default(),
1560            },
1561            MspRequest::MspAnalogRequest => MspPacket {
1562                cmd: MspCommandCode::MSP_ANALOG.to_primitive(),
1563                direction: ToFlightController,
1564                data: MspPacketData::new(), // empty
1565            },
1566            MspRequest::MspAnalog(analog) => MspPacket {
1567                cmd: MspCommandCode::MSP_ANALOG.to_primitive(),
1568                direction: FromFlightController,
1569                data: pack_or_empty(Some(&analog)).unwrap_or_default(),
1570            },
1571            MspRequest::MspVoltageMeterConfigRequest => MspPacket {
1572                cmd: MspCommandCode::MSP_VOLTAGE_METER_CONFIG.to_primitive(),
1573                direction: ToFlightController,
1574                data: MspPacketData::new(),
1575            },
1576            MspRequest::MspVoltageMeterConfig(config) => MspPacket {
1577                cmd: MspCommandCode::MSP_VOLTAGE_METER_CONFIG.to_primitive(),
1578                direction: FromFlightController,
1579                data: pack_or_empty(Some(&config)).unwrap_or_default(),
1580            },
1581            MspRequest::MspVoltageMetersRequest => MspPacket {
1582                cmd: MspCommandCode::MSP_VOLTAGE_METERS.to_primitive(),
1583                direction: ToFlightController,
1584                data: MspPacketData::new(), // empty
1585            },
1586            MspRequest::MspVoltageMeter(meter) => MspPacket {
1587                cmd: MspCommandCode::MSP_VOLTAGE_METERS.to_primitive(),
1588                direction: FromFlightController,
1589                data: pack_or_empty(Some(&meter)).unwrap_or_default(),
1590            },
1591            MspRequest::MspRc => MspPacket {
1592                cmd: MspCommandCode::MSP_RC.to_primitive(),
1593                direction: ToFlightController,
1594                data: MspPacketData::new(), // empty
1595            },
1596            MspRequest::MspSetRawRc(rc) => {
1597                let data = rc.pack().unwrap();
1598                MspPacket {
1599                    cmd: MspCommandCode::MSP_SET_RAW_RC.to_primitive(),
1600                    direction: ToFlightController,
1601                    data: MspPacketData::from(data.as_slice()),
1602                }
1603            }
1604            MspRequest::MspRawImu => MspPacket {
1605                cmd: MspCommandCode::MSP_RAW_IMU.to_primitive(),
1606                direction: ToFlightController,
1607                data: MspPacketData::new(), // empty
1608            },
1609            MspRequest::MspStatus(status) => MspPacket {
1610                cmd: MspCommandCode::MSP_STATUS.to_primitive(),
1611                direction: FromFlightController,
1612                data: status.to_packet_data().unwrap(),
1613            },
1614            MspRequest::MspStatusEx(status) => MspPacket {
1615                cmd: MspCommandCode::MSP_STATUS_EX.to_primitive(),
1616                direction: FromFlightController,
1617                data: status.to_packet_data().unwrap(),
1618            },
1619            MspRequest::MspDisplayPort(displayport) => MspPacket {
1620                cmd: MspCommandCode::MSP_DISPLAYPORT.to_primitive(),
1621                direction: FromFlightController,
1622                data: MspPacketData::from(displayport.as_bytes()),
1623            },
1624            MspRequest::MspSensorRangefinder(data) => MspPacket {
1625                cmd: MspCommandCode::MSP2_SENSOR_RANGEFINDER.to_primitive(),
1626                direction: ToFlightController,
1627                data: pack_or_empty(Some(&data)).unwrap_or_default(),
1628            },
1629            MspRequest::MspSensorOpticFlow(data) => MspPacket {
1630                cmd: MspCommandCode::MSP2_SENSOR_OPTIC_FLOW.to_primitive(),
1631                direction: ToFlightController,
1632                data: pack_or_empty(Some(&data)).unwrap_or_default(),
1633            },
1634            _ => MspPacket {
1635                cmd: MspCommandCode::MSP_API_VERSION.to_primitive(),
1636                direction: ToFlightController,
1637                data: MspPacketData::new(), // empty
1638            },
1639        }
1640    }
1641}
1642
1643impl From<&MspRequest> for MspPacket {
1644    fn from(request: &MspRequest) -> Self {
1645        match request {
1646            MspRequest::MspApiVersionRequest => MspPacket {
1647                cmd: MspCommandCode::MSP_API_VERSION.to_primitive(),
1648                direction: ToFlightController,
1649                data: MspPacketData::new(),
1650            },
1651            MspRequest::MspApiVersion(version) => MspPacket {
1652                cmd: MspCommandCode::MSP_API_VERSION.to_primitive(),
1653                direction: FromFlightController,
1654                data: pack_or_empty(Some(version)).unwrap_or_default(),
1655            },
1656            MspRequest::MspFcVersionRequest => MspPacket {
1657                cmd: MspCommandCode::MSP_FC_VERSION.to_primitive(),
1658                direction: ToFlightController,
1659                data: MspPacketData::new(),
1660            },
1661            MspRequest::MspFlightControllerVersion(version) => MspPacket {
1662                cmd: MspCommandCode::MSP_FC_VERSION.to_primitive(),
1663                direction: FromFlightController,
1664                data: pack_or_empty(Some(version)).unwrap_or_default(),
1665            },
1666            MspRequest::MspBatteryConfigRequest => MspPacket {
1667                cmd: MspCommandCode::MSP_BATTERY_CONFIG.to_primitive(),
1668                direction: ToFlightController,
1669                data: MspPacketData::new(),
1670            },
1671            MspRequest::MspBatteryConfig(config) => MspPacket {
1672                cmd: MspCommandCode::MSP_BATTERY_CONFIG.to_primitive(),
1673                direction: FromFlightController,
1674                data: pack_or_empty(Some(config)).unwrap_or_default(),
1675            },
1676            MspRequest::MspBatteryStateRequest => MspPacket {
1677                cmd: MspCommandCode::MSP_BATTERY_STATE.to_primitive(),
1678                direction: ToFlightController,
1679                data: MspPacketData::new(), // empty
1680            },
1681            MspRequest::MspBatteryState(state) => MspPacket {
1682                cmd: MspCommandCode::MSP_BATTERY_STATE.to_primitive(),
1683                direction: FromFlightController,
1684                data: pack_or_empty(Some(state)).unwrap_or_default(),
1685            },
1686            MspRequest::MspAnalogRequest => MspPacket {
1687                cmd: MspCommandCode::MSP_ANALOG.to_primitive(),
1688                direction: ToFlightController,
1689                data: MspPacketData::new(), // empty
1690            },
1691            MspRequest::MspAnalog(analog) => MspPacket {
1692                cmd: MspCommandCode::MSP_ANALOG.to_primitive(),
1693                direction: FromFlightController,
1694                data: pack_or_empty(Some(analog)).unwrap_or_default(),
1695            },
1696            MspRequest::MspVoltageMeterConfigRequest => MspPacket {
1697                cmd: MspCommandCode::MSP_VOLTAGE_METER_CONFIG.to_primitive(),
1698                direction: ToFlightController,
1699                data: MspPacketData::new(),
1700            },
1701            MspRequest::MspVoltageMeterConfig(config) => MspPacket {
1702                cmd: MspCommandCode::MSP_VOLTAGE_METER_CONFIG.to_primitive(),
1703                direction: FromFlightController,
1704                data: pack_or_empty(Some(config)).unwrap_or_default(),
1705            },
1706            MspRequest::MspVoltageMetersRequest => MspPacket {
1707                cmd: MspCommandCode::MSP_VOLTAGE_METERS.to_primitive(),
1708                direction: ToFlightController,
1709                data: MspPacketData::new(), // empty
1710            },
1711            MspRequest::MspVoltageMeter(meter) => MspPacket {
1712                cmd: MspCommandCode::MSP_VOLTAGE_METERS.to_primitive(),
1713                direction: FromFlightController,
1714                data: pack_or_empty(Some(meter)).unwrap_or_default(),
1715            },
1716            MspRequest::MspRc => MspPacket {
1717                cmd: MspCommandCode::MSP_RC.to_primitive(),
1718                direction: ToFlightController,
1719                data: MspPacketData::new(), // empty
1720            },
1721            MspRequest::MspSetRawRc(rc) => MspPacket {
1722                cmd: MspCommandCode::MSP_SET_RAW_RC.to_primitive(),
1723                direction: ToFlightController,
1724                data: MspPacketData::from(rc.pack().unwrap().as_slice()),
1725            },
1726            MspRequest::MspRawImu => MspPacket {
1727                cmd: MspCommandCode::MSP_RAW_IMU.to_primitive(),
1728                direction: ToFlightController,
1729                data: MspPacketData::new(), // empty
1730            },
1731            MspRequest::MspStatus(status) => MspPacket {
1732                cmd: MspCommandCode::MSP_STATUS.to_primitive(),
1733                direction: FromFlightController,
1734                data: status.to_packet_data().unwrap(),
1735            },
1736            MspRequest::MspStatusEx(status) => MspPacket {
1737                cmd: MspCommandCode::MSP_STATUS_EX.to_primitive(),
1738                direction: FromFlightController,
1739                data: status.to_packet_data().unwrap(),
1740            },
1741            MspRequest::MspDisplayPort(displayport) => MspPacket {
1742                cmd: MspCommandCode::MSP_DISPLAYPORT.to_primitive(),
1743                direction: FromFlightController,
1744                data: MspPacketData::from(displayport.as_bytes()),
1745            },
1746            MspRequest::MspSensorRangefinder(data) => MspPacket {
1747                cmd: MspCommandCode::MSP2_SENSOR_RANGEFINDER.to_primitive(),
1748                direction: ToFlightController,
1749                data: pack_or_empty(Some(data)).unwrap_or_default(),
1750            },
1751            MspRequest::MspSensorOpticFlow(data) => MspPacket {
1752                cmd: MspCommandCode::MSP2_SENSOR_OPTIC_FLOW.to_primitive(),
1753                direction: ToFlightController,
1754                data: pack_or_empty(Some(data)).unwrap_or_default(),
1755            },
1756            _ => MspPacket {
1757                cmd: MspCommandCode::MSP_API_VERSION.to_primitive(),
1758                direction: ToFlightController,
1759                data: MspPacketData::new(), // empty
1760            },
1761        }
1762    }
1763}
1764
1765// Gather all the commands in a common enum we can use as a higher level protocol
1766#[cfg_attr(feature = "bincode", derive(Decode, Encode))]
1767#[cfg_attr(feature = "bincode", bincode(describe))]
1768#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1769pub enum MspResponse {
1770    #[default]
1771    Unknown,
1772    MspApiVersion(MspApiVersion),
1773    MspFlightControllerVariant(MspFlightControllerVariant),
1774    MspStatus(MspStatus),
1775    MspStatusEx(MspStatusEx),
1776    MspBfConfig(MspBfConfig),
1777    MspRawImu(MspRawImu),
1778    MspDataFlashSummaryReply(MspDataFlashSummaryReply),
1779    MspDataFlashReply(MspDataFlashReply),
1780    MspDataFlashRead(MspDataFlashRead),
1781    MspAccTrim(MspAccTrim),
1782    MspIdent(MspIdent),
1783    MspMisc(MspMisc),
1784    MspAttitude(MspAttitude),
1785    MspAltitude(MspAltitude),
1786    MspBatteryConfig(MspBatteryConfig),
1787    MspVoltageMeterConfig(MspVoltageMeterConfig),
1788    MspAnalog(MspAnalog),
1789    MspRssiConfig(MspRssiConfig),
1790    MspVoltageMeter(MspVoltageMeter),
1791    MspCurrentMeter(MspCurrentMeter),
1792    MspBatteryState(MspBatteryState),
1793    MspRcTuning(MspRcTuning),
1794    MspRxConfig(MspRxConfig),
1795    MspRcChannelValue(MspRcChannelValue),
1796    MspRcMappedChannel(MspRcMappedChannel),
1797    MspFeatures(MspFeatures),
1798    MspMotor(MspMotor),
1799    MspMotor3DConfig(MspMotor3DConfig),
1800    MspMotorConfig(MspMotorConfig),
1801    MspRcDeadband(MspRcDeadband),
1802    MspSensorAlignment(MspSensorAlignment),
1803    MspAdvancedConfig(MspAdvancedConfig),
1804    MspFilterConfig(MspFilterConfig),
1805    MspPidAdvanced(MspPidAdvanced),
1806    MspSensorConfig(MspSensorConfig),
1807    MspServos(MspServos),
1808    MspMixerConfig(MspMixerConfig),
1809    MspModeRange(MspModeRange),
1810    MspSetModeRange(MspSetModeRange),
1811    MspOsdConfig(MspOsdConfig),
1812    MspSetGetOsdConfig(MspSetGetOsdConfig),
1813    MspSetOsdLayout(MspSetOsdLayout),
1814    MspSetOsdLayoutItem(MspSetOsdLayoutItem),
1815    MspOsdLayouts(MspOsdLayouts),
1816    MspSerialSetting(MspSerialSetting),
1817    MspSettingInfoRequest(MspSettingInfoRequest),
1818    MspSettingInfo(MspSettingInfo),
1819    MspRc(MspRc),
1820}
1821
1822macro_rules! msp_response_command_map {
1823    ($($variant:ident => $cmd:ident),* $(,)?) => {
1824        fn response_command_code(resp: &MspResponse) -> MspCommandCode {
1825            match resp {
1826                $(MspResponse::$variant(_) => MspCommandCode::$cmd,)*
1827                MspResponse::Unknown => MspCommandCode::MSP_API_VERSION,
1828            }
1829        }
1830    };
1831}
1832
1833macro_rules! msp_response_decode_map {
1834    ($($cmd:ident => $variant:ident : $ty:ty => |$packet:ident| $decode:expr),* $(,)?) => {
1835        fn decode_response(cmd: MspCommandCode, packet: &MspPacket) -> MspResponse {
1836            match cmd {
1837                $(
1838                    MspCommandCode::$cmd => {
1839                        let $packet = packet;
1840                        $decode
1841                            .map(MspResponse::$variant)
1842                            .unwrap_or(MspResponse::Unknown)
1843                    }
1844                )*
1845                _ => MspResponse::Unknown,
1846            }
1847        }
1848    };
1849}
1850
1851msp_response_command_map!(
1852    MspApiVersion => MSP_API_VERSION,
1853    MspFlightControllerVariant => MSP_FC_VARIANT,
1854    MspStatus => MSP_STATUS,
1855    MspStatusEx => MSP_STATUS_EX,
1856    MspBfConfig => MSP_BF_CONFIG,
1857    MspRawImu => MSP_RAW_IMU,
1858    MspDataFlashSummaryReply => MSP_DATAFLASH_SUMMARY,
1859    MspDataFlashReply => MSP_DATAFLASH_READ,
1860    MspDataFlashRead => MSP_DATAFLASH_READ,
1861    MspAccTrim => MSP_ACC_TRIM,
1862    MspIdent => MSP_IDENT,
1863    MspMisc => MSP_MISC,
1864    MspAttitude => MSP_ATTITUDE,
1865    MspAltitude => MSP_ALTITUDE,
1866    MspBatteryConfig => MSP_BATTERY_CONFIG,
1867    MspVoltageMeterConfig => MSP_VOLTAGE_METER_CONFIG,
1868    MspAnalog => MSP_ANALOG,
1869    MspRssiConfig => MSP_RSSI_CONFIG,
1870    MspVoltageMeter => MSP_VOLTAGE_METERS,
1871    MspCurrentMeter => MSP_AMPERAGE_METER_CONFIG,
1872    MspBatteryState => MSP_BATTERY_STATE,
1873    MspRcTuning => MSP_RC_TUNING,
1874    MspRxConfig => MSP_RX_CONFIG,
1875    MspRcChannelValue => MSP_RX_MAP,
1876    MspRcMappedChannel => MSP_SET_RX_MAP,
1877    MspFeatures => MSP_FEATURE,
1878    MspMotor => MSP_MOTOR,
1879    MspMotor3DConfig => MSP_MOTOR_3D_CONFIG,
1880    MspMotorConfig => MSP_MOTOR_CONFIG,
1881    MspRcDeadband => MSP_RC_DEADBAND,
1882    MspSensorAlignment => MSP_BOARD_ALIGNMENT,
1883    MspAdvancedConfig => MSP_ADVANCED_CONFIG,
1884    MspFilterConfig => MSP_FILTER_CONFIG,
1885    MspPidAdvanced => MSP_PID_ADVANCED,
1886    MspSensorConfig => MSP_SENSOR_CONFIG,
1887    MspServos => MSP_SERVO,
1888    MspMixerConfig => MSP_MIXER,
1889    MspModeRange => MSP_MODE_RANGES,
1890    MspSetModeRange => MSP_SET_MODE_RANGE,
1891    MspOsdConfig => MSP_OSD_CONFIG,
1892    MspSetGetOsdConfig => MSP_OSD_CONFIG,
1893    MspSetOsdLayout => MSP_OSD_LAYOUT_CONFIG,
1894    MspSetOsdLayoutItem => MSP2_INAV_OSD_SET_LAYOUT_ITEM,
1895    MspOsdLayouts => MSP2_INAV_OSD_LAYOUTS,
1896    MspSerialSetting => MSP2_SET_SERIAL_CONFIG,
1897    MspSettingInfoRequest => MSP2_COMMON_SETTING,
1898    MspSettingInfo => MSP2_COMMON_SETTING_INFO,
1899    MspRc => MSP_RC,
1900);
1901
1902msp_response_decode_map!(
1903    MSP_API_VERSION => MspApiVersion: MspApiVersion => |packet| packet.decode_as::<MspApiVersion>(),
1904    MSP_FC_VARIANT => MspFlightControllerVariant: MspFlightControllerVariant => |packet| {
1905        packet.decode_as::<MspFlightControllerVariant>()
1906    },
1907    MSP_STATUS => MspStatus: MspStatus => |packet| MspStatus::from_bytes(packet.data.as_slice()),
1908    MSP_STATUS_EX => MspStatusEx: MspStatusEx => |packet| {
1909        MspStatusEx::from_bytes(packet.data.as_slice())
1910    },
1911    MSP_BF_CONFIG => MspBfConfig: MspBfConfig => |packet| packet.decode_as::<MspBfConfig>(),
1912    MSP_RAW_IMU => MspRawImu: MspRawImu => |packet| packet.decode_as::<MspRawImu>(),
1913    MSP_DATAFLASH_SUMMARY => MspDataFlashSummaryReply: MspDataFlashSummaryReply => |packet| {
1914        packet.decode_as::<MspDataFlashSummaryReply>()
1915    },
1916    MSP_DATAFLASH_READ => MspDataFlashReply: MspDataFlashReply => |packet| {
1917        packet.decode_as::<MspDataFlashReply>()
1918    },
1919    MSP_ACC_TRIM => MspAccTrim: MspAccTrim => |packet| packet.decode_as::<MspAccTrim>(),
1920    MSP_IDENT => MspIdent: MspIdent => |packet| packet.decode_as::<MspIdent>(),
1921    MSP_MISC => MspMisc: MspMisc => |packet| packet.decode_as::<MspMisc>(),
1922    MSP_ATTITUDE => MspAttitude: MspAttitude => |packet| packet.decode_as::<MspAttitude>(),
1923    MSP_ALTITUDE => MspAltitude: MspAltitude => |packet| packet.decode_as::<MspAltitude>(),
1924    MSP_BATTERY_CONFIG => MspBatteryConfig: MspBatteryConfig => |packet| {
1925        packet.decode_as::<MspBatteryConfig>()
1926    },
1927    MSP_VOLTAGE_METER_CONFIG => MspVoltageMeterConfig: MspVoltageMeterConfig => |packet| {
1928        packet.decode_as::<MspVoltageMeterConfig>()
1929    },
1930    MSP_ANALOG => MspAnalog: MspAnalog => |packet| packet.decode_as::<MspAnalog>(),
1931    MSP_RSSI_CONFIG => MspRssiConfig: MspRssiConfig => |packet| {
1932        packet.decode_as::<MspRssiConfig>()
1933    },
1934    MSP_VOLTAGE_METERS => MspVoltageMeter: MspVoltageMeter => |packet| {
1935        packet.decode_as::<MspVoltageMeter>()
1936    },
1937    MSP_AMPERAGE_METER_CONFIG => MspCurrentMeter: MspCurrentMeter => |packet| {
1938        packet.decode_as::<MspCurrentMeter>()
1939    },
1940    MSP_BATTERY_STATE => MspBatteryState: MspBatteryState => |packet| {
1941        packet.decode_as::<MspBatteryState>()
1942    },
1943    MSP_RC_TUNING => MspRcTuning: MspRcTuning => |packet| packet.decode_as::<MspRcTuning>(),
1944    MSP_RX_CONFIG => MspRxConfig: MspRxConfig => |packet| packet.decode_as::<MspRxConfig>(),
1945    MSP_RX_MAP => MspRcChannelValue: MspRcChannelValue => |packet| {
1946        packet.decode_as::<MspRcChannelValue>()
1947    },
1948    MSP_SET_RX_MAP => MspRcMappedChannel: MspRcMappedChannel => |packet| {
1949        packet.decode_as::<MspRcMappedChannel>()
1950    },
1951    MSP_FEATURE => MspFeatures: MspFeatures => |packet| packet.decode_as::<MspFeatures>(),
1952    MSP_MOTOR => MspMotor: MspMotor => |packet| packet.decode_as::<MspMotor>(),
1953    MSP_MOTOR_3D_CONFIG => MspMotor3DConfig: MspMotor3DConfig => |packet| {
1954        packet.decode_as::<MspMotor3DConfig>()
1955    },
1956    MSP_MOTOR_CONFIG => MspMotorConfig: MspMotorConfig => |packet| {
1957        packet.decode_as::<MspMotorConfig>()
1958    },
1959    MSP_RC_DEADBAND => MspRcDeadband: MspRcDeadband => |packet| packet.decode_as::<MspRcDeadband>(),
1960    MSP_BOARD_ALIGNMENT => MspSensorAlignment: MspSensorAlignment => |packet| {
1961        packet.decode_as::<MspSensorAlignment>()
1962    },
1963    MSP_ADVANCED_CONFIG => MspAdvancedConfig: MspAdvancedConfig => |packet| {
1964        packet.decode_as::<MspAdvancedConfig>()
1965    },
1966    MSP_FILTER_CONFIG => MspFilterConfig: MspFilterConfig => |packet| {
1967        packet.decode_as::<MspFilterConfig>()
1968    },
1969    MSP_PID_ADVANCED => MspPidAdvanced: MspPidAdvanced => |packet| {
1970        packet.decode_as::<MspPidAdvanced>()
1971    },
1972    MSP_SENSOR_CONFIG => MspSensorConfig: MspSensorConfig => |packet| {
1973        packet.decode_as::<MspSensorConfig>()
1974    },
1975    MSP_SERVO => MspServos: MspServos => |packet| packet.decode_as::<MspServos>(),
1976    MSP_MIXER => MspMixerConfig: MspMixerConfig => |packet| packet.decode_as::<MspMixerConfig>(),
1977    MSP_MODE_RANGES => MspModeRange: MspModeRange => |packet| packet.decode_as::<MspModeRange>(),
1978    MSP_SET_MODE_RANGE => MspSetModeRange: MspSetModeRange => |packet| {
1979        packet.decode_as::<MspSetModeRange>()
1980    },
1981    MSP_OSD_CONFIG => MspOsdConfig: MspOsdConfig => |packet| packet.decode_as::<MspOsdConfig>(),
1982    MSP_OSD_LAYOUT_CONFIG => MspSetOsdLayout: MspSetOsdLayout => |packet| {
1983        packet.decode_as::<MspSetOsdLayout>()
1984    },
1985    MSP2_INAV_OSD_SET_LAYOUT_ITEM => MspSetOsdLayoutItem: MspSetOsdLayoutItem => |packet| {
1986        packet.decode_as::<MspSetOsdLayoutItem>()
1987    },
1988    MSP2_INAV_OSD_LAYOUTS => MspOsdLayouts: MspOsdLayouts => |packet| {
1989        packet.decode_as::<MspOsdLayouts>()
1990    },
1991    MSP2_SET_SERIAL_CONFIG => MspSerialSetting: MspSerialSetting => |packet| {
1992        packet.decode_as::<MspSerialSetting>()
1993    },
1994    MSP2_COMMON_SETTING => MspSettingInfoRequest: MspSettingInfoRequest => |packet| {
1995        packet.decode_as::<MspSettingInfoRequest>()
1996    },
1997    MSP2_COMMON_SETTING_INFO => MspSettingInfo: MspSettingInfo => |packet| {
1998        packet.decode_as::<MspSettingInfo>()
1999    },
2000    MSP_RC => MspRc: MspRc => |packet| packet.decode_as::<MspRc>(),
2001);
2002
2003impl MspResponse {
2004    pub fn command_code(&self) -> MspCommandCode {
2005        // TODO: Not sure about all those mapping recheck them.
2006        response_command_code(self)
2007    }
2008
2009    pub fn to_bytes(&self) -> Result<MspPacketData, PackingError> {
2010        fn pack_into_packet_data<T: PackedStruct>(
2011            value: &T,
2012        ) -> Result<MspPacketData, PackingError> {
2013            let packed = value.pack()?;
2014            Ok(MspPacketData::from(packed.as_bytes_slice()))
2015        }
2016
2017        match self {
2018            MspResponse::MspApiVersion(data) => pack_into_packet_data(data),
2019            MspResponse::MspFlightControllerVariant(data) => pack_into_packet_data(data),
2020            MspResponse::MspStatus(data) => data.to_packet_data(),
2021            MspResponse::MspStatusEx(data) => data.to_packet_data(),
2022            MspResponse::MspBfConfig(data) => pack_into_packet_data(data),
2023            MspResponse::MspRawImu(data) => pack_into_packet_data(data),
2024            MspResponse::MspDataFlashSummaryReply(data) => pack_into_packet_data(data),
2025            MspResponse::MspDataFlashReply(data) => pack_into_packet_data(data),
2026            MspResponse::MspDataFlashRead(data) => pack_into_packet_data(data),
2027            MspResponse::MspAccTrim(data) => pack_into_packet_data(data),
2028            MspResponse::MspIdent(data) => pack_into_packet_data(data),
2029            MspResponse::MspMisc(data) => pack_into_packet_data(data),
2030            MspResponse::MspAttitude(data) => pack_into_packet_data(data),
2031            MspResponse::MspAltitude(data) => pack_into_packet_data(data),
2032            MspResponse::MspBatteryConfig(data) => pack_into_packet_data(data),
2033            MspResponse::MspVoltageMeterConfig(data) => pack_into_packet_data(data),
2034            MspResponse::MspAnalog(data) => pack_into_packet_data(data),
2035            MspResponse::MspRssiConfig(data) => pack_into_packet_data(data),
2036            MspResponse::MspVoltageMeter(data) => pack_into_packet_data(data),
2037            MspResponse::MspCurrentMeter(data) => pack_into_packet_data(data),
2038            MspResponse::MspBatteryState(data) => pack_into_packet_data(data),
2039            MspResponse::MspRcTuning(data) => pack_into_packet_data(data),
2040            MspResponse::MspRxConfig(data) => pack_into_packet_data(data),
2041            MspResponse::MspRcChannelValue(data) => pack_into_packet_data(data),
2042            MspResponse::MspRcMappedChannel(data) => pack_into_packet_data(data),
2043            MspResponse::MspFeatures(data) => pack_into_packet_data(data),
2044            MspResponse::MspMotor(data) => pack_into_packet_data(data),
2045            MspResponse::MspMotor3DConfig(data) => pack_into_packet_data(data),
2046            MspResponse::MspMotorConfig(data) => pack_into_packet_data(data),
2047            MspResponse::MspRcDeadband(data) => pack_into_packet_data(data),
2048            MspResponse::MspSensorAlignment(data) => pack_into_packet_data(data),
2049            MspResponse::MspAdvancedConfig(data) => pack_into_packet_data(data),
2050            MspResponse::MspFilterConfig(data) => pack_into_packet_data(data),
2051            MspResponse::MspPidAdvanced(data) => pack_into_packet_data(data),
2052            MspResponse::MspSensorConfig(data) => pack_into_packet_data(data),
2053            MspResponse::MspServos(data) => pack_into_packet_data(data),
2054            MspResponse::MspMixerConfig(data) => pack_into_packet_data(data),
2055            MspResponse::MspModeRange(data) => pack_into_packet_data(data),
2056            MspResponse::MspSetModeRange(data) => pack_into_packet_data(data),
2057            MspResponse::MspOsdConfig(data) => pack_into_packet_data(data),
2058            MspResponse::MspSetGetOsdConfig(data) => pack_into_packet_data(data),
2059            MspResponse::MspSetOsdLayout(data) => pack_into_packet_data(data),
2060            MspResponse::MspSetOsdLayoutItem(data) => pack_into_packet_data(data),
2061            MspResponse::MspOsdLayouts(data) => pack_into_packet_data(data),
2062            MspResponse::MspSerialSetting(data) => pack_into_packet_data(data),
2063            MspResponse::MspSettingInfoRequest(data) => pack_into_packet_data(data),
2064            MspResponse::MspSettingInfo(data) => pack_into_packet_data(data),
2065            MspResponse::MspRc(data) => pack_into_packet_data(data),
2066            MspResponse::Unknown => Err(PackingError::InvalidValue),
2067        }
2068    }
2069}
2070
2071impl From<MspResponse> for MspPacket {
2072    fn from(command: MspResponse) -> Self {
2073        MspPacket {
2074            cmd: command.command_code().to_primitive(),
2075            direction: FromFlightController,
2076            data: command.to_bytes().unwrap(), // should always be able to serialize a constructed MSPCommand
2077        }
2078    }
2079}
2080impl From<MspPacket> for MspResponse {
2081    fn from(packet: MspPacket) -> Self {
2082        let Some(cmd) = MspCommandCode::from_primitive(packet.cmd) else {
2083            return MspResponse::Unknown;
2084        };
2085        decode_response(cmd, &packet)
2086    }
2087}
2088
2089#[cfg(test)]
2090mod msp_status_tests {
2091    use super::*;
2092
2093    #[test]
2094    fn msp_status_serialization_roundtrip() {
2095        let status = MspStatus {
2096            cycle_time: 1234,
2097            i2c_errors: 5,
2098            sensors: MspStatusSensors {
2099                acc: true,
2100                baro: true,
2101                mag: false,
2102                gps: false,
2103                rangefinder: false,
2104                gyro: true,
2105                optical_flow: false,
2106            },
2107            flight_mode_flags: 0x11223344,
2108            current_pid_profile_index: 2,
2109            average_system_load_percent: 100,
2110            gyro_cycle_time: 250,
2111            extra_flight_mode_flags: vec![0xAA, 0xBB],
2112            arming_disable_flags_count: 29,
2113            arming_disable_flags: 0xDEADBEEF,
2114            config_state_flags: 1,
2115            core_temp_celsius: 42,
2116            control_rate_profile_count: 3,
2117        };
2118
2119        let data = status.to_packet_data().expect("serialize MSP_STATUS");
2120        let expected = vec![
2121            0xD2, 0x04, // cycle_time
2122            0x05, 0x00, // i2c_errors
2123            0x23, 0x00, // sensors
2124            0x44, 0x33, 0x22, 0x11, // flight_mode_flags
2125            0x02, // current_pid_profile_index
2126            0x64, 0x00, // average_system_load_percent
2127            0xFA, 0x00, // gyro_cycle_time
2128            0x02, // extra_flight_mode_flags_len
2129            0xAA, 0xBB, // extra_flight_mode_flags
2130            0x1D, // arming_disable_flags_count
2131            0xEF, 0xBE, 0xAD, 0xDE, // arming_disable_flags
2132            0x01, // config_state_flags
2133            0x2A, 0x00, // core_temp_celsius
2134            0x03, // control_rate_profile_count
2135        ];
2136
2137        assert_eq!(data.as_slice(), expected.as_slice());
2138
2139        let decoded = MspStatus::from_bytes(data.as_slice()).expect("decode MSP_STATUS");
2140        assert_eq!(decoded, status);
2141    }
2142
2143    #[test]
2144    fn msp_status_ex_serialization_roundtrip() {
2145        let status = MspStatusEx {
2146            cycle_time: 321,
2147            i2c_errors: 1,
2148            sensors: MspStatusSensors {
2149                acc: true,
2150                baro: false,
2151                mag: true,
2152                gps: false,
2153                rangefinder: false,
2154                gyro: true,
2155                optical_flow: true,
2156            },
2157            flight_mode_flags: 0xAABBCCDD,
2158            current_pid_profile_index: 1,
2159            average_system_load_percent: 250,
2160            max_profile_count: 3,
2161            current_control_rate_profile_index: 2,
2162            extra_flight_mode_flags: vec![],
2163            arming_disable_flags_count: 29,
2164            arming_disable_flags: 0,
2165            config_state_flags: 0,
2166            core_temp_celsius: 0,
2167            control_rate_profile_count: 3,
2168        };
2169
2170        let data = status.to_packet_data().expect("serialize MSP_STATUS_EX");
2171        let decoded = MspStatusEx::from_bytes(data.as_slice()).expect("decode MSP_STATUS_EX");
2172        assert_eq!(decoded, status);
2173    }
2174}
2175
2176#[cfg(test)]
2177mod tests {
2178    use super::*;
2179    #[test]
2180    fn test_mixer() {
2181        use packed_struct::prelude::*;
2182
2183        let m = MspMixerConfig {
2184            mixer_mode: MixerMode::QuadX,
2185        };
2186        assert_eq!(3, m.mixer_mode.to_primitive());
2187        let r = m.pack().unwrap();
2188        assert_eq!(3, r.as_slice()[0]);
2189    }
2190
2191    #[test]
2192    #[cfg(feature = "bincode")]
2193    fn test_command_enum_with_bincode() {
2194        let command = MspResponse::MspApiVersion(MspApiVersion {
2195            protocol_version: 1,
2196            api_version_major: 2,
2197            api_version_minor: 3,
2198        });
2199
2200        let encoded = bincode::encode_to_vec(&command, bincode::config::standard()).unwrap();
2201        let decoded: MspResponse =
2202            bincode::decode_from_slice(&encoded, bincode::config::standard())
2203                .unwrap()
2204                .0;
2205
2206        match (command, decoded) {
2207            (MspResponse::MspApiVersion(c1), MspResponse::MspApiVersion(c2)) => {
2208                assert_eq!(c1.protocol_version, c2.protocol_version);
2209                assert_eq!(c1.api_version_major, c2.api_version_major);
2210                assert_eq!(c1.api_version_minor, c2.api_version_minor);
2211            }
2212            _ => panic!("Decoded command does not match the original command"),
2213        }
2214    }
2215}