1use 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 }
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 pub altitude: i32,
507 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 pub amperage: i16,
575 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 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 pub battery_capacity: u16,
616
617 pub battery_voltage: u8,
618 pub mah_drawn: u16,
619 pub amperage: i16,
621
622 pub alerts: u8,
623 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 #[default]
688 Roll = 0,
689 Pitch = 1,
691 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, pub reverse_input: u32, }
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#[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], }
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 group_id: u16,
1336
1337 #[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 pub absolute_index: u16,
1349
1350 pub profile_id: u8,
1354 pub profile_count: u8,
1355 }
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], }
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#[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(), },
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(), },
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(), },
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(), },
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(), },
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(), },
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(), },
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(), },
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(), },
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(), },
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(), },
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(), },
1761 }
1762 }
1763}
1764
1765#[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 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(), }
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, 0x05, 0x00, 0x23, 0x00, 0x44, 0x33, 0x22, 0x11, 0x02, 0x64, 0x00, 0xFA, 0x00, 0x02, 0xAA, 0xBB, 0x1D, 0xEF, 0xBE, 0xAD, 0xDE, 0x01, 0x2A, 0x00, 0x03, ];
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}