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