2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
25 #include "common/log.h" //for MSP_SIMULATOR
28 #include "blackbox/blackbox.h"
30 #include "build/debug.h"
31 #include "build/version.h"
33 #include "common/axis.h"
34 #include "common/color.h"
35 #include "common/maths.h"
36 #include "common/streambuf.h"
37 #include "common/bitarray.h"
38 #include "common/time.h"
39 #include "common/utils.h"
40 #include "programming/global_variables.h"
41 #include "programming/pid.h"
43 #include "config/parameter_group_ids.h"
45 #include "drivers/accgyro/accgyro.h"
46 #include "drivers/compass/compass.h"
47 #include "drivers/compass/compass_msp.h"
48 #include "drivers/barometer/barometer_msp.h"
49 #include "drivers/pitotmeter/pitotmeter_msp.h"
50 #include "sensors/battery_sensor_fake.h"
51 #include "drivers/bus_i2c.h"
52 #include "drivers/display.h"
53 #include "drivers/flash.h"
54 #include "drivers/osd.h"
55 #include "drivers/osd_symbols.h"
56 #include "drivers/pwm_mapping.h"
57 #include "drivers/sdcard/sdcard.h"
58 #include "drivers/serial.h"
59 #include "drivers/system.h"
60 #include "drivers/time.h"
61 #include "drivers/timer.h"
62 #include "drivers/vtx_common.h"
64 #include "fc/fc_core.h"
65 #include "fc/config.h"
66 #include "fc/controlrate_profile.h"
67 #include "fc/fc_msp.h"
68 #include "fc/fc_msp_box.h"
69 #include "fc/firmware_update.h"
70 #include "fc/rc_adjustments.h"
71 #include "fc/rc_controls.h"
72 #include "fc/rc_modes.h"
73 #include "fc/runtime_config.h"
74 #include "fc/settings.h"
76 #include "flight/failsafe.h"
77 #include "flight/imu.h"
78 #include "flight/mixer_profile.h"
79 #include "flight/mixer.h"
80 #include "flight/pid.h"
81 #include "flight/servos.h"
82 #include "flight/ez_tune.h"
84 #include "config/config_eeprom.h"
85 #include "config/feature.h"
88 #include "io/asyncfatfs/asyncfatfs.h"
89 #include "io/flashfs.h"
91 #include "io/gps_ublox.h"
92 #include "io/opflow.h"
93 #include "io/rangefinder.h"
94 #include "io/ledstrip.h"
96 #include "io/serial.h"
97 #include "io/serial_4way.h"
99 #include "io/vtx_string.h"
100 #include "io/gps_private.h" //for MSP_SIMULATOR
101 #include "io/headtracker_msp.h"
103 #include "io/osd/custom_elements.h"
106 #include "msp/msp_protocol.h"
107 #include "msp/msp_serial.h"
109 #include "navigation/navigation.h"
110 #include "navigation/navigation_private.h" //for MSP_SIMULATOR
111 #include "navigation/navigation_pos_estimator_private.h" //for MSP_SIMULATOR
115 #include "rx/srxl2.h"
118 #include "scheduler/scheduler.h"
120 #include "sensors/boardalignment.h"
121 #include "sensors/sensors.h"
122 #include "sensors/diagnostics.h"
123 #include "sensors/battery.h"
124 #include "sensors/rangefinder.h"
125 #include "sensors/acceleration.h"
126 #include "sensors/barometer.h"
127 #include "sensors/pitotmeter.h"
128 #include "sensors/compass.h"
129 #include "sensors/gyro.h"
130 #include "sensors/opflow.h"
131 #include "sensors/temperature.h"
132 #include "sensors/esc_sensor.h"
134 #include "telemetry/telemetry.h"
136 #ifdef USE_HARDWARE_REVISION_DETECTION
137 #include "hardware_revision.h"
140 extern timeDelta_t cycleTime
; // FIXME dependency on mw.c
142 static const char * const flightControllerIdentifier
= INAV_IDENTIFIER
; // 4 UPPER CASE alpha numeric characters that identify the flight controller.
143 static const char * const boardIdentifier
= TARGET_BOARD_IDENTIFIER
;
146 extern int16_t motor_disarmed
[MAX_SUPPORTED_MOTORS
];
148 static const char pidnames
[] =
161 MSP_SDCARD_STATE_NOT_PRESENT
= 0,
162 MSP_SDCARD_STATE_FATAL
= 1,
163 MSP_SDCARD_STATE_CARD_INIT
= 2,
164 MSP_SDCARD_STATE_FS_INIT
= 3,
165 MSP_SDCARD_STATE_READY
= 4
169 MSP_SDCARD_FLAG_SUPPORTTED
= 1
173 MSP_FLASHFS_BIT_READY
= 1,
174 MSP_FLASHFS_BIT_SUPPORTED
= 2
178 MSP_PASSTHROUGH_SERIAL_ID
= 0xFD,
179 MSP_PASSTHROUGH_SERIAL_FUNCTION_ID
= 0xFE,
180 MSP_PASSTHROUGH_ESC_4WAY
= 0xFF,
181 } mspPassthroughType_e
;
183 static uint8_t mspPassthroughMode
;
184 static uint8_t mspPassthroughArgument
;
186 static serialPort_t
*mspFindPassthroughSerialPort(void)
188 serialPortUsage_t
*portUsage
= NULL
;
190 switch (mspPassthroughMode
) {
191 case MSP_PASSTHROUGH_SERIAL_ID
:
193 portUsage
= findSerialPortUsageByIdentifier(mspPassthroughArgument
);
196 case MSP_PASSTHROUGH_SERIAL_FUNCTION_ID
:
198 const serialPortConfig_t
*portConfig
= findSerialPortConfig(1 << mspPassthroughArgument
);
200 portUsage
= findSerialPortUsageByIdentifier(portConfig
->identifier
);
205 return portUsage
? portUsage
->serialPort
: NULL
;
208 static void mspSerialPassthroughFn(serialPort_t
*serialPort
)
210 serialPort_t
*passthroughPort
= mspFindPassthroughSerialPort();
211 if (passthroughPort
&& serialPort
) {
212 serialPassthrough(passthroughPort
, serialPort
, NULL
, NULL
);
216 static void mspFcSetPassthroughCommand(sbuf_t
*dst
, sbuf_t
*src
, mspPostProcessFnPtr
*mspPostProcessFn
)
218 const unsigned int dataSize
= sbufBytesRemaining(src
);
222 mspPassthroughMode
= MSP_PASSTHROUGH_ESC_4WAY
;
224 mspPassthroughMode
= sbufReadU8(src
);
225 if (!sbufReadU8Safe(&mspPassthroughArgument
, src
)) {
226 mspPassthroughArgument
= 0;
230 switch (mspPassthroughMode
) {
231 case MSP_PASSTHROUGH_SERIAL_ID
:
232 case MSP_PASSTHROUGH_SERIAL_FUNCTION_ID
:
233 if (mspFindPassthroughSerialPort()) {
234 if (mspPostProcessFn
) {
235 *mspPostProcessFn
= mspSerialPassthroughFn
;
242 #ifdef USE_SERIAL_4WAY_BLHELI_INTERFACE
243 case MSP_PASSTHROUGH_ESC_4WAY
:
244 // get channel number
245 // switch all motor lines HI
246 // reply with the count of ESC found
247 sbufWriteU8(dst
, esc4wayInit());
249 if (mspPostProcessFn
) {
250 *mspPostProcessFn
= esc4wayProcess
;
259 static void mspRebootFn(serialPort_t
*serialPort
)
265 static void serializeSDCardSummaryReply(sbuf_t
*dst
)
268 uint8_t flags
= MSP_SDCARD_FLAG_SUPPORTTED
;
271 sbufWriteU8(dst
, flags
);
273 // Merge the card and filesystem states together
274 if (!sdcard_isInserted()) {
275 state
= MSP_SDCARD_STATE_NOT_PRESENT
;
276 } else if (!sdcard_isFunctional()) {
277 state
= MSP_SDCARD_STATE_FATAL
;
279 switch (afatfs_getFilesystemState()) {
280 case AFATFS_FILESYSTEM_STATE_READY
:
281 state
= MSP_SDCARD_STATE_READY
;
283 case AFATFS_FILESYSTEM_STATE_INITIALIZATION
:
284 if (sdcard_isInitialized()) {
285 state
= MSP_SDCARD_STATE_FS_INIT
;
287 state
= MSP_SDCARD_STATE_CARD_INIT
;
290 case AFATFS_FILESYSTEM_STATE_FATAL
:
291 case AFATFS_FILESYSTEM_STATE_UNKNOWN
:
293 state
= MSP_SDCARD_STATE_FATAL
;
298 sbufWriteU8(dst
, state
);
299 sbufWriteU8(dst
, afatfs_getLastError());
300 // Write free space and total space in kilobytes
301 sbufWriteU32(dst
, afatfs_getContiguousFreeSpace() / 1024);
302 sbufWriteU32(dst
, sdcard_getMetadata()->numBlocks
/ 2); // Block size is half a kilobyte
307 sbufWriteU32(dst
, 0);
308 sbufWriteU32(dst
, 0);
312 static void serializeDataflashSummaryReply(sbuf_t
*dst
)
315 const flashGeometry_t
*geometry
= flashGetGeometry();
316 sbufWriteU8(dst
, flashIsReady() ? 1 : 0);
317 sbufWriteU32(dst
, geometry
->sectors
);
318 sbufWriteU32(dst
, geometry
->totalSize
);
319 sbufWriteU32(dst
, flashfsGetOffset()); // Effectively the current number of bytes stored on the volume
322 sbufWriteU32(dst
, 0);
323 sbufWriteU32(dst
, 0);
324 sbufWriteU32(dst
, 0);
329 static void serializeDataflashReadReply(sbuf_t
*dst
, uint32_t address
, uint16_t size
)
331 // Check how much bytes we can read
332 const int bytesRemainingInBuf
= sbufBytesRemaining(dst
);
333 uint16_t readLen
= (size
> bytesRemainingInBuf
) ? bytesRemainingInBuf
: size
;
335 // size will be lower than that requested if we reach end of volume
336 const uint32_t flashfsSize
= flashfsGetSize();
337 if (readLen
> flashfsSize
- address
) {
338 // truncate the request
339 readLen
= flashfsSize
- address
;
343 sbufWriteU32(dst
, address
);
345 // Read into streambuf directly
346 const int bytesRead
= flashfsReadAbs(address
, sbufPtr(dst
), readLen
);
347 sbufAdvance(dst
, bytesRead
);
352 * Returns true if the command was processd, false otherwise.
353 * May set mspPostProcessFunc to a function to be called once the command has been processed
355 static bool mspFcProcessOutCommand(uint16_t cmdMSP
, sbuf_t
*dst
, mspPostProcessFnPtr
*mspPostProcessFn
)
358 case MSP_API_VERSION
:
359 sbufWriteU8(dst
, MSP_PROTOCOL_VERSION
);
360 sbufWriteU8(dst
, API_VERSION_MAJOR
);
361 sbufWriteU8(dst
, API_VERSION_MINOR
);
365 sbufWriteData(dst
, flightControllerIdentifier
, FLIGHT_CONTROLLER_IDENTIFIER_LENGTH
);
369 sbufWriteU8(dst
, FC_VERSION_MAJOR
);
370 sbufWriteU8(dst
, FC_VERSION_MINOR
);
371 sbufWriteU8(dst
, FC_VERSION_PATCH_LEVEL
);
376 sbufWriteData(dst
, boardIdentifier
, BOARD_IDENTIFIER_LENGTH
);
377 #ifdef USE_HARDWARE_REVISION_DETECTION
378 sbufWriteU16(dst
, hardwareRevision
);
380 sbufWriteU16(dst
, 0); // No other build targets currently have hardware revision detection.
382 // OSD support (for BF compatibility):
384 // 1 = OSD slave (not supported in INAV)
385 // 2 = OSD chip on board
391 // Board communication capabilities (uint8)
392 // Bit 0: 1 iff the board has VCP
393 // Bit 1: 1 iff the board supports software serial
394 uint8_t commCapabilities
= 0;
396 commCapabilities
|= 1 << 0;
398 #if defined(USE_SOFTSERIAL1) || defined(USE_SOFTSERIAL2)
399 commCapabilities
|= 1 << 1;
401 sbufWriteU8(dst
, commCapabilities
);
403 sbufWriteU8(dst
, strlen(targetName
));
404 sbufWriteData(dst
, targetName
, strlen(targetName
));
409 sbufWriteData(dst
, buildDate
, BUILD_DATE_LENGTH
);
410 sbufWriteData(dst
, buildTime
, BUILD_TIME_LENGTH
);
411 sbufWriteData(dst
, shortGitRevision
, GIT_SHORT_REVISION_LENGTH
);
414 case MSP_SENSOR_STATUS
:
415 sbufWriteU8(dst
, isHardwareHealthy() ? 1 : 0);
416 sbufWriteU8(dst
, getHwGyroStatus());
417 sbufWriteU8(dst
, getHwAccelerometerStatus());
418 sbufWriteU8(dst
, getHwCompassStatus());
419 sbufWriteU8(dst
, getHwBarometerStatus());
420 sbufWriteU8(dst
, getHwGPSStatus());
421 sbufWriteU8(dst
, getHwRangefinderStatus());
422 sbufWriteU8(dst
, getHwPitotmeterStatus());
423 sbufWriteU8(dst
, getHwOpticalFlowStatus());
426 case MSP_ACTIVEBOXES
:
428 boxBitmask_t mspBoxModeFlags
;
429 packBoxModeFlags(&mspBoxModeFlags
);
430 sbufWriteData(dst
, &mspBoxModeFlags
, sizeof(mspBoxModeFlags
));
437 boxBitmask_t mspBoxModeFlags
;
438 packBoxModeFlags(&mspBoxModeFlags
);
440 sbufWriteU16(dst
, (uint16_t)cycleTime
);
442 sbufWriteU16(dst
, i2cGetErrorCounter());
444 sbufWriteU16(dst
, 0);
446 sbufWriteU16(dst
, packSensorStatus());
447 sbufWriteData(dst
, &mspBoxModeFlags
, 4);
448 sbufWriteU8(dst
, getConfigProfile());
450 if (cmdMSP
== MSP_STATUS_EX
) {
451 sbufWriteU16(dst
, averageSystemLoadPercent
);
452 sbufWriteU16(dst
, armingFlags
);
453 sbufWriteU8(dst
, accGetCalibrationAxisFlags());
458 case MSP2_INAV_STATUS
:
460 // Preserves full arming flags and box modes
461 boxBitmask_t mspBoxModeFlags
;
462 packBoxModeFlags(&mspBoxModeFlags
);
464 sbufWriteU16(dst
, (uint16_t)cycleTime
);
466 sbufWriteU16(dst
, i2cGetErrorCounter());
468 sbufWriteU16(dst
, 0);
470 sbufWriteU16(dst
, packSensorStatus());
471 sbufWriteU16(dst
, averageSystemLoadPercent
);
472 sbufWriteU8(dst
, (getConfigBatteryProfile() << 4) | getConfigProfile());
473 sbufWriteU32(dst
, armingFlags
);
474 sbufWriteData(dst
, &mspBoxModeFlags
, sizeof(mspBoxModeFlags
));
475 sbufWriteU8(dst
, getConfigMixerProfile());
481 for (int i
= 0; i
< 3; i
++) {
482 sbufWriteU16(dst
, (int16_t)lrintf(acc
.accADCf
[i
] * 512));
484 for (int i
= 0; i
< 3; i
++) {
485 sbufWriteU16(dst
, gyroRateDps(i
));
487 for (int i
= 0; i
< 3; i
++) {
489 sbufWriteU16(dst
, lrintf(mag
.magADC
[i
]));
491 sbufWriteU16(dst
, 0);
498 sbufWriteData(dst
, &servo
, MAX_SUPPORTED_SERVOS
* 2);
500 case MSP_SERVO_CONFIGURATIONS
:
501 for (int i
= 0; i
< MAX_SUPPORTED_SERVOS
; i
++) {
502 sbufWriteU16(dst
, servoParams(i
)->min
);
503 sbufWriteU16(dst
, servoParams(i
)->max
);
504 sbufWriteU16(dst
, servoParams(i
)->middle
);
505 sbufWriteU8(dst
, servoParams(i
)->rate
);
508 sbufWriteU8(dst
, 255); // used to be forwardFromChannel, not used anymore, send 0xff for compatibility reasons
509 sbufWriteU32(dst
, 0); //Input reversing is not required since it can be done on mixer level
512 case MSP2_INAV_SERVO_CONFIG
:
513 for (int i
= 0; i
< MAX_SUPPORTED_SERVOS
; i
++) {
514 sbufWriteU16(dst
, servoParams(i
)->min
);
515 sbufWriteU16(dst
, servoParams(i
)->max
);
516 sbufWriteU16(dst
, servoParams(i
)->middle
);
517 sbufWriteU8(dst
, servoParams(i
)->rate
);
520 case MSP_SERVO_MIX_RULES
:
521 for (int i
= 0; i
< MAX_SERVO_RULES
; i
++) {
522 sbufWriteU8(dst
, customServoMixers(i
)->targetChannel
);
523 sbufWriteU8(dst
, customServoMixers(i
)->inputSource
);
524 sbufWriteU16(dst
, customServoMixers(i
)->rate
);
525 sbufWriteU8(dst
, customServoMixers(i
)->speed
);
527 sbufWriteU8(dst
, 100);
531 case MSP2_INAV_SERVO_MIXER
:
532 for (int i
= 0; i
< MAX_SERVO_RULES
; i
++) {
533 sbufWriteU8(dst
, customServoMixers(i
)->targetChannel
);
534 sbufWriteU8(dst
, customServoMixers(i
)->inputSource
);
535 sbufWriteU16(dst
, customServoMixers(i
)->rate
);
536 sbufWriteU8(dst
, customServoMixers(i
)->speed
);
537 #ifdef USE_PROGRAMMING_FRAMEWORK
538 sbufWriteU8(dst
, customServoMixers(i
)->conditionId
);
540 sbufWriteU8(dst
, -1);
543 if(MAX_MIXER_PROFILE_COUNT
==1) break;
544 for (int i
= 0; i
< MAX_SERVO_RULES
; i
++) {
545 sbufWriteU8(dst
, mixerServoMixersByIndex(nextMixerProfileIndex
)[i
].targetChannel
);
546 sbufWriteU8(dst
, mixerServoMixersByIndex(nextMixerProfileIndex
)[i
].inputSource
);
547 sbufWriteU16(dst
, mixerServoMixersByIndex(nextMixerProfileIndex
)[i
].rate
);
548 sbufWriteU8(dst
, mixerServoMixersByIndex(nextMixerProfileIndex
)[i
].speed
);
549 #ifdef USE_PROGRAMMING_FRAMEWORK
550 sbufWriteU8(dst
, mixerServoMixersByIndex(nextMixerProfileIndex
)[i
].conditionId
);
552 sbufWriteU8(dst
, -1);
556 #ifdef USE_PROGRAMMING_FRAMEWORK
557 case MSP2_INAV_LOGIC_CONDITIONS
:
558 for (int i
= 0; i
< MAX_LOGIC_CONDITIONS
; i
++) {
559 sbufWriteU8(dst
, logicConditions(i
)->enabled
);
560 sbufWriteU8(dst
, logicConditions(i
)->activatorId
);
561 sbufWriteU8(dst
, logicConditions(i
)->operation
);
562 sbufWriteU8(dst
, logicConditions(i
)->operandA
.type
);
563 sbufWriteU32(dst
, logicConditions(i
)->operandA
.value
);
564 sbufWriteU8(dst
, logicConditions(i
)->operandB
.type
);
565 sbufWriteU32(dst
, logicConditions(i
)->operandB
.value
);
566 sbufWriteU8(dst
, logicConditions(i
)->flags
);
569 case MSP2_INAV_LOGIC_CONDITIONS_STATUS
:
570 for (int i
= 0; i
< MAX_LOGIC_CONDITIONS
; i
++) {
571 sbufWriteU32(dst
, logicConditionGetValue(i
));
574 case MSP2_INAV_GVAR_STATUS
:
575 for (int i
= 0; i
< MAX_GLOBAL_VARIABLES
; i
++) {
576 sbufWriteU32(dst
, gvGet(i
));
579 case MSP2_INAV_PROGRAMMING_PID
:
580 for (int i
= 0; i
< MAX_PROGRAMMING_PID_COUNT
; i
++) {
581 sbufWriteU8(dst
, programmingPids(i
)->enabled
);
582 sbufWriteU8(dst
, programmingPids(i
)->setpoint
.type
);
583 sbufWriteU32(dst
, programmingPids(i
)->setpoint
.value
);
584 sbufWriteU8(dst
, programmingPids(i
)->measurement
.type
);
585 sbufWriteU32(dst
, programmingPids(i
)->measurement
.value
);
586 sbufWriteU16(dst
, programmingPids(i
)->gains
.P
);
587 sbufWriteU16(dst
, programmingPids(i
)->gains
.I
);
588 sbufWriteU16(dst
, programmingPids(i
)->gains
.D
);
589 sbufWriteU16(dst
, programmingPids(i
)->gains
.FF
);
592 case MSP2_INAV_PROGRAMMING_PID_STATUS
:
593 for (int i
= 0; i
< MAX_PROGRAMMING_PID_COUNT
; i
++) {
594 sbufWriteU32(dst
, programmingPidGetOutput(i
));
598 case MSP2_COMMON_MOTOR_MIXER
:
599 for (uint8_t i
= 0; i
< MAX_SUPPORTED_MOTORS
; i
++) {
600 sbufWriteU16(dst
, constrainf(primaryMotorMixer(i
)->throttle
+ 2.0f
, 0.0f
, 4.0f
) * 1000);
601 sbufWriteU16(dst
, constrainf(primaryMotorMixer(i
)->roll
+ 2.0f
, 0.0f
, 4.0f
) * 1000);
602 sbufWriteU16(dst
, constrainf(primaryMotorMixer(i
)->pitch
+ 2.0f
, 0.0f
, 4.0f
) * 1000);
603 sbufWriteU16(dst
, constrainf(primaryMotorMixer(i
)->yaw
+ 2.0f
, 0.0f
, 4.0f
) * 1000);
605 if (MAX_MIXER_PROFILE_COUNT
==1) break;
606 for (uint8_t i
= 0; i
< MAX_SUPPORTED_MOTORS
; i
++) {
607 sbufWriteU16(dst
, constrainf(mixerMotorMixersByIndex(nextMixerProfileIndex
)[i
].throttle
+ 2.0f
, 0.0f
, 4.0f
) * 1000);
608 sbufWriteU16(dst
, constrainf(mixerMotorMixersByIndex(nextMixerProfileIndex
)[i
].roll
+ 2.0f
, 0.0f
, 4.0f
) * 1000);
609 sbufWriteU16(dst
, constrainf(mixerMotorMixersByIndex(nextMixerProfileIndex
)[i
].pitch
+ 2.0f
, 0.0f
, 4.0f
) * 1000);
610 sbufWriteU16(dst
, constrainf(mixerMotorMixersByIndex(nextMixerProfileIndex
)[i
].yaw
+ 2.0f
, 0.0f
, 4.0f
) * 1000);
615 for (unsigned i
= 0; i
< 8; i
++) {
616 sbufWriteU16(dst
, i
< MAX_SUPPORTED_MOTORS
? motor
[i
] : 0);
621 for (int i
= 0; i
< rxRuntimeConfig
.channelCount
; i
++) {
622 sbufWriteU16(dst
, rxGetChannelValue(i
));
627 sbufWriteU16(dst
, attitude
.values
.roll
);
628 sbufWriteU16(dst
, attitude
.values
.pitch
);
629 sbufWriteU16(dst
, DECIDEGREES_TO_DEGREES(attitude
.values
.yaw
));
633 sbufWriteU32(dst
, lrintf(getEstimatedActualPosition(Z
)));
634 sbufWriteU16(dst
, lrintf(getEstimatedActualVelocity(Z
)));
635 #if defined(USE_BARO)
636 sbufWriteU32(dst
, baroGetLatestAltitude());
638 sbufWriteU32(dst
, 0);
642 case MSP_SONAR_ALTITUDE
:
643 #ifdef USE_RANGEFINDER
644 sbufWriteU32(dst
, rangefinderGetLatestAltitude());
646 sbufWriteU32(dst
, 0);
650 case MSP2_INAV_OPTICAL_FLOW
:
652 sbufWriteU8(dst
, opflow
.rawQuality
);
653 sbufWriteU16(dst
, RADIANS_TO_DEGREES(opflow
.flowRate
[X
]));
654 sbufWriteU16(dst
, RADIANS_TO_DEGREES(opflow
.flowRate
[Y
]));
655 sbufWriteU16(dst
, RADIANS_TO_DEGREES(opflow
.bodyRate
[X
]));
656 sbufWriteU16(dst
, RADIANS_TO_DEGREES(opflow
.bodyRate
[Y
]));
659 sbufWriteU16(dst
, 0);
660 sbufWriteU16(dst
, 0);
661 sbufWriteU16(dst
, 0);
662 sbufWriteU16(dst
, 0);
667 sbufWriteU8(dst
, (uint8_t)constrain(getBatteryVoltage() / 10, 0, 255));
668 sbufWriteU16(dst
, (uint16_t)constrain(getMAhDrawn(), 0, 0xFFFF)); // milliamp hours drawn from battery
669 sbufWriteU16(dst
, getRSSI());
670 sbufWriteU16(dst
, (int16_t)constrain(getAmperage(), -0x8000, 0x7FFF)); // send amperage in 0.01 A steps, range is -320A to 320A
673 case MSP2_INAV_ANALOG
:
674 // Bit 1: battery full, Bit 2: use capacity threshold, Bit 3-4: battery state, Bit 5-8: battery cell count
675 sbufWriteU8(dst
, batteryWasFullWhenPluggedIn() | (batteryUsesCapacityThresholds() << 1) | (getBatteryState() << 2) | (getBatteryCellCount() << 4));
676 sbufWriteU16(dst
, getBatteryVoltage());
677 sbufWriteU16(dst
, getAmperage()); // send amperage in 0.01 A steps
678 sbufWriteU32(dst
, getPower()); // power draw
679 sbufWriteU32(dst
, getMAhDrawn()); // milliamp hours drawn from battery
680 sbufWriteU32(dst
, getMWhDrawn()); // milliWatt hours drawn from battery
681 sbufWriteU32(dst
, getBatteryRemainingCapacity());
682 sbufWriteU8(dst
, calculateBatteryPercentage());
683 sbufWriteU16(dst
, getRSSI());
687 sbufWriteU16(dst
, gyroConfig()->looptime
);
691 sbufWriteU8(dst
, 100); //rcRate8 kept for compatibity reasons, this setting is no longer used
692 sbufWriteU8(dst
, currentControlRateProfile
->stabilized
.rcExpo8
);
693 for (int i
= 0 ; i
< 3; i
++) {
694 sbufWriteU8(dst
, currentControlRateProfile
->stabilized
.rates
[i
]); // R,P,Y see flight_dynamics_index_t
696 sbufWriteU8(dst
, currentControlRateProfile
->throttle
.dynPID
);
697 sbufWriteU8(dst
, currentControlRateProfile
->throttle
.rcMid8
);
698 sbufWriteU8(dst
, currentControlRateProfile
->throttle
.rcExpo8
);
699 sbufWriteU16(dst
, currentControlRateProfile
->throttle
.pa_breakpoint
);
700 sbufWriteU8(dst
, currentControlRateProfile
->stabilized
.rcYawExpo8
);
703 case MSP2_INAV_RATE_PROFILE
:
705 sbufWriteU8(dst
, currentControlRateProfile
->throttle
.rcMid8
);
706 sbufWriteU8(dst
, currentControlRateProfile
->throttle
.rcExpo8
);
707 sbufWriteU8(dst
, currentControlRateProfile
->throttle
.dynPID
);
708 sbufWriteU16(dst
, currentControlRateProfile
->throttle
.pa_breakpoint
);
711 sbufWriteU8(dst
, currentControlRateProfile
->stabilized
.rcExpo8
);
712 sbufWriteU8(dst
, currentControlRateProfile
->stabilized
.rcYawExpo8
);
713 for (uint8_t i
= 0 ; i
< 3; ++i
) {
714 sbufWriteU8(dst
, currentControlRateProfile
->stabilized
.rates
[i
]); // R,P,Y see flight_dynamics_index_t
718 sbufWriteU8(dst
, currentControlRateProfile
->manual
.rcExpo8
);
719 sbufWriteU8(dst
, currentControlRateProfile
->manual
.rcYawExpo8
);
720 for (uint8_t i
= 0 ; i
< 3; ++i
) {
721 sbufWriteU8(dst
, currentControlRateProfile
->manual
.rates
[i
]); // R,P,Y see flight_dynamics_index_t
726 for (int i
= 0; i
< PID_ITEM_COUNT
; i
++) {
727 sbufWriteU8(dst
, constrain(pidBank()->pid
[i
].P
, 0, 255));
728 sbufWriteU8(dst
, constrain(pidBank()->pid
[i
].I
, 0, 255));
729 sbufWriteU8(dst
, constrain(pidBank()->pid
[i
].D
, 0, 255));
730 sbufWriteU8(dst
, constrain(pidBank()->pid
[i
].FF
, 0, 255));
738 for (const char *c
= pidnames
; *c
; c
++) {
739 sbufWriteU8(dst
, *c
);
743 case MSP_MODE_RANGES
:
744 for (int i
= 0; i
< MAX_MODE_ACTIVATION_CONDITION_COUNT
; i
++) {
745 const modeActivationCondition_t
*mac
= modeActivationConditions(i
);
746 const box_t
*box
= findBoxByActiveBoxId(mac
->modeId
);
747 sbufWriteU8(dst
, box
? box
->permanentId
: 0);
748 sbufWriteU8(dst
, mac
->auxChannelIndex
);
749 sbufWriteU8(dst
, mac
->range
.startStep
);
750 sbufWriteU8(dst
, mac
->range
.endStep
);
754 case MSP_ADJUSTMENT_RANGES
:
755 for (int i
= 0; i
< MAX_ADJUSTMENT_RANGE_COUNT
; i
++) {
756 const adjustmentRange_t
*adjRange
= adjustmentRanges(i
);
757 sbufWriteU8(dst
, adjRange
->adjustmentIndex
);
758 sbufWriteU8(dst
, adjRange
->auxChannelIndex
);
759 sbufWriteU8(dst
, adjRange
->range
.startStep
);
760 sbufWriteU8(dst
, adjRange
->range
.endStep
);
761 sbufWriteU8(dst
, adjRange
->adjustmentFunction
);
762 sbufWriteU8(dst
, adjRange
->auxSwitchChannelIndex
);
767 if (!serializeBoxNamesReply(dst
)) {
773 serializeBoxReply(dst
);
777 sbufWriteU16(dst
, PWM_RANGE_MIDDLE
);
779 sbufWriteU16(dst
, 0); // Was min_throttle
780 sbufWriteU16(dst
, getMaxThrottle());
781 sbufWriteU16(dst
, motorConfig()->mincommand
);
783 sbufWriteU16(dst
, currentBatteryProfile
->failsafe_throttle
);
786 sbufWriteU8(dst
, gpsConfig()->provider
); // gps_type
787 sbufWriteU8(dst
, 0); // TODO gps_baudrate (an index, cleanflight uses a uint32_t
788 sbufWriteU8(dst
, gpsConfig()->sbasMode
); // gps_ubx_sbas
790 sbufWriteU8(dst
, 0); // gps_type
791 sbufWriteU8(dst
, 0); // TODO gps_baudrate (an index, cleanflight uses a uint32_t
792 sbufWriteU8(dst
, 0); // gps_ubx_sbas
794 sbufWriteU8(dst
, 0); // multiwiiCurrentMeterOutput
795 sbufWriteU8(dst
, rxConfig()->rssi_channel
);
799 sbufWriteU16(dst
, compassConfig()->mag_declination
/ 10);
801 sbufWriteU16(dst
, 0);
805 sbufWriteU8(dst
, batteryMetersConfig()->voltage
.scale
/ 10);
806 sbufWriteU8(dst
, currentBatteryProfile
->voltage
.cellMin
/ 10);
807 sbufWriteU8(dst
, currentBatteryProfile
->voltage
.cellMax
/ 10);
808 sbufWriteU8(dst
, currentBatteryProfile
->voltage
.cellWarning
/ 10);
818 sbufWriteU16(dst
, PWM_RANGE_MIDDLE
);
820 sbufWriteU16(dst
, 0); //Was min_throttle
821 sbufWriteU16(dst
, getMaxThrottle());
822 sbufWriteU16(dst
, motorConfig()->mincommand
);
824 sbufWriteU16(dst
, currentBatteryProfile
->failsafe_throttle
);
827 sbufWriteU8(dst
, gpsConfig()->provider
); // gps_type
828 sbufWriteU8(dst
, 0); // TODO gps_baudrate (an index, cleanflight uses a uint32_t
829 sbufWriteU8(dst
, gpsConfig()->sbasMode
); // gps_ubx_sbas
831 sbufWriteU8(dst
, 0); // gps_type
832 sbufWriteU8(dst
, 0); // TODO gps_baudrate (an index, cleanflight uses a uint32_t
833 sbufWriteU8(dst
, 0); // gps_ubx_sbas
835 sbufWriteU8(dst
, rxConfig()->rssi_channel
);
838 sbufWriteU16(dst
, compassConfig()->mag_declination
/ 10);
840 sbufWriteU16(dst
, 0);
844 sbufWriteU16(dst
, batteryMetersConfig()->voltage
.scale
);
845 sbufWriteU8(dst
, batteryMetersConfig()->voltageSource
);
846 sbufWriteU8(dst
, currentBatteryProfile
->cells
);
847 sbufWriteU16(dst
, currentBatteryProfile
->voltage
.cellDetect
);
848 sbufWriteU16(dst
, currentBatteryProfile
->voltage
.cellMin
);
849 sbufWriteU16(dst
, currentBatteryProfile
->voltage
.cellMax
);
850 sbufWriteU16(dst
, currentBatteryProfile
->voltage
.cellWarning
);
852 sbufWriteU16(dst
, 0);
855 sbufWriteU16(dst
, 0);
856 sbufWriteU16(dst
, 0);
857 sbufWriteU16(dst
, 0);
858 sbufWriteU16(dst
, 0);
861 sbufWriteU32(dst
, currentBatteryProfile
->capacity
.value
);
862 sbufWriteU32(dst
, currentBatteryProfile
->capacity
.warning
);
863 sbufWriteU32(dst
, currentBatteryProfile
->capacity
.critical
);
864 sbufWriteU8(dst
, batteryMetersConfig()->capacity_unit
);
867 case MSP2_INAV_MISC2
:
869 sbufWriteU32(dst
, micros() / 1000000); // On time (seconds)
870 sbufWriteU32(dst
, getFlightTime()); // Flight time (seconds)
873 sbufWriteU8(dst
, getThrottlePercent(true)); // Throttle Percent
874 sbufWriteU8(dst
, navigationIsControllingThrottle() ? 1 : 0); // Auto Throttle Flag (0 or 1)
878 case MSP2_INAV_BATTERY_CONFIG
:
880 sbufWriteU16(dst
, batteryMetersConfig()->voltage
.scale
);
881 sbufWriteU8(dst
, batteryMetersConfig()->voltageSource
);
882 sbufWriteU8(dst
, currentBatteryProfile
->cells
);
883 sbufWriteU16(dst
, currentBatteryProfile
->voltage
.cellDetect
);
884 sbufWriteU16(dst
, currentBatteryProfile
->voltage
.cellMin
);
885 sbufWriteU16(dst
, currentBatteryProfile
->voltage
.cellMax
);
886 sbufWriteU16(dst
, currentBatteryProfile
->voltage
.cellWarning
);
888 sbufWriteU16(dst
, 0);
891 sbufWriteU16(dst
, 0);
892 sbufWriteU16(dst
, 0);
893 sbufWriteU16(dst
, 0);
894 sbufWriteU16(dst
, 0);
897 sbufWriteU16(dst
, batteryMetersConfig()->current
.offset
);
898 sbufWriteU16(dst
, batteryMetersConfig()->current
.scale
);
900 sbufWriteU32(dst
, currentBatteryProfile
->capacity
.value
);
901 sbufWriteU32(dst
, currentBatteryProfile
->capacity
.warning
);
902 sbufWriteU32(dst
, currentBatteryProfile
->capacity
.critical
);
903 sbufWriteU8(dst
, batteryMetersConfig()->capacity_unit
);
908 sbufWriteU8(dst
, gpsSol
.fixType
);
909 sbufWriteU8(dst
, gpsSol
.numSat
);
910 sbufWriteU32(dst
, gpsSol
.llh
.lat
);
911 sbufWriteU32(dst
, gpsSol
.llh
.lon
);
912 sbufWriteU16(dst
, gpsSol
.llh
.alt
/100); // meters
913 sbufWriteU16(dst
, gpsSol
.groundSpeed
);
914 sbufWriteU16(dst
, gpsSol
.groundCourse
);
915 sbufWriteU16(dst
, gpsSol
.hdop
);
919 sbufWriteU16(dst
, GPS_distanceToHome
);
920 sbufWriteU16(dst
, GPS_directionToHome
);
921 sbufWriteU8(dst
, gpsSol
.flags
.gpsHeartbeat
? 1 : 0);
924 sbufWriteU8(dst
, NAV_Status
.mode
);
925 sbufWriteU8(dst
, NAV_Status
.state
);
926 sbufWriteU8(dst
, NAV_Status
.activeWpAction
);
927 sbufWriteU8(dst
, NAV_Status
.activeWpNumber
);
928 sbufWriteU8(dst
, NAV_Status
.error
);
929 //sbufWriteU16(dst, (int16_t)(target_bearing/100));
930 sbufWriteU16(dst
, getHeadingHoldTarget());
935 /* Compatibility stub - return zero SVs */
941 sbufWriteU8(dst
, gpsSol
.hdop
/ 100);
942 sbufWriteU8(dst
, gpsSol
.hdop
/ 100);
945 case MSP_GPSSTATISTICS
:
946 sbufWriteU16(dst
, gpsStats
.lastMessageDt
);
947 sbufWriteU32(dst
, gpsStats
.errors
);
948 sbufWriteU32(dst
, gpsStats
.timeouts
);
949 sbufWriteU32(dst
, gpsStats
.packetCount
);
950 sbufWriteU16(dst
, gpsSol
.hdop
);
951 sbufWriteU16(dst
, gpsSol
.eph
);
952 sbufWriteU16(dst
, gpsSol
.epv
);
955 case MSP2_ADSB_VEHICLE_LIST
:
957 sbufWriteU8(dst
, MAX_ADSB_VEHICLES
);
958 sbufWriteU8(dst
, ADSB_CALL_SIGN_MAX_LENGTH
);
959 sbufWriteU32(dst
, getAdsbStatus()->vehiclesMessagesTotal
);
960 sbufWriteU32(dst
, getAdsbStatus()->heartbeatMessagesTotal
);
962 for(uint8_t i
= 0; i
< MAX_ADSB_VEHICLES
; i
++){
964 adsbVehicle_t
*adsbVehicle
= findVehicle(i
);
966 for(uint8_t ii
= 0; ii
< ADSB_CALL_SIGN_MAX_LENGTH
; ii
++){
967 sbufWriteU8(dst
, adsbVehicle
->vehicleValues
.callsign
[ii
]);
970 sbufWriteU32(dst
, adsbVehicle
->vehicleValues
.icao
);
971 sbufWriteU32(dst
, adsbVehicle
->vehicleValues
.lat
);
972 sbufWriteU32(dst
, adsbVehicle
->vehicleValues
.lon
);
973 sbufWriteU32(dst
, adsbVehicle
->vehicleValues
.alt
);
974 sbufWriteU16(dst
, (uint16_t)CENTIDEGREES_TO_DEGREES(adsbVehicle
->vehicleValues
.heading
));
975 sbufWriteU8(dst
, adsbVehicle
->vehicleValues
.tslc
);
976 sbufWriteU8(dst
, adsbVehicle
->vehicleValues
.emitterType
);
977 sbufWriteU8(dst
, adsbVehicle
->ttl
);
982 sbufWriteU32(dst
, 0);
983 sbufWriteU32(dst
, 0);
987 // output some useful QA statistics
988 // debug[x] = ((hse_value / 1000000) * 1000) + (SystemCoreClock / 1000000); // XX0YY [crystal clock : core clock]
990 for (int i
= 0; i
< 4; i
++) {
991 sbufWriteU16(dst
, debug
[i
]); // 4 variables are here for general monitoring purpose
995 case MSP2_INAV_DEBUG
:
996 for (int i
= 0; i
< DEBUG32_VALUE_COUNT
; i
++) {
997 sbufWriteU32(dst
, debug
[i
]); // 8 variables are here for general monitoring purpose
1002 sbufWriteU32(dst
, U_ID_0
);
1003 sbufWriteU32(dst
, U_ID_1
);
1004 sbufWriteU32(dst
, U_ID_2
);
1008 sbufWriteU32(dst
, featureMask());
1011 case MSP_BOARD_ALIGNMENT
:
1012 sbufWriteU16(dst
, boardAlignment()->rollDeciDegrees
);
1013 sbufWriteU16(dst
, boardAlignment()->pitchDeciDegrees
);
1014 sbufWriteU16(dst
, boardAlignment()->yawDeciDegrees
);
1017 case MSP_VOLTAGE_METER_CONFIG
:
1019 sbufWriteU8(dst
, batteryMetersConfig()->voltage
.scale
/ 10);
1020 sbufWriteU8(dst
, currentBatteryProfile
->voltage
.cellMin
/ 10);
1021 sbufWriteU8(dst
, currentBatteryProfile
->voltage
.cellMax
/ 10);
1022 sbufWriteU8(dst
, currentBatteryProfile
->voltage
.cellWarning
/ 10);
1024 sbufWriteU8(dst
, 0);
1025 sbufWriteU8(dst
, 0);
1026 sbufWriteU8(dst
, 0);
1027 sbufWriteU8(dst
, 0);
1031 case MSP_CURRENT_METER_CONFIG
:
1032 sbufWriteU16(dst
, batteryMetersConfig()->current
.scale
);
1033 sbufWriteU16(dst
, batteryMetersConfig()->current
.offset
);
1034 sbufWriteU8(dst
, batteryMetersConfig()->current
.type
);
1035 sbufWriteU16(dst
, constrain(currentBatteryProfile
->capacity
.value
, 0, 0xFFFF));
1039 sbufWriteU8(dst
, 3); // mixerMode no longer supported, send 3 (QuadX) as fallback
1044 sbufWriteU8(dst
, rxConfig()->serialrx_provider
);
1045 sbufWriteU16(dst
, rxConfig()->maxcheck
);
1046 sbufWriteU16(dst
, PWM_RANGE_MIDDLE
);
1047 sbufWriteU16(dst
, rxConfig()->mincheck
);
1048 #ifdef USE_SPEKTRUM_BIND
1049 sbufWriteU8(dst
, rxConfig()->spektrum_sat_bind
);
1051 sbufWriteU8(dst
, 0);
1053 sbufWriteU16(dst
, rxConfig()->rx_min_usec
);
1054 sbufWriteU16(dst
, rxConfig()->rx_max_usec
);
1055 sbufWriteU8(dst
, 0); // for compatibility with betaflight (rcInterpolation)
1056 sbufWriteU8(dst
, 0); // for compatibility with betaflight (rcInterpolationInterval)
1057 sbufWriteU16(dst
, 0); // for compatibility with betaflight (airModeActivateThreshold)
1058 sbufWriteU8(dst
, 0);
1059 sbufWriteU32(dst
, 0);
1060 sbufWriteU8(dst
, 0);
1061 sbufWriteU8(dst
, 0); // for compatibility with betaflight (fpvCamAngleDegrees)
1062 sbufWriteU8(dst
, rxConfig()->receiverType
);
1065 case MSP_FAILSAFE_CONFIG
:
1066 sbufWriteU8(dst
, failsafeConfig()->failsafe_delay
);
1067 sbufWriteU8(dst
, failsafeConfig()->failsafe_off_delay
);
1068 sbufWriteU16(dst
, currentBatteryProfile
->failsafe_throttle
);
1069 sbufWriteU8(dst
, 0); // was failsafe_kill_switch
1070 sbufWriteU16(dst
, failsafeConfig()->failsafe_throttle_low_delay
);
1071 sbufWriteU8(dst
, failsafeConfig()->failsafe_procedure
);
1072 sbufWriteU8(dst
, failsafeConfig()->failsafe_recovery_delay
);
1073 sbufWriteU16(dst
, failsafeConfig()->failsafe_fw_roll_angle
);
1074 sbufWriteU16(dst
, failsafeConfig()->failsafe_fw_pitch_angle
);
1075 sbufWriteU16(dst
, failsafeConfig()->failsafe_fw_yaw_rate
);
1076 sbufWriteU16(dst
, failsafeConfig()->failsafe_stick_motion_threshold
);
1077 sbufWriteU16(dst
, failsafeConfig()->failsafe_min_distance
);
1078 sbufWriteU8(dst
, failsafeConfig()->failsafe_min_distance_procedure
);
1081 case MSP_RSSI_CONFIG
:
1082 sbufWriteU8(dst
, rxConfig()->rssi_channel
);
1086 sbufWriteData(dst
, rxConfig()->rcmap
, MAX_MAPPABLE_RX_INPUTS
);
1089 case MSP2_COMMON_SERIAL_CONFIG
:
1090 for (int i
= 0; i
< SERIAL_PORT_COUNT
; i
++) {
1091 if (!serialIsPortAvailable(serialConfig()->portConfigs
[i
].identifier
)) {
1094 sbufWriteU8(dst
, serialConfig()->portConfigs
[i
].identifier
);
1095 sbufWriteU32(dst
, serialConfig()->portConfigs
[i
].functionMask
);
1096 sbufWriteU8(dst
, serialConfig()->portConfigs
[i
].msp_baudrateIndex
);
1097 sbufWriteU8(dst
, serialConfig()->portConfigs
[i
].gps_baudrateIndex
);
1098 sbufWriteU8(dst
, serialConfig()->portConfigs
[i
].telemetry_baudrateIndex
);
1099 sbufWriteU8(dst
, serialConfig()->portConfigs
[i
].peripheral_baudrateIndex
);
1103 #ifdef USE_LED_STRIP
1104 case MSP_LED_COLORS
:
1105 for (int i
= 0; i
< LED_CONFIGURABLE_COLOR_COUNT
; i
++) {
1106 const hsvColor_t
*color
= &ledStripConfig()->colors
[i
];
1107 sbufWriteU16(dst
, color
->h
);
1108 sbufWriteU8(dst
, color
->s
);
1109 sbufWriteU8(dst
, color
->v
);
1113 case MSP_LED_STRIP_CONFIG
:
1114 for (int i
= 0; i
< LED_MAX_STRIP_LENGTH
; i
++) {
1115 const ledConfig_t
*ledConfig
= &ledStripConfig()->ledConfigs
[i
];
1117 uint32_t legacyLedConfig
= ledConfig
->led_position
;
1119 legacyLedConfig
|= ledConfig
->led_function
<< shiftCount
;
1121 legacyLedConfig
|= (ledConfig
->led_overlay
& 0x3F) << (shiftCount
);
1123 legacyLedConfig
|= (ledConfig
->led_color
) << (shiftCount
);
1125 legacyLedConfig
|= (ledConfig
->led_direction
) << (shiftCount
);
1127 legacyLedConfig
|= (ledConfig
->led_params
) << (shiftCount
);
1129 sbufWriteU32(dst
, legacyLedConfig
);
1133 case MSP2_INAV_LED_STRIP_CONFIG_EX
:
1134 for (int i
= 0; i
< LED_MAX_STRIP_LENGTH
; i
++) {
1135 const ledConfig_t
*ledConfig
= &ledStripConfig()->ledConfigs
[i
];
1136 sbufWriteDataSafe(dst
, ledConfig
, sizeof(ledConfig_t
));
1141 case MSP_LED_STRIP_MODECOLOR
:
1142 for (int i
= 0; i
< LED_MODE_COUNT
; i
++) {
1143 for (int j
= 0; j
< LED_DIRECTION_COUNT
; j
++) {
1144 sbufWriteU8(dst
, i
);
1145 sbufWriteU8(dst
, j
);
1146 sbufWriteU8(dst
, ledStripConfig()->modeColors
[i
].color
[j
]);
1150 for (int j
= 0; j
< LED_SPECIAL_COLOR_COUNT
; j
++) {
1151 sbufWriteU8(dst
, LED_MODE_COUNT
);
1152 sbufWriteU8(dst
, j
);
1153 sbufWriteU8(dst
, ledStripConfig()->specialColors
.color
[j
]);
1158 case MSP_DATAFLASH_SUMMARY
:
1159 serializeDataflashSummaryReply(dst
);
1162 case MSP_BLACKBOX_CONFIG
:
1163 sbufWriteU8(dst
, 0); // API no longer supported
1164 sbufWriteU8(dst
, 0);
1165 sbufWriteU8(dst
, 0);
1166 sbufWriteU8(dst
, 0);
1169 case MSP2_BLACKBOX_CONFIG
:
1171 sbufWriteU8(dst
, 1); //Blackbox supported
1172 sbufWriteU8(dst
, blackboxConfig()->device
);
1173 sbufWriteU16(dst
, blackboxConfig()->rate_num
);
1174 sbufWriteU16(dst
, blackboxConfig()->rate_denom
);
1175 sbufWriteU32(dst
,blackboxConfig()->includeFlags
);
1177 sbufWriteU8(dst
, 0); // Blackbox not supported
1178 sbufWriteU8(dst
, 0);
1179 sbufWriteU16(dst
, 0);
1180 sbufWriteU16(dst
, 0);
1184 case MSP_SDCARD_SUMMARY
:
1185 serializeSDCardSummaryReply(dst
);
1188 #if defined (USE_DJI_HD_OSD) || defined (USE_MSP_DISPLAYPORT)
1189 case MSP_BATTERY_STATE
:
1190 // Battery characteristics
1191 sbufWriteU8(dst
, constrain(getBatteryCellCount(), 0, 255));
1192 sbufWriteU16(dst
, currentBatteryProfile
->capacity
.value
);
1195 sbufWriteU8(dst
, constrain(getBatteryVoltage() / 10, 0, 255)); // in 0.1V steps
1196 sbufWriteU16(dst
, constrain(getMAhDrawn(), 0, 0xFFFF));
1197 sbufWriteU16(dst
, constrain(getAmperage(), -0x8000, 0x7FFF));
1199 // Battery alerts - used values match Betaflight's/DJI's
1200 sbufWriteU8(dst
, getBatteryState());
1202 // Additional battery voltage field (in 0.01V steps)
1203 sbufWriteU16(dst
, getBatteryVoltage());
1207 case MSP_OSD_CONFIG
:
1209 sbufWriteU8(dst
, OSD_DRIVER_MAX7456
); // OSD supported
1210 // send video system (AUTO/PAL/NTSC)
1211 sbufWriteU8(dst
, osdConfig()->video_system
);
1212 sbufWriteU8(dst
, osdConfig()->units
);
1213 sbufWriteU8(dst
, osdConfig()->rssi_alarm
);
1214 sbufWriteU16(dst
, currentBatteryProfile
->capacity
.warning
);
1215 sbufWriteU16(dst
, osdConfig()->time_alarm
);
1216 sbufWriteU16(dst
, osdConfig()->alt_alarm
);
1217 sbufWriteU16(dst
, osdConfig()->dist_alarm
);
1218 sbufWriteU16(dst
, osdConfig()->neg_alt_alarm
);
1219 for (int i
= 0; i
< OSD_ITEM_COUNT
; i
++) {
1220 sbufWriteU16(dst
, osdLayoutsConfig()->item_pos
[0][i
]);
1223 sbufWriteU8(dst
, OSD_DRIVER_NONE
); // OSD not supported
1228 sbufWriteU16(dst
, reversibleMotorsConfig()->deadband_low
);
1229 sbufWriteU16(dst
, reversibleMotorsConfig()->deadband_high
);
1230 sbufWriteU16(dst
, reversibleMotorsConfig()->neutral
);
1233 case MSP_RC_DEADBAND
:
1234 sbufWriteU8(dst
, rcControlsConfig()->deadband
);
1235 sbufWriteU8(dst
, rcControlsConfig()->yaw_deadband
);
1236 sbufWriteU8(dst
, rcControlsConfig()->alt_hold_deadband
);
1237 sbufWriteU16(dst
, rcControlsConfig()->mid_throttle_deadband
);
1240 case MSP_SENSOR_ALIGNMENT
:
1241 sbufWriteU8(dst
, 0); // was gyroConfig()->gyro_align
1242 sbufWriteU8(dst
, 0); // was accelerometerConfig()->acc_align
1244 sbufWriteU8(dst
, compassConfig()->mag_align
);
1246 sbufWriteU8(dst
, 0);
1249 sbufWriteU8(dst
, opticalFlowConfig()->opflow_align
);
1251 sbufWriteU8(dst
, 0);
1255 case MSP_ADVANCED_CONFIG
:
1256 sbufWriteU8(dst
, 1); // gyroConfig()->gyroSyncDenominator
1257 sbufWriteU8(dst
, 1); // BF: masterConfig.pid_process_denom
1258 sbufWriteU8(dst
, 1); // BF: motorConfig()->useUnsyncedPwm
1259 sbufWriteU8(dst
, motorConfig()->motorPwmProtocol
);
1260 sbufWriteU16(dst
, motorConfig()->motorPwmRate
);
1261 sbufWriteU16(dst
, servoConfig()->servoPwmRate
);
1262 sbufWriteU8(dst
, 0);
1265 case MSP_FILTER_CONFIG
:
1266 sbufWriteU8(dst
, gyroConfig()->gyro_main_lpf_hz
);
1267 sbufWriteU16(dst
, pidProfile()->dterm_lpf_hz
);
1268 sbufWriteU16(dst
, pidProfile()->yaw_lpf_hz
);
1269 sbufWriteU16(dst
, 0); //Was gyroConfig()->gyro_notch_hz
1270 sbufWriteU16(dst
, 1); //Was gyroConfig()->gyro_notch_cutoff
1271 sbufWriteU16(dst
, 0); //BF: pidProfile()->dterm_notch_hz
1272 sbufWriteU16(dst
, 1); //pidProfile()->dterm_notch_cutoff
1274 sbufWriteU16(dst
, 0); //BF: masterConfig.gyro_soft_notch_hz_2
1275 sbufWriteU16(dst
, 1); //BF: masterConfig.gyro_soft_notch_cutoff_2
1277 sbufWriteU16(dst
, accelerometerConfig()->acc_notch_hz
);
1278 sbufWriteU16(dst
, accelerometerConfig()->acc_notch_cutoff
);
1280 sbufWriteU16(dst
, 0); //Was gyroConfig()->gyro_stage2_lowpass_hz
1283 case MSP_PID_ADVANCED
:
1284 sbufWriteU16(dst
, 0); // pidProfile()->rollPitchItermIgnoreRate
1285 sbufWriteU16(dst
, 0); // pidProfile()->yawItermIgnoreRate
1286 sbufWriteU16(dst
, 0); //pidProfile()->yaw_p_limit
1287 sbufWriteU8(dst
, 0); //BF: pidProfile()->deltaMethod
1288 sbufWriteU8(dst
, 0); //BF: pidProfile()->vbatPidCompensation
1289 sbufWriteU8(dst
, 0); //BF: pidProfile()->setpointRelaxRatio
1290 sbufWriteU8(dst
, 0);
1291 sbufWriteU16(dst
, 0); //Was pidsum limit
1292 sbufWriteU8(dst
, 0); //BF: pidProfile()->itermThrottleGain
1295 * To keep compatibility on MSP frame length level with Betaflight, axis axisAccelerationLimitYaw
1296 * limit will be sent and received in [dps / 10]
1298 sbufWriteU16(dst
, constrain(pidProfile()->axisAccelerationLimitRollPitch
/ 10, 0, 65535));
1299 sbufWriteU16(dst
, constrain(pidProfile()->axisAccelerationLimitYaw
/ 10, 0, 65535));
1303 sbufWriteU8(dst
, 0); //Legacy, no longer in use async processing value
1304 sbufWriteU16(dst
, 0); //Legacy, no longer in use async processing value
1305 sbufWriteU16(dst
, 0); //Legacy, no longer in use async processing value
1306 sbufWriteU8(dst
, pidProfile()->heading_hold_rate_limit
);
1307 sbufWriteU8(dst
, HEADING_HOLD_ERROR_LPF_FREQ
);
1308 sbufWriteU16(dst
, 0);
1309 sbufWriteU8(dst
, GYRO_LPF_256HZ
);
1310 sbufWriteU8(dst
, accelerometerConfig()->acc_lpf_hz
);
1311 sbufWriteU8(dst
, 0); //reserved
1312 sbufWriteU8(dst
, 0); //reserved
1313 sbufWriteU8(dst
, 0); //reserved
1314 sbufWriteU8(dst
, 0); //reserved
1317 case MSP_SENSOR_CONFIG
:
1318 sbufWriteU8(dst
, accelerometerConfig()->acc_hardware
);
1320 sbufWriteU8(dst
, barometerConfig()->baro_hardware
);
1322 sbufWriteU8(dst
, 0);
1325 sbufWriteU8(dst
, compassConfig()->mag_hardware
);
1327 sbufWriteU8(dst
, 0);
1330 sbufWriteU8(dst
, pitotmeterConfig()->pitot_hardware
);
1332 sbufWriteU8(dst
, 0);
1334 #ifdef USE_RANGEFINDER
1335 sbufWriteU8(dst
, rangefinderConfig()->rangefinder_hardware
);
1337 sbufWriteU8(dst
, 0);
1340 sbufWriteU8(dst
, opticalFlowConfig()->opflow_hardware
);
1342 sbufWriteU8(dst
, 0);
1346 case MSP_NAV_POSHOLD
:
1347 sbufWriteU8(dst
, navConfig()->general
.flags
.user_control_mode
);
1348 sbufWriteU16(dst
, navConfig()->general
.max_auto_speed
);
1349 sbufWriteU16(dst
, mixerConfig()->platformType
== PLATFORM_AIRPLANE
? navConfig()->fw
.max_auto_climb_rate
: navConfig()->mc
.max_auto_climb_rate
);
1350 sbufWriteU16(dst
, navConfig()->general
.max_manual_speed
);
1351 sbufWriteU16(dst
, mixerConfig()->platformType
== PLATFORM_AIRPLANE
? navConfig()->fw
.max_manual_climb_rate
: navConfig()->mc
.max_manual_climb_rate
);
1352 sbufWriteU8(dst
, navConfig()->mc
.max_bank_angle
);
1353 sbufWriteU8(dst
, navConfig()->mc
.althold_throttle_type
);
1354 sbufWriteU16(dst
, currentBatteryProfile
->nav
.mc
.hover_throttle
);
1357 case MSP_RTH_AND_LAND_CONFIG
:
1358 sbufWriteU16(dst
, navConfig()->general
.min_rth_distance
);
1359 sbufWriteU8(dst
, navConfig()->general
.flags
.rth_climb_first
);
1360 sbufWriteU8(dst
, navConfig()->general
.flags
.rth_climb_ignore_emerg
);
1361 sbufWriteU8(dst
, navConfig()->general
.flags
.rth_tail_first
);
1362 sbufWriteU8(dst
, navConfig()->general
.flags
.rth_allow_landing
);
1363 sbufWriteU8(dst
, navConfig()->general
.flags
.rth_alt_control_mode
);
1364 sbufWriteU16(dst
, navConfig()->general
.rth_abort_threshold
);
1365 sbufWriteU16(dst
, navConfig()->general
.rth_altitude
);
1366 sbufWriteU16(dst
, navConfig()->general
.land_minalt_vspd
);
1367 sbufWriteU16(dst
, navConfig()->general
.land_maxalt_vspd
);
1368 sbufWriteU16(dst
, navConfig()->general
.land_slowdown_minalt
);
1369 sbufWriteU16(dst
, navConfig()->general
.land_slowdown_maxalt
);
1370 sbufWriteU16(dst
, navConfig()->general
.emerg_descent_rate
);
1374 sbufWriteU16(dst
, currentBatteryProfile
->nav
.fw
.cruise_throttle
);
1375 sbufWriteU16(dst
, currentBatteryProfile
->nav
.fw
.min_throttle
);
1376 sbufWriteU16(dst
, currentBatteryProfile
->nav
.fw
.max_throttle
);
1377 sbufWriteU8(dst
, navConfig()->fw
.max_bank_angle
);
1378 sbufWriteU8(dst
, navConfig()->fw
.max_climb_angle
);
1379 sbufWriteU8(dst
, navConfig()->fw
.max_dive_angle
);
1380 sbufWriteU8(dst
, currentBatteryProfile
->nav
.fw
.pitch_to_throttle
);
1381 sbufWriteU16(dst
, navConfig()->fw
.loiter_radius
);
1384 case MSP_CALIBRATION_DATA
:
1385 sbufWriteU8(dst
, accGetCalibrationAxisFlags());
1386 sbufWriteU16(dst
, accelerometerConfig()->accZero
.raw
[X
]);
1387 sbufWriteU16(dst
, accelerometerConfig()->accZero
.raw
[Y
]);
1388 sbufWriteU16(dst
, accelerometerConfig()->accZero
.raw
[Z
]);
1389 sbufWriteU16(dst
, accelerometerConfig()->accGain
.raw
[X
]);
1390 sbufWriteU16(dst
, accelerometerConfig()->accGain
.raw
[Y
]);
1391 sbufWriteU16(dst
, accelerometerConfig()->accGain
.raw
[Z
]);
1394 sbufWriteU16(dst
, compassConfig()->magZero
.raw
[X
]);
1395 sbufWriteU16(dst
, compassConfig()->magZero
.raw
[Y
]);
1396 sbufWriteU16(dst
, compassConfig()->magZero
.raw
[Z
]);
1398 sbufWriteU16(dst
, 0);
1399 sbufWriteU16(dst
, 0);
1400 sbufWriteU16(dst
, 0);
1404 sbufWriteU16(dst
, opticalFlowConfig()->opflow_scale
* 256);
1406 sbufWriteU16(dst
, 0);
1410 sbufWriteU16(dst
, compassConfig()->magGain
[X
]);
1411 sbufWriteU16(dst
, compassConfig()->magGain
[Y
]);
1412 sbufWriteU16(dst
, compassConfig()->magGain
[Z
]);
1414 sbufWriteU16(dst
, 0);
1415 sbufWriteU16(dst
, 0);
1416 sbufWriteU16(dst
, 0);
1421 case MSP_POSITION_ESTIMATION_CONFIG
:
1423 sbufWriteU16(dst
, positionEstimationConfig()->w_z_baro_p
* 100); // inav_w_z_baro_p float as value * 100
1424 sbufWriteU16(dst
, positionEstimationConfig()->w_z_gps_p
* 100); // 2 inav_w_z_gps_p float as value * 100
1425 sbufWriteU16(dst
, positionEstimationConfig()->w_z_gps_v
* 100); // 2 inav_w_z_gps_v float as value * 100
1426 sbufWriteU16(dst
, positionEstimationConfig()->w_xy_gps_p
* 100); // 2 inav_w_xy_gps_p float as value * 100
1427 sbufWriteU16(dst
, positionEstimationConfig()->w_xy_gps_v
* 100); // 2 inav_w_xy_gps_v float as value * 100
1428 sbufWriteU8(dst
, gpsConfigMutable()->gpsMinSats
); // 1
1429 sbufWriteU8(dst
, 1); // 1 inav_use_gps_velned ON/OFF
1434 if (!ARMING_FLAG(ARMED
)) {
1435 if (mspPostProcessFn
) {
1436 *mspPostProcessFn
= mspRebootFn
;
1441 case MSP_WP_GETINFO
:
1442 sbufWriteU8(dst
, 0); // Reserved for waypoint capabilities
1443 sbufWriteU8(dst
, NAV_MAX_WAYPOINTS
); // Maximum number of waypoints supported
1444 sbufWriteU8(dst
, isWaypointListValid()); // Is current mission valid
1445 sbufWriteU8(dst
, getWaypointCount()); // Number of waypoints in current mission
1449 sbufWriteU8(dst
, getRSSISource());
1450 uint8_t rtcDateTimeIsSet
= 0;
1452 if (rtcGetDateTime(&dt
)) {
1453 rtcDateTimeIsSet
= 1;
1455 sbufWriteU8(dst
, rtcDateTimeIsSet
);
1460 int32_t seconds
= 0;
1461 uint16_t millis
= 0;
1464 seconds
= rtcTimeGetSeconds(&t
);
1465 millis
= rtcTimeGetMillis(&t
);
1467 sbufWriteU32(dst
, (uint32_t)seconds
);
1468 sbufWriteU16(dst
, millis
);
1472 case MSP_VTX_CONFIG
:
1473 #ifdef USE_VTX_CONTROL
1475 vtxDevice_t
*vtxDevice
= vtxCommonDevice();
1478 uint8_t deviceType
= vtxCommonGetDeviceType(vtxDevice
);
1480 // Return band, channel and power from vtxSettingsConfig_t
1481 // since the VTX might be configured but temporarily offline.
1482 uint8_t pitmode
= 0;
1483 vtxCommonGetPitMode(vtxDevice
, &pitmode
);
1485 sbufWriteU8(dst
, deviceType
);
1486 sbufWriteU8(dst
, vtxSettingsConfig()->band
);
1487 sbufWriteU8(dst
, vtxSettingsConfig()->channel
);
1488 sbufWriteU8(dst
, vtxSettingsConfig()->power
);
1489 sbufWriteU8(dst
, pitmode
);
1491 // Betaflight < 4 doesn't send these fields
1492 sbufWriteU8(dst
, vtxCommonDeviceIsReady(vtxDevice
) ? 1 : 0);
1493 sbufWriteU8(dst
, vtxSettingsConfig()->lowPowerDisarm
);
1494 // future extensions here...
1497 sbufWriteU8(dst
, VTXDEV_UNKNOWN
); // no VTX configured
1501 sbufWriteU8(dst
, VTXDEV_UNKNOWN
); // no VTX configured
1507 const char *name
= systemConfig()->craftName
;
1509 sbufWriteU8(dst
, *name
++);
1514 case MSP2_COMMON_TZ
:
1515 sbufWriteU16(dst
, (uint16_t)timeConfig()->tz_offset
);
1516 sbufWriteU8(dst
, (uint8_t)timeConfig()->tz_automatic_dst
);
1519 case MSP2_INAV_AIR_SPEED
:
1521 sbufWriteU32(dst
, getAirspeedEstimate());
1523 sbufWriteU32(dst
, 0);
1527 case MSP2_INAV_MIXER
:
1528 sbufWriteU8(dst
, mixerConfig()->motorDirectionInverted
);
1529 sbufWriteU8(dst
, 0);
1530 sbufWriteU8(dst
, mixerConfig()->motorstopOnLow
);
1531 sbufWriteU8(dst
, mixerConfig()->platformType
);
1532 sbufWriteU8(dst
, mixerConfig()->hasFlaps
);
1533 sbufWriteU16(dst
, mixerConfig()->appliedMixerPreset
);
1534 sbufWriteU8(dst
, MAX_SUPPORTED_MOTORS
);
1535 sbufWriteU8(dst
, MAX_SUPPORTED_SERVOS
);
1538 #if defined(USE_OSD)
1539 case MSP2_INAV_OSD_ALARMS
:
1540 sbufWriteU8(dst
, osdConfig()->rssi_alarm
);
1541 sbufWriteU16(dst
, osdConfig()->time_alarm
);
1542 sbufWriteU16(dst
, osdConfig()->alt_alarm
);
1543 sbufWriteU16(dst
, osdConfig()->dist_alarm
);
1544 sbufWriteU16(dst
, osdConfig()->neg_alt_alarm
);
1545 sbufWriteU16(dst
, osdConfig()->gforce_alarm
* 1000);
1546 sbufWriteU16(dst
, (int16_t)(osdConfig()->gforce_axis_alarm_min
* 1000));
1547 sbufWriteU16(dst
, (int16_t)(osdConfig()->gforce_axis_alarm_max
* 1000));
1548 sbufWriteU8(dst
, osdConfig()->current_alarm
);
1549 sbufWriteU16(dst
, osdConfig()->imu_temp_alarm_min
);
1550 sbufWriteU16(dst
, osdConfig()->imu_temp_alarm_max
);
1552 sbufWriteU16(dst
, osdConfig()->baro_temp_alarm_min
);
1553 sbufWriteU16(dst
, osdConfig()->baro_temp_alarm_max
);
1555 sbufWriteU16(dst
, 0);
1556 sbufWriteU16(dst
, 0);
1559 sbufWriteU16(dst
, osdConfig()->adsb_distance_warning
);
1560 sbufWriteU16(dst
, osdConfig()->adsb_distance_alert
);
1562 sbufWriteU16(dst
, 0);
1563 sbufWriteU16(dst
, 0);
1567 case MSP2_INAV_OSD_PREFERENCES
:
1568 sbufWriteU8(dst
, osdConfig()->video_system
);
1569 sbufWriteU8(dst
, osdConfig()->main_voltage_decimals
);
1570 sbufWriteU8(dst
, osdConfig()->ahi_reverse_roll
);
1571 sbufWriteU8(dst
, osdConfig()->crosshairs_style
);
1572 sbufWriteU8(dst
, osdConfig()->left_sidebar_scroll
);
1573 sbufWriteU8(dst
, osdConfig()->right_sidebar_scroll
);
1574 sbufWriteU8(dst
, osdConfig()->sidebar_scroll_arrows
);
1575 sbufWriteU8(dst
, osdConfig()->units
);
1576 sbufWriteU8(dst
, osdConfig()->stats_energy_unit
);
1581 case MSP2_INAV_OUTPUT_MAPPING
:
1582 for (uint8_t i
= 0; i
< timerHardwareCount
; ++i
)
1583 if (!(timerHardware
[i
].usageFlags
& (TIM_USE_PPM
| TIM_USE_PWM
))) {
1584 sbufWriteU8(dst
, timerHardware
[i
].usageFlags
);
1588 // Obsolete, replaced by MSP2_INAV_OUTPUT_MAPPING_EXT2
1589 case MSP2_INAV_OUTPUT_MAPPING_EXT
:
1590 for (uint8_t i
= 0; i
< timerHardwareCount
; ++i
)
1591 if (!(timerHardware
[i
].usageFlags
& (TIM_USE_PPM
| TIM_USE_PWM
))) {
1592 #if defined(SITL_BUILD)
1593 sbufWriteU8(dst
, i
);
1595 sbufWriteU8(dst
, timer2id(timerHardware
[i
].tim
));
1597 // usageFlags is u32, cuts out the higher 24bits
1598 sbufWriteU8(dst
, timerHardware
[i
].usageFlags
);
1601 case MSP2_INAV_OUTPUT_MAPPING_EXT2
:
1603 #if !defined(SITL_BUILD) && defined(WS2811_PIN)
1604 ioTag_t led_tag
= IO_TAG(WS2811_PIN
);
1606 for (uint8_t i
= 0; i
< timerHardwareCount
; ++i
)
1608 if (!(timerHardware
[i
].usageFlags
& (TIM_USE_PPM
| TIM_USE_PWM
))) {
1609 #if defined(SITL_BUILD)
1610 sbufWriteU8(dst
, i
);
1612 sbufWriteU8(dst
, timer2id(timerHardware
[i
].tim
));
1614 sbufWriteU32(dst
, timerHardware
[i
].usageFlags
);
1615 #if defined(SITL_BUILD) || !defined(WS2811_PIN)
1616 sbufWriteU8(dst
, 0);
1618 // Extra label to help identify repurposed PINs.
1619 // Eventually, we can try to add more labels for PPM pins, etc.
1620 sbufWriteU8(dst
, timerHardware
[i
].tag
== led_tag
? PIN_LABEL_LED
: PIN_LABEL_NONE
);
1627 case MSP2_INAV_MC_BRAKING
:
1628 #ifdef USE_MR_BRAKING_MODE
1629 sbufWriteU16(dst
, navConfig()->mc
.braking_speed_threshold
);
1630 sbufWriteU16(dst
, navConfig()->mc
.braking_disengage_speed
);
1631 sbufWriteU16(dst
, navConfig()->mc
.braking_timeout
);
1632 sbufWriteU8(dst
, navConfig()->mc
.braking_boost_factor
);
1633 sbufWriteU16(dst
, navConfig()->mc
.braking_boost_timeout
);
1634 sbufWriteU16(dst
, navConfig()->mc
.braking_boost_speed_threshold
);
1635 sbufWriteU16(dst
, navConfig()->mc
.braking_boost_disengage_speed
);
1636 sbufWriteU8(dst
, navConfig()->mc
.braking_bank_angle
);
1640 #ifdef USE_TEMPERATURE_SENSOR
1641 case MSP2_INAV_TEMP_SENSOR_CONFIG
:
1642 for (uint8_t index
= 0; index
< MAX_TEMP_SENSORS
; ++index
) {
1643 const tempSensorConfig_t
*sensorConfig
= tempSensorConfig(index
);
1644 sbufWriteU8(dst
, sensorConfig
->type
);
1645 for (uint8_t addrIndex
= 0; addrIndex
< 8; ++addrIndex
)
1646 sbufWriteU8(dst
, ((uint8_t *)&sensorConfig
->address
)[addrIndex
]);
1647 sbufWriteU16(dst
, sensorConfig
->alarm_min
);
1648 sbufWriteU16(dst
, sensorConfig
->alarm_max
);
1649 sbufWriteU8(dst
, sensorConfig
->osdSymbol
);
1650 for (uint8_t labelIndex
= 0; labelIndex
< TEMPERATURE_LABEL_LEN
; ++labelIndex
)
1651 sbufWriteU8(dst
, sensorConfig
->label
[labelIndex
]);
1656 #ifdef USE_TEMPERATURE_SENSOR
1657 case MSP2_INAV_TEMPERATURES
:
1658 for (uint8_t index
= 0; index
< MAX_TEMP_SENSORS
; ++index
) {
1659 int16_t temperature
;
1660 bool valid
= getSensorTemperature(index
, &temperature
);
1661 sbufWriteU16(dst
, valid
? temperature
: -1000);
1666 #ifdef USE_ESC_SENSOR
1667 case MSP2_INAV_ESC_RPM
:
1669 uint8_t motorCount
= getMotorCount();
1671 for (uint8_t i
= 0; i
< motorCount
; i
++){
1672 const escSensorData_t
*escState
= getEscTelemetry(i
); //Get ESC telemetry
1673 sbufWriteU32(dst
, escState
->rpm
);
1681 case MSP2_INAV_EZ_TUNE
:
1683 sbufWriteU8(dst
, ezTune()->enabled
);
1684 sbufWriteU16(dst
, ezTune()->filterHz
);
1685 sbufWriteU8(dst
, ezTune()->axisRatio
);
1686 sbufWriteU8(dst
, ezTune()->response
);
1687 sbufWriteU8(dst
, ezTune()->damping
);
1688 sbufWriteU8(dst
, ezTune()->stability
);
1689 sbufWriteU8(dst
, ezTune()->aggressiveness
);
1690 sbufWriteU8(dst
, ezTune()->rate
);
1691 sbufWriteU8(dst
, ezTune()->expo
);
1692 sbufWriteU8(dst
, ezTune()->snappiness
);
1697 #ifdef USE_RATE_DYNAMICS
1699 case MSP2_INAV_RATE_DYNAMICS
:
1701 sbufWriteU8(dst
, currentControlRateProfile
->rateDynamics
.sensitivityCenter
);
1702 sbufWriteU8(dst
, currentControlRateProfile
->rateDynamics
.sensitivityEnd
);
1703 sbufWriteU8(dst
, currentControlRateProfile
->rateDynamics
.correctionCenter
);
1704 sbufWriteU8(dst
, currentControlRateProfile
->rateDynamics
.correctionEnd
);
1705 sbufWriteU8(dst
, currentControlRateProfile
->rateDynamics
.weightCenter
);
1706 sbufWriteU8(dst
, currentControlRateProfile
->rateDynamics
.weightEnd
);
1711 #ifdef USE_PROGRAMMING_FRAMEWORK
1712 case MSP2_INAV_CUSTOM_OSD_ELEMENTS
:
1713 sbufWriteU8(dst
, MAX_CUSTOM_ELEMENTS
);
1714 sbufWriteU8(dst
, OSD_CUSTOM_ELEMENT_TEXT_SIZE
- 1);
1716 for (int i
= 0; i
< MAX_CUSTOM_ELEMENTS
; i
++) {
1717 const osdCustomElement_t
*customElement
= osdCustomElements(i
);
1718 for (int ii
= 0; ii
< CUSTOM_ELEMENTS_PARTS
; ii
++) {
1719 sbufWriteU8(dst
, customElement
->part
[ii
].type
);
1720 sbufWriteU16(dst
, customElement
->part
[ii
].value
);
1722 sbufWriteU8(dst
, customElement
->visibility
.type
);
1723 sbufWriteU16(dst
, customElement
->visibility
.value
);
1724 for (int ii
= 0; ii
< OSD_CUSTOM_ELEMENT_TEXT_SIZE
- 1; ii
++) {
1725 sbufWriteU8(dst
, customElement
->osdCustomElementText
[ii
]);
1736 #ifdef USE_SAFE_HOME
1737 static mspResult_e
mspFcSafeHomeOutCommand(sbuf_t
*dst
, sbuf_t
*src
)
1739 const uint8_t safe_home_no
= sbufReadU8(src
); // get the home number
1740 if(safe_home_no
< MAX_SAFE_HOMES
) {
1741 sbufWriteU8(dst
, safe_home_no
);
1742 sbufWriteU8(dst
, safeHomeConfig(safe_home_no
)->enabled
);
1743 sbufWriteU32(dst
, safeHomeConfig(safe_home_no
)->lat
);
1744 sbufWriteU32(dst
, safeHomeConfig(safe_home_no
)->lon
);
1745 return MSP_RESULT_ACK
;
1747 return MSP_RESULT_ERROR
;
1752 #ifdef USE_FW_AUTOLAND
1753 static mspResult_e
mspFwApproachOutCommand(sbuf_t
*dst
, sbuf_t
*src
)
1755 const uint8_t idx
= sbufReadU8(src
);
1756 if(idx
< MAX_FW_LAND_APPOACH_SETTINGS
) {
1757 sbufWriteU8(dst
, idx
);
1758 sbufWriteU32(dst
, fwAutolandApproachConfig(idx
)->approachAlt
);
1759 sbufWriteU32(dst
, fwAutolandApproachConfig(idx
)->landAlt
);
1760 sbufWriteU8(dst
, fwAutolandApproachConfig(idx
)->approachDirection
);
1761 sbufWriteU16(dst
, fwAutolandApproachConfig(idx
)->landApproachHeading1
);
1762 sbufWriteU16(dst
, fwAutolandApproachConfig(idx
)->landApproachHeading2
);
1763 sbufWriteU8(dst
, fwAutolandApproachConfig(idx
)->isSeaLevelRef
);
1764 return MSP_RESULT_ACK
;
1766 return MSP_RESULT_ERROR
;
1771 static mspResult_e
mspFcLogicConditionCommand(sbuf_t
*dst
, sbuf_t
*src
) {
1772 const uint8_t idx
= sbufReadU8(src
);
1773 if (idx
< MAX_LOGIC_CONDITIONS
) {
1774 sbufWriteU8(dst
, logicConditions(idx
)->enabled
);
1775 sbufWriteU8(dst
, logicConditions(idx
)->activatorId
);
1776 sbufWriteU8(dst
, logicConditions(idx
)->operation
);
1777 sbufWriteU8(dst
, logicConditions(idx
)->operandA
.type
);
1778 sbufWriteU32(dst
, logicConditions(idx
)->operandA
.value
);
1779 sbufWriteU8(dst
, logicConditions(idx
)->operandB
.type
);
1780 sbufWriteU32(dst
, logicConditions(idx
)->operandB
.value
);
1781 sbufWriteU8(dst
, logicConditions(idx
)->flags
);
1782 return MSP_RESULT_ACK
;
1784 return MSP_RESULT_ERROR
;
1788 static void mspFcWaypointOutCommand(sbuf_t
*dst
, sbuf_t
*src
)
1790 const uint8_t msp_wp_no
= sbufReadU8(src
); // get the wp number
1791 navWaypoint_t msp_wp
;
1792 getWaypoint(msp_wp_no
, &msp_wp
);
1793 sbufWriteU8(dst
, msp_wp_no
); // wp_no
1794 sbufWriteU8(dst
, msp_wp
.action
); // action (WAYPOINT)
1795 sbufWriteU32(dst
, msp_wp
.lat
); // lat
1796 sbufWriteU32(dst
, msp_wp
.lon
); // lon
1797 sbufWriteU32(dst
, msp_wp
.alt
); // altitude (cm)
1798 sbufWriteU16(dst
, msp_wp
.p1
); // P1
1799 sbufWriteU16(dst
, msp_wp
.p2
); // P2
1800 sbufWriteU16(dst
, msp_wp
.p3
); // P3
1801 sbufWriteU8(dst
, msp_wp
.flag
); // flags
1805 static void mspFcDataFlashReadCommand(sbuf_t
*dst
, sbuf_t
*src
)
1807 const unsigned int dataSize
= sbufBytesRemaining(src
);
1808 uint16_t readLength
;
1810 const uint32_t readAddress
= sbufReadU32(src
);
1813 // uint32_t - address to read from
1814 // uint16_t - size of block to read (optional)
1815 if (dataSize
== sizeof(uint32_t) + sizeof(uint16_t)) {
1816 readLength
= sbufReadU16(src
);
1822 serializeDataflashReadReply(dst
, readAddress
, readLength
);
1826 static mspResult_e
mspFcProcessInCommand(uint16_t cmdMSP
, sbuf_t
*src
)
1831 const unsigned int dataSize
= sbufBytesRemaining(src
);
1834 case MSP_SELECT_SETTING
:
1835 if (sbufReadU8Safe(&tmp_u8
, src
) && (!ARMING_FLAG(ARMED
)))
1836 setConfigProfileAndWriteEEPROM(tmp_u8
);
1838 return MSP_RESULT_ERROR
;
1842 if (sbufReadU16Safe(&tmp_u16
, src
))
1843 updateHeadingHoldTarget(tmp_u16
);
1845 return MSP_RESULT_ERROR
;
1849 case MSP_SET_RAW_RC
:
1851 uint8_t channelCount
= dataSize
/ sizeof(uint16_t);
1852 if ((channelCount
> MAX_SUPPORTED_RC_CHANNEL_COUNT
) || (dataSize
> channelCount
* sizeof(uint16_t))) {
1853 return MSP_RESULT_ERROR
;
1855 uint16_t frame
[MAX_SUPPORTED_RC_CHANNEL_COUNT
];
1856 for (int i
= 0; i
< channelCount
; i
++) {
1857 frame
[i
] = sbufReadU16(src
);
1859 rxMspFrameReceive(frame
, channelCount
);
1865 case MSP_SET_LOOP_TIME
:
1866 if (sbufReadU16Safe(&tmp_u16
, src
))
1867 gyroConfigMutable()->looptime
= tmp_u16
;
1869 return MSP_RESULT_ERROR
;
1873 if (dataSize
== PID_ITEM_COUNT
* 4) {
1874 for (int i
= 0; i
< PID_ITEM_COUNT
; i
++) {
1875 pidBankMutable()->pid
[i
].P
= sbufReadU8(src
);
1876 pidBankMutable()->pid
[i
].I
= sbufReadU8(src
);
1877 pidBankMutable()->pid
[i
].D
= sbufReadU8(src
);
1878 pidBankMutable()->pid
[i
].FF
= sbufReadU8(src
);
1880 schedulePidGainsUpdate();
1881 navigationUsePIDs();
1883 return MSP_RESULT_ERROR
;
1886 case MSP_SET_MODE_RANGE
:
1887 sbufReadU8Safe(&tmp_u8
, src
);
1888 if ((dataSize
== 5) && (tmp_u8
< MAX_MODE_ACTIVATION_CONDITION_COUNT
)) {
1889 modeActivationCondition_t
*mac
= modeActivationConditionsMutable(tmp_u8
);
1890 tmp_u8
= sbufReadU8(src
);
1891 const box_t
*box
= findBoxByPermanentId(tmp_u8
);
1893 mac
->modeId
= box
->boxId
;
1894 mac
->auxChannelIndex
= sbufReadU8(src
);
1895 mac
->range
.startStep
= sbufReadU8(src
);
1896 mac
->range
.endStep
= sbufReadU8(src
);
1898 updateUsedModeActivationConditionFlags();
1900 return MSP_RESULT_ERROR
;
1903 return MSP_RESULT_ERROR
;
1907 case MSP_SET_ADJUSTMENT_RANGE
:
1908 sbufReadU8Safe(&tmp_u8
, src
);
1909 if ((dataSize
== 7) && (tmp_u8
< MAX_ADJUSTMENT_RANGE_COUNT
)) {
1910 adjustmentRange_t
*adjRange
= adjustmentRangesMutable(tmp_u8
);
1911 tmp_u8
= sbufReadU8(src
);
1912 if (tmp_u8
< MAX_SIMULTANEOUS_ADJUSTMENT_COUNT
) {
1913 adjRange
->adjustmentIndex
= tmp_u8
;
1914 adjRange
->auxChannelIndex
= sbufReadU8(src
);
1915 adjRange
->range
.startStep
= sbufReadU8(src
);
1916 adjRange
->range
.endStep
= sbufReadU8(src
);
1917 adjRange
->adjustmentFunction
= sbufReadU8(src
);
1918 adjRange
->auxSwitchChannelIndex
= sbufReadU8(src
);
1920 return MSP_RESULT_ERROR
;
1923 return MSP_RESULT_ERROR
;
1927 case MSP_SET_RC_TUNING
:
1928 if ((dataSize
== 10) || (dataSize
== 11)) {
1929 sbufReadU8(src
); //Read rcRate8, kept for protocol compatibility reasons
1930 // need to cast away const to set controlRateProfile
1931 ((controlRateConfig_t
*)currentControlRateProfile
)->stabilized
.rcExpo8
= sbufReadU8(src
);
1932 for (int i
= 0; i
< 3; i
++) {
1933 tmp_u8
= sbufReadU8(src
);
1935 ((controlRateConfig_t
*)currentControlRateProfile
)->stabilized
.rates
[i
] = constrain(tmp_u8
, SETTING_YAW_RATE_MIN
, SETTING_YAW_RATE_MAX
);
1938 ((controlRateConfig_t
*)currentControlRateProfile
)->stabilized
.rates
[i
] = constrain(tmp_u8
, SETTING_CONSTANT_ROLL_PITCH_RATE_MIN
, SETTING_CONSTANT_ROLL_PITCH_RATE_MAX
);
1941 tmp_u8
= sbufReadU8(src
);
1942 ((controlRateConfig_t
*)currentControlRateProfile
)->throttle
.dynPID
= MIN(tmp_u8
, SETTING_TPA_RATE_MAX
);
1943 ((controlRateConfig_t
*)currentControlRateProfile
)->throttle
.rcMid8
= sbufReadU8(src
);
1944 ((controlRateConfig_t
*)currentControlRateProfile
)->throttle
.rcExpo8
= sbufReadU8(src
);
1945 ((controlRateConfig_t
*)currentControlRateProfile
)->throttle
.pa_breakpoint
= sbufReadU16(src
);
1946 if (dataSize
> 10) {
1947 ((controlRateConfig_t
*)currentControlRateProfile
)->stabilized
.rcYawExpo8
= sbufReadU8(src
);
1950 schedulePidGainsUpdate();
1952 return MSP_RESULT_ERROR
;
1956 case MSP2_INAV_SET_RATE_PROFILE
:
1957 if (dataSize
== 15) {
1958 controlRateConfig_t
*currentControlRateProfile_p
= (controlRateConfig_t
*)currentControlRateProfile
; // need to cast away const to set controlRateProfile
1961 currentControlRateProfile_p
->throttle
.rcMid8
= sbufReadU8(src
);
1962 currentControlRateProfile_p
->throttle
.rcExpo8
= sbufReadU8(src
);
1963 currentControlRateProfile_p
->throttle
.dynPID
= sbufReadU8(src
);
1964 currentControlRateProfile_p
->throttle
.pa_breakpoint
= sbufReadU16(src
);
1967 currentControlRateProfile_p
->stabilized
.rcExpo8
= sbufReadU8(src
);
1968 currentControlRateProfile_p
->stabilized
.rcYawExpo8
= sbufReadU8(src
);
1969 for (uint8_t i
= 0; i
< 3; ++i
) {
1970 tmp_u8
= sbufReadU8(src
);
1972 currentControlRateProfile_p
->stabilized
.rates
[i
] = constrain(tmp_u8
, SETTING_YAW_RATE_MIN
, SETTING_YAW_RATE_MAX
);
1974 currentControlRateProfile_p
->stabilized
.rates
[i
] = constrain(tmp_u8
, SETTING_CONSTANT_ROLL_PITCH_RATE_MIN
, SETTING_CONSTANT_ROLL_PITCH_RATE_MAX
);
1979 currentControlRateProfile_p
->manual
.rcExpo8
= sbufReadU8(src
);
1980 currentControlRateProfile_p
->manual
.rcYawExpo8
= sbufReadU8(src
);
1981 for (uint8_t i
= 0; i
< 3; ++i
) {
1982 tmp_u8
= sbufReadU8(src
);
1984 currentControlRateProfile_p
->manual
.rates
[i
] = constrain(tmp_u8
, SETTING_YAW_RATE_MIN
, SETTING_YAW_RATE_MAX
);
1986 currentControlRateProfile_p
->manual
.rates
[i
] = constrain(tmp_u8
, SETTING_CONSTANT_ROLL_PITCH_RATE_MIN
, SETTING_CONSTANT_ROLL_PITCH_RATE_MAX
);
1991 return MSP_RESULT_ERROR
;
1996 if (dataSize
== 22) {
1997 sbufReadU16(src
); // midrc
1999 sbufReadU16(src
); //Was min_throttle
2000 sbufReadU16(src
); //Was maxThrottle
2001 motorConfigMutable()->mincommand
= constrain(sbufReadU16(src
), 0, PWM_RANGE_MAX
);
2003 currentBatteryProfileMutable
->failsafe_throttle
= constrain(sbufReadU16(src
), PWM_RANGE_MIN
, PWM_RANGE_MAX
);
2006 gpsConfigMutable()->provider
= sbufReadU8(src
); // gps_type
2007 sbufReadU8(src
); // gps_baudrate
2008 gpsConfigMutable()->sbasMode
= sbufReadU8(src
); // gps_ubx_sbas
2010 sbufReadU8(src
); // gps_type
2011 sbufReadU8(src
); // gps_baudrate
2012 sbufReadU8(src
); // gps_ubx_sbas
2014 sbufReadU8(src
); // multiwiiCurrentMeterOutput
2015 tmp_u8
= sbufReadU8(src
);
2016 if (tmp_u8
<= MAX_SUPPORTED_RC_CHANNEL_COUNT
) {
2017 rxConfigMutable()->rssi_channel
= tmp_u8
;
2018 rxUpdateRSSISource(); // Changing rssi_channel might change the RSSI source
2023 compassConfigMutable()->mag_declination
= sbufReadU16(src
) * 10;
2029 batteryMetersConfigMutable()->voltage
.scale
= sbufReadU8(src
) * 10;
2030 currentBatteryProfileMutable
->voltage
.cellMin
= sbufReadU8(src
) * 10; // vbatlevel_warn1 in MWC2.3 GUI
2031 currentBatteryProfileMutable
->voltage
.cellMax
= sbufReadU8(src
) * 10; // vbatlevel_warn2 in MWC2.3 GUI
2032 currentBatteryProfileMutable
->voltage
.cellWarning
= sbufReadU8(src
) * 10; // vbatlevel when buzzer starts to alert
2040 return MSP_RESULT_ERROR
;
2043 case MSP2_INAV_SET_MISC
:
2044 if (dataSize
== 41) {
2045 sbufReadU16(src
); // midrc
2047 sbufReadU16(src
); // was min_throttle
2048 sbufReadU16(src
); // was maxThrottle
2049 motorConfigMutable()->mincommand
= constrain(sbufReadU16(src
), 0, PWM_RANGE_MAX
);
2051 currentBatteryProfileMutable
->failsafe_throttle
= constrain(sbufReadU16(src
), PWM_RANGE_MIN
, PWM_RANGE_MAX
);
2054 gpsConfigMutable()->provider
= sbufReadU8(src
); // gps_type
2055 sbufReadU8(src
); // gps_baudrate
2056 gpsConfigMutable()->sbasMode
= sbufReadU8(src
); // gps_ubx_sbas
2058 sbufReadU8(src
); // gps_type
2059 sbufReadU8(src
); // gps_baudrate
2060 sbufReadU8(src
); // gps_ubx_sbas
2063 tmp_u8
= sbufReadU8(src
);
2064 if (tmp_u8
<= MAX_SUPPORTED_RC_CHANNEL_COUNT
)
2065 rxConfigMutable()->rssi_channel
= tmp_u8
;
2068 compassConfigMutable()->mag_declination
= sbufReadU16(src
) * 10;
2074 batteryMetersConfigMutable()->voltage
.scale
= sbufReadU16(src
);
2075 batteryMetersConfigMutable()->voltageSource
= sbufReadU8(src
);
2076 currentBatteryProfileMutable
->cells
= sbufReadU8(src
);
2077 currentBatteryProfileMutable
->voltage
.cellDetect
= sbufReadU16(src
);
2078 currentBatteryProfileMutable
->voltage
.cellMin
= sbufReadU16(src
);
2079 currentBatteryProfileMutable
->voltage
.cellMax
= sbufReadU16(src
);
2080 currentBatteryProfileMutable
->voltage
.cellWarning
= sbufReadU16(src
);
2091 currentBatteryProfileMutable
->capacity
.value
= sbufReadU32(src
);
2092 currentBatteryProfileMutable
->capacity
.warning
= sbufReadU32(src
);
2093 currentBatteryProfileMutable
->capacity
.critical
= sbufReadU32(src
);
2094 batteryMetersConfigMutable()->capacity_unit
= sbufReadU8(src
);
2095 if ((batteryMetersConfig()->voltageSource
!= BAT_VOLTAGE_RAW
) && (batteryMetersConfig()->voltageSource
!= BAT_VOLTAGE_SAG_COMP
)) {
2096 batteryMetersConfigMutable()->voltageSource
= BAT_VOLTAGE_RAW
;
2097 return MSP_RESULT_ERROR
;
2099 if ((batteryMetersConfig()->capacity_unit
!= BAT_CAPACITY_UNIT_MAH
) && (batteryMetersConfig()->capacity_unit
!= BAT_CAPACITY_UNIT_MWH
)) {
2100 batteryMetersConfigMutable()->capacity_unit
= BAT_CAPACITY_UNIT_MAH
;
2101 return MSP_RESULT_ERROR
;
2104 return MSP_RESULT_ERROR
;
2107 case MSP2_INAV_SET_BATTERY_CONFIG
:
2108 if (dataSize
== 29) {
2110 batteryMetersConfigMutable()->voltage
.scale
= sbufReadU16(src
);
2111 batteryMetersConfigMutable()->voltageSource
= sbufReadU8(src
);
2112 currentBatteryProfileMutable
->cells
= sbufReadU8(src
);
2113 currentBatteryProfileMutable
->voltage
.cellDetect
= sbufReadU16(src
);
2114 currentBatteryProfileMutable
->voltage
.cellMin
= sbufReadU16(src
);
2115 currentBatteryProfileMutable
->voltage
.cellMax
= sbufReadU16(src
);
2116 currentBatteryProfileMutable
->voltage
.cellWarning
= sbufReadU16(src
);
2127 batteryMetersConfigMutable()->current
.offset
= sbufReadU16(src
);
2128 batteryMetersConfigMutable()->current
.scale
= sbufReadU16(src
);
2130 currentBatteryProfileMutable
->capacity
.value
= sbufReadU32(src
);
2131 currentBatteryProfileMutable
->capacity
.warning
= sbufReadU32(src
);
2132 currentBatteryProfileMutable
->capacity
.critical
= sbufReadU32(src
);
2133 batteryMetersConfigMutable()->capacity_unit
= sbufReadU8(src
);
2134 if ((batteryMetersConfig()->voltageSource
!= BAT_VOLTAGE_RAW
) && (batteryMetersConfig()->voltageSource
!= BAT_VOLTAGE_SAG_COMP
)) {
2135 batteryMetersConfigMutable()->voltageSource
= BAT_VOLTAGE_RAW
;
2136 return MSP_RESULT_ERROR
;
2138 if ((batteryMetersConfig()->capacity_unit
!= BAT_CAPACITY_UNIT_MAH
) && (batteryMetersConfig()->capacity_unit
!= BAT_CAPACITY_UNIT_MWH
)) {
2139 batteryMetersConfigMutable()->capacity_unit
= BAT_CAPACITY_UNIT_MAH
;
2140 return MSP_RESULT_ERROR
;
2143 return MSP_RESULT_ERROR
;
2147 if (dataSize
== 8 * sizeof(uint16_t)) {
2148 for (int i
= 0; i
< 8; i
++) {
2149 const int16_t disarmed
= sbufReadU16(src
);
2150 if (i
< MAX_SUPPORTED_MOTORS
) {
2151 motor_disarmed
[i
] = disarmed
;
2155 return MSP_RESULT_ERROR
;
2158 case MSP_SET_SERVO_CONFIGURATION
:
2159 if (dataSize
!= (1 + 14)) {
2160 return MSP_RESULT_ERROR
;
2162 tmp_u8
= sbufReadU8(src
);
2163 if (tmp_u8
>= MAX_SUPPORTED_SERVOS
) {
2164 return MSP_RESULT_ERROR
;
2166 servoParamsMutable(tmp_u8
)->min
= sbufReadU16(src
);
2167 servoParamsMutable(tmp_u8
)->max
= sbufReadU16(src
);
2168 servoParamsMutable(tmp_u8
)->middle
= sbufReadU16(src
);
2169 servoParamsMutable(tmp_u8
)->rate
= sbufReadU8(src
);
2172 sbufReadU8(src
); // used to be forwardFromChannel, ignored
2173 sbufReadU32(src
); // used to be reversedSources
2174 servoComputeScalingFactors(tmp_u8
);
2178 case MSP2_INAV_SET_SERVO_CONFIG
:
2179 if (dataSize
!= 8) {
2180 return MSP_RESULT_ERROR
;
2182 tmp_u8
= sbufReadU8(src
);
2183 if (tmp_u8
>= MAX_SUPPORTED_SERVOS
) {
2184 return MSP_RESULT_ERROR
;
2186 servoParamsMutable(tmp_u8
)->min
= sbufReadU16(src
);
2187 servoParamsMutable(tmp_u8
)->max
= sbufReadU16(src
);
2188 servoParamsMutable(tmp_u8
)->middle
= sbufReadU16(src
);
2189 servoParamsMutable(tmp_u8
)->rate
= sbufReadU8(src
);
2190 servoComputeScalingFactors(tmp_u8
);
2194 case MSP_SET_SERVO_MIX_RULE
:
2195 sbufReadU8Safe(&tmp_u8
, src
);
2196 if ((dataSize
== 9) && (tmp_u8
< MAX_SERVO_RULES
)) {
2197 customServoMixersMutable(tmp_u8
)->targetChannel
= sbufReadU8(src
);
2198 customServoMixersMutable(tmp_u8
)->inputSource
= sbufReadU8(src
);
2199 customServoMixersMutable(tmp_u8
)->rate
= sbufReadU16(src
);
2200 customServoMixersMutable(tmp_u8
)->speed
= sbufReadU8(src
);
2201 sbufReadU16(src
); //Read 2bytes for min/max and ignore it
2202 sbufReadU8(src
); //Read 1 byte for `box` and ignore it
2203 loadCustomServoMixer();
2205 return MSP_RESULT_ERROR
;
2208 case MSP2_INAV_SET_SERVO_MIXER
:
2209 sbufReadU8Safe(&tmp_u8
, src
);
2210 if ((dataSize
== 7) && (tmp_u8
< MAX_SERVO_RULES
)) {
2211 customServoMixersMutable(tmp_u8
)->targetChannel
= sbufReadU8(src
);
2212 customServoMixersMutable(tmp_u8
)->inputSource
= sbufReadU8(src
);
2213 customServoMixersMutable(tmp_u8
)->rate
= sbufReadU16(src
);
2214 customServoMixersMutable(tmp_u8
)->speed
= sbufReadU8(src
);
2215 #ifdef USE_PROGRAMMING_FRAMEWORK
2216 customServoMixersMutable(tmp_u8
)->conditionId
= sbufReadU8(src
);
2220 loadCustomServoMixer();
2222 return MSP_RESULT_ERROR
;
2224 #ifdef USE_PROGRAMMING_FRAMEWORK
2225 case MSP2_INAV_SET_LOGIC_CONDITIONS
:
2226 sbufReadU8Safe(&tmp_u8
, src
);
2227 if ((dataSize
== 15) && (tmp_u8
< MAX_LOGIC_CONDITIONS
)) {
2228 logicConditionsMutable(tmp_u8
)->enabled
= sbufReadU8(src
);
2229 logicConditionsMutable(tmp_u8
)->activatorId
= sbufReadU8(src
);
2230 logicConditionsMutable(tmp_u8
)->operation
= sbufReadU8(src
);
2231 logicConditionsMutable(tmp_u8
)->operandA
.type
= sbufReadU8(src
);
2232 logicConditionsMutable(tmp_u8
)->operandA
.value
= sbufReadU32(src
);
2233 logicConditionsMutable(tmp_u8
)->operandB
.type
= sbufReadU8(src
);
2234 logicConditionsMutable(tmp_u8
)->operandB
.value
= sbufReadU32(src
);
2235 logicConditionsMutable(tmp_u8
)->flags
= sbufReadU8(src
);
2237 return MSP_RESULT_ERROR
;
2240 case MSP2_INAV_SET_PROGRAMMING_PID
:
2241 sbufReadU8Safe(&tmp_u8
, src
);
2242 if ((dataSize
== 20) && (tmp_u8
< MAX_PROGRAMMING_PID_COUNT
)) {
2243 programmingPidsMutable(tmp_u8
)->enabled
= sbufReadU8(src
);
2244 programmingPidsMutable(tmp_u8
)->setpoint
.type
= sbufReadU8(src
);
2245 programmingPidsMutable(tmp_u8
)->setpoint
.value
= sbufReadU32(src
);
2246 programmingPidsMutable(tmp_u8
)->measurement
.type
= sbufReadU8(src
);
2247 programmingPidsMutable(tmp_u8
)->measurement
.value
= sbufReadU32(src
);
2248 programmingPidsMutable(tmp_u8
)->gains
.P
= sbufReadU16(src
);
2249 programmingPidsMutable(tmp_u8
)->gains
.I
= sbufReadU16(src
);
2250 programmingPidsMutable(tmp_u8
)->gains
.D
= sbufReadU16(src
);
2251 programmingPidsMutable(tmp_u8
)->gains
.FF
= sbufReadU16(src
);
2253 return MSP_RESULT_ERROR
;
2256 case MSP2_COMMON_SET_MOTOR_MIXER
:
2257 sbufReadU8Safe(&tmp_u8
, src
);
2258 if ((dataSize
== 9) && (tmp_u8
< MAX_SUPPORTED_MOTORS
)) {
2259 primaryMotorMixerMutable(tmp_u8
)->throttle
= constrainf(sbufReadU16(src
) / 1000.0f
, 0.0f
, 4.0f
) - 2.0f
;
2260 primaryMotorMixerMutable(tmp_u8
)->roll
= constrainf(sbufReadU16(src
) / 1000.0f
, 0.0f
, 4.0f
) - 2.0f
;
2261 primaryMotorMixerMutable(tmp_u8
)->pitch
= constrainf(sbufReadU16(src
) / 1000.0f
, 0.0f
, 4.0f
) - 2.0f
;
2262 primaryMotorMixerMutable(tmp_u8
)->yaw
= constrainf(sbufReadU16(src
) / 1000.0f
, 0.0f
, 4.0f
) - 2.0f
;
2264 return MSP_RESULT_ERROR
;
2268 if (dataSize
== 6) {
2269 reversibleMotorsConfigMutable()->deadband_low
= sbufReadU16(src
);
2270 reversibleMotorsConfigMutable()->deadband_high
= sbufReadU16(src
);
2271 reversibleMotorsConfigMutable()->neutral
= sbufReadU16(src
);
2273 return MSP_RESULT_ERROR
;
2276 case MSP_SET_RC_DEADBAND
:
2277 if (dataSize
== 5) {
2278 rcControlsConfigMutable()->deadband
= sbufReadU8(src
);
2279 rcControlsConfigMutable()->yaw_deadband
= sbufReadU8(src
);
2280 rcControlsConfigMutable()->alt_hold_deadband
= sbufReadU8(src
);
2281 rcControlsConfigMutable()->mid_throttle_deadband
= sbufReadU16(src
);
2283 return MSP_RESULT_ERROR
;
2286 case MSP_SET_RESET_CURR_PID
:
2287 PG_RESET_CURRENT(pidProfile
);
2290 case MSP_SET_SENSOR_ALIGNMENT
:
2291 if (dataSize
== 4) {
2292 sbufReadU8(src
); // was gyroConfigMutable()->gyro_align
2293 sbufReadU8(src
); // was accelerometerConfigMutable()->acc_align
2295 compassConfigMutable()->mag_align
= sbufReadU8(src
);
2300 opticalFlowConfigMutable()->opflow_align
= sbufReadU8(src
);
2305 return MSP_RESULT_ERROR
;
2308 case MSP_SET_ADVANCED_CONFIG
:
2309 if (dataSize
== 9) {
2310 sbufReadU8(src
); // gyroConfig()->gyroSyncDenominator
2311 sbufReadU8(src
); // BF: masterConfig.pid_process_denom
2312 sbufReadU8(src
); // BF: motorConfig()->useUnsyncedPwm
2313 motorConfigMutable()->motorPwmProtocol
= sbufReadU8(src
);
2314 motorConfigMutable()->motorPwmRate
= sbufReadU16(src
);
2315 servoConfigMutable()->servoPwmRate
= sbufReadU16(src
);
2316 sbufReadU8(src
); //Was gyroSync
2318 return MSP_RESULT_ERROR
;
2321 case MSP_SET_FILTER_CONFIG
:
2322 if (dataSize
>= 5) {
2323 gyroConfigMutable()->gyro_main_lpf_hz
= sbufReadU8(src
);
2324 pidProfileMutable()->dterm_lpf_hz
= constrain(sbufReadU16(src
), 0, 500);
2325 pidProfileMutable()->yaw_lpf_hz
= constrain(sbufReadU16(src
), 0, 255);
2326 if (dataSize
>= 9) {
2327 sbufReadU16(src
); //Was gyroConfigMutable()->gyro_notch_hz
2328 sbufReadU16(src
); //Was gyroConfigMutable()->gyro_notch_cutoff
2330 return MSP_RESULT_ERROR
;
2332 if (dataSize
>= 13) {
2337 return MSP_RESULT_ERROR
;
2339 if (dataSize
>= 17) {
2340 sbufReadU16(src
); // Was gyroConfigMutable()->gyro_soft_notch_hz_2
2341 sbufReadU16(src
); // Was gyroConfigMutable()->gyro_soft_notch_cutoff_2
2343 return MSP_RESULT_ERROR
;
2346 if (dataSize
>= 21) {
2347 accelerometerConfigMutable()->acc_notch_hz
= constrain(sbufReadU16(src
), 0, 255);
2348 accelerometerConfigMutable()->acc_notch_cutoff
= constrain(sbufReadU16(src
), 1, 255);
2350 return MSP_RESULT_ERROR
;
2353 if (dataSize
>= 22) {
2354 sbufReadU16(src
); //Was gyro_stage2_lowpass_hz
2356 return MSP_RESULT_ERROR
;
2359 return MSP_RESULT_ERROR
;
2362 case MSP_SET_PID_ADVANCED
:
2363 if (dataSize
== 17) {
2364 sbufReadU16(src
); // pidProfileMutable()->rollPitchItermIgnoreRate
2365 sbufReadU16(src
); // pidProfileMutable()->yawItermIgnoreRate
2366 sbufReadU16(src
); //pidProfile()->yaw_p_limit
2368 sbufReadU8(src
); //BF: pidProfileMutable()->deltaMethod
2369 sbufReadU8(src
); //BF: pidProfileMutable()->vbatPidCompensation
2370 sbufReadU8(src
); //BF: pidProfileMutable()->setpointRelaxRatio
2372 sbufReadU16(src
); // Was pidsumLimit
2373 sbufReadU8(src
); //BF: pidProfileMutable()->itermThrottleGain
2376 * To keep compatibility on MSP frame length level with Betaflight, axis axisAccelerationLimitYaw
2377 * limit will be sent and received in [dps / 10]
2379 pidProfileMutable()->axisAccelerationLimitRollPitch
= sbufReadU16(src
) * 10;
2380 pidProfileMutable()->axisAccelerationLimitYaw
= sbufReadU16(src
) * 10;
2382 return MSP_RESULT_ERROR
;
2385 case MSP_SET_INAV_PID
:
2386 if (dataSize
== 15) {
2387 sbufReadU8(src
); //Legacy, no longer in use async processing value
2388 sbufReadU16(src
); //Legacy, no longer in use async processing value
2389 sbufReadU16(src
); //Legacy, no longer in use async processing value
2390 pidProfileMutable()->heading_hold_rate_limit
= sbufReadU8(src
);
2391 sbufReadU8(src
); //HEADING_HOLD_ERROR_LPF_FREQ
2392 sbufReadU16(src
); //Legacy yaw_jump_prevention_limit
2393 sbufReadU8(src
); // was gyro lpf
2394 accelerometerConfigMutable()->acc_lpf_hz
= sbufReadU8(src
);
2395 sbufReadU8(src
); //reserved
2396 sbufReadU8(src
); //reserved
2397 sbufReadU8(src
); //reserved
2398 sbufReadU8(src
); //reserved
2400 return MSP_RESULT_ERROR
;
2403 case MSP_SET_SENSOR_CONFIG
:
2404 if (dataSize
== 6) {
2405 accelerometerConfigMutable()->acc_hardware
= sbufReadU8(src
);
2407 barometerConfigMutable()->baro_hardware
= sbufReadU8(src
);
2412 compassConfigMutable()->mag_hardware
= sbufReadU8(src
);
2417 pitotmeterConfigMutable()->pitot_hardware
= sbufReadU8(src
);
2421 #ifdef USE_RANGEFINDER
2422 rangefinderConfigMutable()->rangefinder_hardware
= sbufReadU8(src
);
2424 sbufReadU8(src
); // rangefinder hardware
2427 opticalFlowConfigMutable()->opflow_hardware
= sbufReadU8(src
);
2429 sbufReadU8(src
); // optical flow hardware
2432 return MSP_RESULT_ERROR
;
2435 case MSP_SET_NAV_POSHOLD
:
2436 if (dataSize
== 13) {
2437 navConfigMutable()->general
.flags
.user_control_mode
= sbufReadU8(src
);
2438 navConfigMutable()->general
.max_auto_speed
= sbufReadU16(src
);
2439 if (mixerConfig()->platformType
== PLATFORM_AIRPLANE
) {
2440 navConfigMutable()->fw
.max_auto_climb_rate
= sbufReadU16(src
);
2442 navConfigMutable()->mc
.max_auto_climb_rate
= sbufReadU16(src
);
2444 navConfigMutable()->general
.max_manual_speed
= sbufReadU16(src
);
2445 if (mixerConfig()->platformType
== PLATFORM_AIRPLANE
) {
2446 navConfigMutable()->fw
.max_manual_climb_rate
= sbufReadU16(src
);
2448 navConfigMutable()->mc
.max_manual_climb_rate
= sbufReadU16(src
);
2450 navConfigMutable()->mc
.max_bank_angle
= sbufReadU8(src
);
2451 navConfigMutable()->mc
.althold_throttle_type
= sbufReadU8(src
);
2452 currentBatteryProfileMutable
->nav
.mc
.hover_throttle
= sbufReadU16(src
);
2454 return MSP_RESULT_ERROR
;
2457 case MSP_SET_RTH_AND_LAND_CONFIG
:
2458 if (dataSize
== 21) {
2459 navConfigMutable()->general
.min_rth_distance
= sbufReadU16(src
);
2460 navConfigMutable()->general
.flags
.rth_climb_first
= sbufReadU8(src
);
2461 navConfigMutable()->general
.flags
.rth_climb_ignore_emerg
= sbufReadU8(src
);
2462 navConfigMutable()->general
.flags
.rth_tail_first
= sbufReadU8(src
);
2463 navConfigMutable()->general
.flags
.rth_allow_landing
= sbufReadU8(src
);
2464 navConfigMutable()->general
.flags
.rth_alt_control_mode
= sbufReadU8(src
);
2465 navConfigMutable()->general
.rth_abort_threshold
= sbufReadU16(src
);
2466 navConfigMutable()->general
.rth_altitude
= sbufReadU16(src
);
2467 navConfigMutable()->general
.land_minalt_vspd
= sbufReadU16(src
);
2468 navConfigMutable()->general
.land_maxalt_vspd
= sbufReadU16(src
);
2469 navConfigMutable()->general
.land_slowdown_minalt
= sbufReadU16(src
);
2470 navConfigMutable()->general
.land_slowdown_maxalt
= sbufReadU16(src
);
2471 navConfigMutable()->general
.emerg_descent_rate
= sbufReadU16(src
);
2473 return MSP_RESULT_ERROR
;
2476 case MSP_SET_FW_CONFIG
:
2477 if (dataSize
== 12) {
2478 currentBatteryProfileMutable
->nav
.fw
.cruise_throttle
= sbufReadU16(src
);
2479 currentBatteryProfileMutable
->nav
.fw
.min_throttle
= sbufReadU16(src
);
2480 currentBatteryProfileMutable
->nav
.fw
.max_throttle
= sbufReadU16(src
);
2481 navConfigMutable()->fw
.max_bank_angle
= sbufReadU8(src
);
2482 navConfigMutable()->fw
.max_climb_angle
= sbufReadU8(src
);
2483 navConfigMutable()->fw
.max_dive_angle
= sbufReadU8(src
);
2484 currentBatteryProfileMutable
->nav
.fw
.pitch_to_throttle
= sbufReadU8(src
);
2485 navConfigMutable()->fw
.loiter_radius
= sbufReadU16(src
);
2487 return MSP_RESULT_ERROR
;
2490 case MSP_SET_CALIBRATION_DATA
:
2491 if (dataSize
>= 18) {
2492 accelerometerConfigMutable()->accZero
.raw
[X
] = sbufReadU16(src
);
2493 accelerometerConfigMutable()->accZero
.raw
[Y
] = sbufReadU16(src
);
2494 accelerometerConfigMutable()->accZero
.raw
[Z
] = sbufReadU16(src
);
2495 accelerometerConfigMutable()->accGain
.raw
[X
] = sbufReadU16(src
);
2496 accelerometerConfigMutable()->accGain
.raw
[Y
] = sbufReadU16(src
);
2497 accelerometerConfigMutable()->accGain
.raw
[Z
] = sbufReadU16(src
);
2500 compassConfigMutable()->magZero
.raw
[X
] = sbufReadU16(src
);
2501 compassConfigMutable()->magZero
.raw
[Y
] = sbufReadU16(src
);
2502 compassConfigMutable()->magZero
.raw
[Z
] = sbufReadU16(src
);
2509 if (dataSize
>= 20) {
2510 opticalFlowConfigMutable()->opflow_scale
= sbufReadU16(src
) / 256.0f
;
2514 if (dataSize
>= 22) {
2515 compassConfigMutable()->magGain
[X
] = sbufReadU16(src
);
2516 compassConfigMutable()->magGain
[Y
] = sbufReadU16(src
);
2517 compassConfigMutable()->magGain
[Z
] = sbufReadU16(src
);
2520 if (dataSize
>= 22) {
2527 return MSP_RESULT_ERROR
;
2530 case MSP_SET_POSITION_ESTIMATION_CONFIG
:
2531 if (dataSize
== 12) {
2532 positionEstimationConfigMutable()->w_z_baro_p
= constrainf(sbufReadU16(src
) / 100.0f
, 0.0f
, 10.0f
);
2533 positionEstimationConfigMutable()->w_z_gps_p
= constrainf(sbufReadU16(src
) / 100.0f
, 0.0f
, 10.0f
);
2534 positionEstimationConfigMutable()->w_z_gps_v
= constrainf(sbufReadU16(src
) / 100.0f
, 0.0f
, 10.0f
);
2535 positionEstimationConfigMutable()->w_xy_gps_p
= constrainf(sbufReadU16(src
) / 100.0f
, 0.0f
, 10.0f
);
2536 positionEstimationConfigMutable()->w_xy_gps_v
= constrainf(sbufReadU16(src
) / 100.0f
, 0.0f
, 10.0f
);
2537 gpsConfigMutable()->gpsMinSats
= constrain(sbufReadU8(src
), 5, 10);
2538 sbufReadU8(src
); // was positionEstimationConfigMutable()->use_gps_velned
2540 return MSP_RESULT_ERROR
;
2543 case MSP_RESET_CONF
:
2544 if (!ARMING_FLAG(ARMED
)) {
2551 return MSP_RESULT_ERROR
;
2554 case MSP_ACC_CALIBRATION
:
2555 if (!ARMING_FLAG(ARMED
))
2556 accStartCalibration();
2558 return MSP_RESULT_ERROR
;
2561 case MSP_MAG_CALIBRATION
:
2562 if (!ARMING_FLAG(ARMED
))
2563 ENABLE_STATE(CALIBRATE_MAG
);
2565 return MSP_RESULT_ERROR
;
2569 case MSP2_INAV_OPFLOW_CALIBRATION
:
2570 if (!ARMING_FLAG(ARMED
))
2571 opflowStartCalibration();
2573 return MSP_RESULT_ERROR
;
2577 case MSP_EEPROM_WRITE
:
2578 if (!ARMING_FLAG(ARMED
)) {
2584 return MSP_RESULT_ERROR
;
2588 case MSP2_SET_BLACKBOX_CONFIG
:
2589 // Don't allow config to be updated while Blackbox is logging
2590 if ((dataSize
== 9) && blackboxMayEditConfig()) {
2591 blackboxConfigMutable()->device
= sbufReadU8(src
);
2592 blackboxConfigMutable()->rate_num
= sbufReadU16(src
);
2593 blackboxConfigMutable()->rate_denom
= sbufReadU16(src
);
2594 blackboxConfigMutable()->includeFlags
= sbufReadU32(src
);
2596 return MSP_RESULT_ERROR
;
2601 case MSP_SET_OSD_CONFIG
:
2602 sbufReadU8Safe(&tmp_u8
, src
);
2603 // set all the other settings
2604 if ((int8_t)tmp_u8
== -1) {
2605 if (dataSize
>= 10) {
2606 osdConfigMutable()->video_system
= sbufReadU8(src
);
2607 osdConfigMutable()->units
= sbufReadU8(src
);
2608 osdConfigMutable()->rssi_alarm
= sbufReadU8(src
);
2609 currentBatteryProfileMutable
->capacity
.warning
= sbufReadU16(src
);
2610 osdConfigMutable()->time_alarm
= sbufReadU16(src
);
2611 osdConfigMutable()->alt_alarm
= sbufReadU16(src
);
2612 // Won't be read if they weren't provided
2613 sbufReadU16Safe(&osdConfigMutable()->dist_alarm
, src
);
2614 sbufReadU16Safe(&osdConfigMutable()->neg_alt_alarm
, src
);
2616 return MSP_RESULT_ERROR
;
2618 // set a position setting
2619 if ((dataSize
>= 3) && (tmp_u8
< OSD_ITEM_COUNT
)) // tmp_u8 == addr
2620 osdLayoutsConfigMutable()->item_pos
[0][tmp_u8
] = sbufReadU16(src
);
2622 return MSP_RESULT_ERROR
;
2624 // Either a element position change or a units change needs
2625 // a full redraw, since an element can change size significantly
2626 // and the old position or the now unused space due to the
2627 // size change need to be erased.
2628 osdStartFullRedraw();
2631 case MSP_OSD_CHAR_WRITE
:
2632 if (dataSize
>= 55) {
2634 size_t osdCharacterBytes
;
2636 if (dataSize
>= OSD_CHAR_VISIBLE_BYTES
+ 2) {
2637 if (dataSize
>= OSD_CHAR_BYTES
+ 2) {
2638 // 16 bit address, full char with metadata
2639 addr
= sbufReadU16(src
);
2640 osdCharacterBytes
= OSD_CHAR_BYTES
;
2641 } else if (dataSize
>= OSD_CHAR_BYTES
+ 1) {
2642 // 8 bit address, full char with metadata
2643 addr
= sbufReadU8(src
);
2644 osdCharacterBytes
= OSD_CHAR_BYTES
;
2646 // 16 bit character address, only visible char bytes
2647 addr
= sbufReadU16(src
);
2648 osdCharacterBytes
= OSD_CHAR_VISIBLE_BYTES
;
2651 // 8 bit character address, only visible char bytes
2652 addr
= sbufReadU8(src
);
2653 osdCharacterBytes
= OSD_CHAR_VISIBLE_BYTES
;
2655 for (unsigned ii
= 0; ii
< MIN(osdCharacterBytes
, sizeof(chr
.data
)); ii
++) {
2656 chr
.data
[ii
] = sbufReadU8(src
);
2658 displayPort_t
*osdDisplayPort
= osdGetDisplayPort();
2659 if (osdDisplayPort
) {
2660 displayWriteFontCharacter(osdDisplayPort
, addr
, &chr
);
2663 return MSP_RESULT_ERROR
;
2668 #ifdef USE_VTX_CONTROL
2669 case MSP_SET_VTX_CONFIG
:
2670 if (dataSize
>= 2) {
2671 vtxDevice_t
*vtxDevice
= vtxCommonDevice();
2673 if (vtxCommonGetDeviceType(vtxDevice
) != VTXDEV_UNKNOWN
) {
2674 uint16_t newFrequency
= sbufReadU16(src
);
2675 if (newFrequency
<= VTXCOMMON_MSP_BANDCHAN_CHKVAL
) { //value is band and channel
2676 const uint8_t newBand
= (newFrequency
/ 8) + 1;
2677 const uint8_t newChannel
= (newFrequency
% 8) + 1;
2679 if(vtxSettingsConfig()->band
!= newBand
|| vtxSettingsConfig()->channel
!= newChannel
) {
2680 vtxCommonSetBandAndChannel(vtxDevice
, newBand
, newChannel
);
2683 vtxSettingsConfigMutable()->band
= newBand
;
2684 vtxSettingsConfigMutable()->channel
= newChannel
;
2687 if (sbufBytesRemaining(src
) > 1) {
2688 uint8_t newPower
= sbufReadU8(src
);
2689 uint8_t currentPower
= 0;
2690 vtxCommonGetPowerIndex(vtxDevice
, ¤tPower
);
2691 if (newPower
!= currentPower
) {
2692 vtxCommonSetPowerByIndex(vtxDevice
, newPower
);
2693 vtxSettingsConfigMutable()->power
= newPower
;
2696 // Delegate pitmode to vtx directly
2697 const uint8_t newPitmode
= sbufReadU8(src
);
2698 uint8_t currentPitmode
= 0;
2699 vtxCommonGetPitMode(vtxDevice
, ¤tPitmode
);
2700 if (currentPitmode
!= newPitmode
) {
2701 vtxCommonSetPitMode(vtxDevice
, newPitmode
);
2704 if (sbufBytesRemaining(src
) > 0) {
2705 vtxSettingsConfigMutable()->lowPowerDisarm
= sbufReadU8(src
);
2708 // //////////////////////////////////////////////////////////
2709 // this code is taken from BF, it's hack for HDZERO VTX MSP frame
2710 // API version 1.42 - this parameter kept separate since clients may already be supplying
2711 if (sbufBytesRemaining(src
) >= 2) {
2712 sbufReadU16(src
); //skip pitModeFreq
2715 // API version 1.42 - extensions for non-encoded versions of the band, channel or frequency
2716 if (sbufBytesRemaining(src
) >= 4) {
2717 uint8_t newBand
= sbufReadU8(src
);
2718 const uint8_t newChannel
= sbufReadU8(src
);
2719 vtxSettingsConfigMutable()->band
= newBand
;
2720 vtxSettingsConfigMutable()->channel
= newChannel
;
2723 /* if (sbufBytesRemaining(src) >= 4) {
2724 sbufRead8(src); // freq_l
2725 sbufRead8(src); // freq_h
2726 sbufRead8(src); // band count
2727 sbufRead8(src); // channel count
2729 // //////////////////////////////////////////////////////////
2734 return MSP_RESULT_ERROR
;
2740 case MSP_DATAFLASH_ERASE
:
2741 flashfsEraseCompletely();
2746 case MSP_SET_RAW_GPS
:
2747 if (dataSize
== 14) {
2748 gpsSol
.fixType
= sbufReadU8(src
);
2749 if (gpsSol
.fixType
) {
2750 ENABLE_STATE(GPS_FIX
);
2752 DISABLE_STATE(GPS_FIX
);
2754 gpsSol
.flags
.validVelNE
= false;
2755 gpsSol
.flags
.validVelD
= false;
2756 gpsSol
.flags
.validEPE
= false;
2757 gpsSol
.flags
.validTime
= false;
2758 gpsSol
.numSat
= sbufReadU8(src
);
2759 gpsSol
.llh
.lat
= sbufReadU32(src
);
2760 gpsSol
.llh
.lon
= sbufReadU32(src
);
2761 gpsSol
.llh
.alt
= 100 * sbufReadU16(src
); // require cm
2762 gpsSol
.groundSpeed
= sbufReadU16(src
);
2763 gpsSol
.velNED
[X
] = 0;
2764 gpsSol
.velNED
[Y
] = 0;
2765 gpsSol
.velNED
[Z
] = 0;
2768 // Feed data to navigation
2769 sensorsSet(SENSOR_GPS
);
2772 return MSP_RESULT_ERROR
;
2777 if (dataSize
== 21) {
2779 const uint8_t msp_wp_no
= sbufReadU8(src
); // get the waypoint number
2780 navWaypoint_t msp_wp
;
2781 msp_wp
.action
= sbufReadU8(src
); // action
2782 msp_wp
.lat
= sbufReadU32(src
); // lat
2783 msp_wp
.lon
= sbufReadU32(src
); // lon
2784 msp_wp
.alt
= sbufReadU32(src
); // to set altitude (cm)
2785 msp_wp
.p1
= sbufReadU16(src
); // P1
2786 msp_wp
.p2
= sbufReadU16(src
); // P2
2787 msp_wp
.p3
= sbufReadU16(src
); // P3
2788 msp_wp
.flag
= sbufReadU8(src
); // future: to set nav flag
2789 setWaypoint(msp_wp_no
, &msp_wp
);
2791 #ifdef USE_FW_AUTOLAND
2792 static uint8_t mmIdx
= 0, fwAppraochStartIdx
= 8;
2793 #ifdef USE_SAFE_HOME
2794 fwAppraochStartIdx
= MAX_SAFE_HOMES
;
2796 if (msp_wp_no
== 0) {
2798 } else if (msp_wp
.flag
== NAV_WP_FLAG_LAST
) {
2801 resetFwAutolandApproach(fwAppraochStartIdx
+ mmIdx
);
2804 return MSP_RESULT_ERROR
;
2808 case MSP2_COMMON_SET_RADAR_POS
:
2809 if (dataSize
== 19) {
2810 const uint8_t msp_radar_no
= MIN(sbufReadU8(src
), RADAR_MAX_POIS
- 1); // Radar poi number, 0 to 3
2811 radar_pois
[msp_radar_no
].state
= sbufReadU8(src
); // 0=undefined, 1=armed, 2=lost
2812 radar_pois
[msp_radar_no
].gps
.lat
= sbufReadU32(src
); // lat 10E7
2813 radar_pois
[msp_radar_no
].gps
.lon
= sbufReadU32(src
); // lon 10E7
2814 radar_pois
[msp_radar_no
].gps
.alt
= sbufReadU32(src
); // altitude (cm)
2815 radar_pois
[msp_radar_no
].heading
= sbufReadU16(src
); // °
2816 radar_pois
[msp_radar_no
].speed
= sbufReadU16(src
); // cm/s
2817 radar_pois
[msp_radar_no
].lq
= sbufReadU8(src
); // Link quality, from 0 to 4
2819 return MSP_RESULT_ERROR
;
2822 case MSP_SET_FEATURE
:
2823 if (dataSize
== 4) {
2825 featureSet(sbufReadU32(src
)); // features bitmap
2826 rxUpdateRSSISource(); // For FEATURE_RSSI_ADC
2828 return MSP_RESULT_ERROR
;
2831 case MSP_SET_BOARD_ALIGNMENT
:
2832 if (dataSize
== 6) {
2833 boardAlignmentMutable()->rollDeciDegrees
= sbufReadU16(src
);
2834 boardAlignmentMutable()->pitchDeciDegrees
= sbufReadU16(src
);
2835 boardAlignmentMutable()->yawDeciDegrees
= sbufReadU16(src
);
2837 return MSP_RESULT_ERROR
;
2840 case MSP_SET_VOLTAGE_METER_CONFIG
:
2841 if (dataSize
== 4) {
2843 batteryMetersConfigMutable()->voltage
.scale
= sbufReadU8(src
) * 10;
2844 currentBatteryProfileMutable
->voltage
.cellMin
= sbufReadU8(src
) * 10;
2845 currentBatteryProfileMutable
->voltage
.cellMax
= sbufReadU8(src
) * 10;
2846 currentBatteryProfileMutable
->voltage
.cellWarning
= sbufReadU8(src
) * 10;
2854 return MSP_RESULT_ERROR
;
2857 case MSP_SET_CURRENT_METER_CONFIG
:
2858 if (dataSize
== 7) {
2859 batteryMetersConfigMutable()->current
.scale
= sbufReadU16(src
);
2860 batteryMetersConfigMutable()->current
.offset
= sbufReadU16(src
);
2861 batteryMetersConfigMutable()->current
.type
= sbufReadU8(src
);
2862 currentBatteryProfileMutable
->capacity
.value
= sbufReadU16(src
);
2864 return MSP_RESULT_ERROR
;
2868 if (dataSize
== 1) {
2869 sbufReadU8(src
); //This is ignored, no longer supporting mixerMode
2870 mixerUpdateStateFlags(); // Required for correct preset functionality
2872 return MSP_RESULT_ERROR
;
2875 case MSP_SET_RX_CONFIG
:
2876 if (dataSize
== 24) {
2877 rxConfigMutable()->serialrx_provider
= sbufReadU8(src
);
2878 rxConfigMutable()->maxcheck
= sbufReadU16(src
);
2879 sbufReadU16(src
); // midrc
2880 rxConfigMutable()->mincheck
= sbufReadU16(src
);
2881 #ifdef USE_SPEKTRUM_BIND
2882 rxConfigMutable()->spektrum_sat_bind
= sbufReadU8(src
);
2886 rxConfigMutable()->rx_min_usec
= sbufReadU16(src
);
2887 rxConfigMutable()->rx_max_usec
= sbufReadU16(src
);
2888 sbufReadU8(src
); // for compatibility with betaflight (rcInterpolation)
2889 sbufReadU8(src
); // for compatibility with betaflight (rcInterpolationInterval)
2890 sbufReadU16(src
); // for compatibility with betaflight (airModeActivateThreshold)
2894 sbufReadU8(src
); // for compatibility with betaflight (fpvCamAngleDegrees)
2895 rxConfigMutable()->receiverType
= sbufReadU8(src
); // Won't be modified if buffer is not large enough
2897 return MSP_RESULT_ERROR
;
2900 case MSP_SET_FAILSAFE_CONFIG
:
2901 if (dataSize
== 20) {
2902 failsafeConfigMutable()->failsafe_delay
= sbufReadU8(src
);
2903 failsafeConfigMutable()->failsafe_off_delay
= sbufReadU8(src
);
2904 currentBatteryProfileMutable
->failsafe_throttle
= sbufReadU16(src
);
2905 sbufReadU8(src
); // was failsafe_kill_switch
2906 failsafeConfigMutable()->failsafe_throttle_low_delay
= sbufReadU16(src
);
2907 failsafeConfigMutable()->failsafe_procedure
= sbufReadU8(src
);
2908 failsafeConfigMutable()->failsafe_recovery_delay
= sbufReadU8(src
);
2909 failsafeConfigMutable()->failsafe_fw_roll_angle
= (int16_t)sbufReadU16(src
);
2910 failsafeConfigMutable()->failsafe_fw_pitch_angle
= (int16_t)sbufReadU16(src
);
2911 failsafeConfigMutable()->failsafe_fw_yaw_rate
= (int16_t)sbufReadU16(src
);
2912 failsafeConfigMutable()->failsafe_stick_motion_threshold
= sbufReadU16(src
);
2913 failsafeConfigMutable()->failsafe_min_distance
= sbufReadU16(src
);
2914 failsafeConfigMutable()->failsafe_min_distance_procedure
= sbufReadU8(src
);
2916 return MSP_RESULT_ERROR
;
2919 case MSP_SET_RSSI_CONFIG
:
2920 sbufReadU8Safe(&tmp_u8
, src
);
2921 if ((dataSize
== 1) && (tmp_u8
<= MAX_SUPPORTED_RC_CHANNEL_COUNT
)) {
2922 rxConfigMutable()->rssi_channel
= tmp_u8
;
2923 rxUpdateRSSISource();
2925 return MSP_RESULT_ERROR
;
2929 case MSP_SET_RX_MAP
:
2930 if (dataSize
== MAX_MAPPABLE_RX_INPUTS
) {
2931 for (int i
= 0; i
< MAX_MAPPABLE_RX_INPUTS
; i
++) {
2932 rxConfigMutable()->rcmap
[i
] = sbufReadU8(src
);
2935 return MSP_RESULT_ERROR
;
2938 case MSP2_COMMON_SET_SERIAL_CONFIG
:
2940 uint8_t portConfigSize
= sizeof(uint8_t) + sizeof(uint32_t) + (sizeof(uint8_t) * 4);
2942 if (dataSize
% portConfigSize
!= 0) {
2943 return MSP_RESULT_ERROR
;
2946 uint8_t remainingPortsInPacket
= dataSize
/ portConfigSize
;
2948 while (remainingPortsInPacket
--) {
2949 uint8_t identifier
= sbufReadU8(src
);
2951 serialPortConfig_t
*portConfig
= serialFindPortConfiguration(identifier
);
2953 return MSP_RESULT_ERROR
;
2956 portConfig
->identifier
= identifier
;
2957 portConfig
->functionMask
= sbufReadU32(src
);
2958 portConfig
->msp_baudrateIndex
= constrain(sbufReadU8(src
), BAUD_MIN
, BAUD_MAX
);
2959 portConfig
->gps_baudrateIndex
= constrain(sbufReadU8(src
), BAUD_MIN
, BAUD_MAX
);
2960 portConfig
->telemetry_baudrateIndex
= constrain(sbufReadU8(src
), BAUD_MIN
, BAUD_MAX
);
2961 portConfig
->peripheral_baudrateIndex
= constrain(sbufReadU8(src
), BAUD_MIN
, BAUD_MAX
);
2966 #ifdef USE_LED_STRIP
2967 case MSP_SET_LED_COLORS
:
2968 if (dataSize
== LED_CONFIGURABLE_COLOR_COUNT
* 4) {
2969 for (int i
= 0; i
< LED_CONFIGURABLE_COLOR_COUNT
; i
++) {
2970 hsvColor_t
*color
= &ledStripConfigMutable()->colors
[i
];
2971 color
->h
= sbufReadU16(src
);
2972 color
->s
= sbufReadU8(src
);
2973 color
->v
= sbufReadU8(src
);
2976 return MSP_RESULT_ERROR
;
2979 case MSP_SET_LED_STRIP_CONFIG
:
2980 if (dataSize
== (1 + sizeof(uint32_t))) {
2981 tmp_u8
= sbufReadU8(src
);
2982 if (tmp_u8
>= LED_MAX_STRIP_LENGTH
) {
2983 return MSP_RESULT_ERROR
;
2985 ledConfig_t
*ledConfig
= &ledStripConfigMutable()->ledConfigs
[tmp_u8
];
2987 uint32_t legacyConfig
= sbufReadU32(src
);
2989 ledConfig
->led_position
= legacyConfig
& 0xFF;
2990 ledConfig
->led_function
= (legacyConfig
>> 8) & 0xF;
2991 ledConfig
->led_overlay
= (legacyConfig
>> 12) & 0x3F;
2992 ledConfig
->led_color
= (legacyConfig
>> 18) & 0xF;
2993 ledConfig
->led_direction
= (legacyConfig
>> 22) & 0x3F;
2994 ledConfig
->led_params
= (legacyConfig
>> 28) & 0xF;
2996 reevaluateLedConfig();
2998 return MSP_RESULT_ERROR
;
3001 case MSP2_INAV_SET_LED_STRIP_CONFIG_EX
:
3002 if (dataSize
== (1 + sizeof(ledConfig_t
))) {
3003 tmp_u8
= sbufReadU8(src
);
3004 if (tmp_u8
>= LED_MAX_STRIP_LENGTH
) {
3005 return MSP_RESULT_ERROR
;
3007 ledConfig_t
*ledConfig
= &ledStripConfigMutable()->ledConfigs
[tmp_u8
];
3008 sbufReadDataSafe(src
, ledConfig
, sizeof(ledConfig_t
));
3009 reevaluateLedConfig();
3011 return MSP_RESULT_ERROR
;
3014 case MSP_SET_LED_STRIP_MODECOLOR
:
3015 if (dataSize
== 3) {
3016 ledModeIndex_e modeIdx
= sbufReadU8(src
);
3017 int funIdx
= sbufReadU8(src
);
3018 int color
= sbufReadU8(src
);
3020 if (!setModeColor(modeIdx
, funIdx
, color
))
3021 return MSP_RESULT_ERROR
;
3023 return MSP_RESULT_ERROR
;
3027 #ifdef NAV_NON_VOLATILE_WAYPOINT_STORAGE
3028 case MSP_WP_MISSION_LOAD
:
3029 sbufReadU8Safe(NULL
, src
); // Mission ID (reserved)
3030 if ((dataSize
!= 1) || (!loadNonVolatileWaypointList(false)))
3031 return MSP_RESULT_ERROR
;
3034 case MSP_WP_MISSION_SAVE
:
3035 sbufReadU8Safe(NULL
, src
); // Mission ID (reserved)
3036 if ((dataSize
!= 1) || (!saveNonVolatileWaypointList()))
3037 return MSP_RESULT_ERROR
;
3042 if (dataSize
== 6) {
3043 // Use seconds and milliseconds to make senders
3044 // easier to implement. Generating a 64 bit value
3045 // might not be trivial in some platforms.
3046 int32_t secs
= (int32_t)sbufReadU32(src
);
3047 uint16_t millis
= sbufReadU16(src
);
3048 rtcTime_t t
= rtcTimeMake(secs
, millis
);
3051 return MSP_RESULT_ERROR
;
3054 case MSP_SET_TX_INFO
:
3056 // This message will be sent while the aircraft is
3057 // armed. Better to guard ourselves against potentially
3058 // malformed requests.
3060 if (sbufReadU8Safe(&rssi
, src
)) {
3061 setRSSIFromMSP(rssi
);
3067 if (dataSize
<= MAX_NAME_LENGTH
) {
3068 char *name
= systemConfigMutable()->craftName
;
3069 int len
= MIN(MAX_NAME_LENGTH
, (int)dataSize
);
3070 sbufReadData(src
, name
, len
);
3071 memset(&name
[len
], '\0', (MAX_NAME_LENGTH
+ 1) - len
);
3073 return MSP_RESULT_ERROR
;
3076 case MSP2_COMMON_SET_TZ
:
3078 timeConfigMutable()->tz_offset
= (int16_t)sbufReadU16(src
);
3079 else if (dataSize
== 3) {
3080 timeConfigMutable()->tz_offset
= (int16_t)sbufReadU16(src
);
3081 timeConfigMutable()->tz_automatic_dst
= (uint8_t)sbufReadU8(src
);
3083 return MSP_RESULT_ERROR
;
3086 case MSP2_INAV_SET_MIXER
:
3087 if (dataSize
== 9) {
3088 mixerConfigMutable()->motorDirectionInverted
= sbufReadU8(src
);
3089 sbufReadU8(src
); // Was yaw_jump_prevention_limit
3090 mixerConfigMutable()->motorstopOnLow
= sbufReadU8(src
);
3091 mixerConfigMutable()->platformType
= sbufReadU8(src
);
3092 mixerConfigMutable()->hasFlaps
= sbufReadU8(src
);
3093 mixerConfigMutable()->appliedMixerPreset
= sbufReadU16(src
);
3094 sbufReadU8(src
); //Read and ignore MAX_SUPPORTED_MOTORS
3095 sbufReadU8(src
); //Read and ignore MAX_SUPPORTED_SERVOS
3096 mixerUpdateStateFlags();
3098 return MSP_RESULT_ERROR
;
3101 #if defined(USE_OSD)
3102 case MSP2_INAV_OSD_SET_LAYOUT_ITEM
:
3105 if (!sbufReadU8Safe(&layout
, src
)) {
3106 return MSP_RESULT_ERROR
;
3109 if (!sbufReadU8Safe(&item
, src
)) {
3110 return MSP_RESULT_ERROR
;
3112 if (!sbufReadU16Safe(&osdLayoutsConfigMutable()->item_pos
[layout
][item
], src
)) {
3113 return MSP_RESULT_ERROR
;
3115 // If the layout is not already overriden and it's different
3116 // than the layout for the item that was just configured,
3117 // override it for 10 seconds.
3119 int activeLayout
= osdGetActiveLayout(&overridden
);
3120 if (activeLayout
!= layout
&& !overridden
) {
3121 osdOverrideLayout(layout
, 10000);
3123 osdStartFullRedraw();
3129 case MSP2_INAV_OSD_SET_ALARMS
:
3131 if (dataSize
== 24) {
3132 osdConfigMutable()->rssi_alarm
= sbufReadU8(src
);
3133 osdConfigMutable()->time_alarm
= sbufReadU16(src
);
3134 osdConfigMutable()->alt_alarm
= sbufReadU16(src
);
3135 osdConfigMutable()->dist_alarm
= sbufReadU16(src
);
3136 osdConfigMutable()->neg_alt_alarm
= sbufReadU16(src
);
3137 tmp_u16
= sbufReadU16(src
);
3138 osdConfigMutable()->gforce_alarm
= tmp_u16
/ 1000.0f
;
3139 tmp_u16
= sbufReadU16(src
);
3140 osdConfigMutable()->gforce_axis_alarm_min
= (int16_t)tmp_u16
/ 1000.0f
;
3141 tmp_u16
= sbufReadU16(src
);
3142 osdConfigMutable()->gforce_axis_alarm_max
= (int16_t)tmp_u16
/ 1000.0f
;
3143 osdConfigMutable()->current_alarm
= sbufReadU8(src
);
3144 osdConfigMutable()->imu_temp_alarm_min
= sbufReadU16(src
);
3145 osdConfigMutable()->imu_temp_alarm_max
= sbufReadU16(src
);
3147 osdConfigMutable()->baro_temp_alarm_min
= sbufReadU16(src
);
3148 osdConfigMutable()->baro_temp_alarm_max
= sbufReadU16(src
);
3151 return MSP_RESULT_ERROR
;
3156 case MSP2_INAV_OSD_SET_PREFERENCES
:
3158 if (dataSize
== 9) {
3159 osdConfigMutable()->video_system
= sbufReadU8(src
);
3160 osdConfigMutable()->main_voltage_decimals
= sbufReadU8(src
);
3161 osdConfigMutable()->ahi_reverse_roll
= sbufReadU8(src
);
3162 osdConfigMutable()->crosshairs_style
= sbufReadU8(src
);
3163 osdConfigMutable()->left_sidebar_scroll
= sbufReadU8(src
);
3164 osdConfigMutable()->right_sidebar_scroll
= sbufReadU8(src
);
3165 osdConfigMutable()->sidebar_scroll_arrows
= sbufReadU8(src
);
3166 osdConfigMutable()->units
= sbufReadU8(src
);
3167 osdConfigMutable()->stats_energy_unit
= sbufReadU8(src
);
3168 osdStartFullRedraw();
3170 return MSP_RESULT_ERROR
;
3176 case MSP2_INAV_SET_MC_BRAKING
:
3177 #ifdef USE_MR_BRAKING_MODE
3178 if (dataSize
== 14) {
3179 navConfigMutable()->mc
.braking_speed_threshold
= sbufReadU16(src
);
3180 navConfigMutable()->mc
.braking_disengage_speed
= sbufReadU16(src
);
3181 navConfigMutable()->mc
.braking_timeout
= sbufReadU16(src
);
3182 navConfigMutable()->mc
.braking_boost_factor
= sbufReadU8(src
);
3183 navConfigMutable()->mc
.braking_boost_timeout
= sbufReadU16(src
);
3184 navConfigMutable()->mc
.braking_boost_speed_threshold
= sbufReadU16(src
);
3185 navConfigMutable()->mc
.braking_boost_disengage_speed
= sbufReadU16(src
);
3186 navConfigMutable()->mc
.braking_bank_angle
= sbufReadU8(src
);
3189 return MSP_RESULT_ERROR
;
3192 case MSP2_INAV_SELECT_BATTERY_PROFILE
:
3193 if (!ARMING_FLAG(ARMED
) && sbufReadU8Safe(&tmp_u8
, src
)) {
3194 setConfigBatteryProfileAndWriteEEPROM(tmp_u8
);
3196 return MSP_RESULT_ERROR
;
3200 case MSP2_INAV_SELECT_MIXER_PROFILE
:
3201 if (!ARMING_FLAG(ARMED
) && sbufReadU8Safe(&tmp_u8
, src
)) {
3202 setConfigMixerProfileAndWriteEEPROM(tmp_u8
);
3204 return MSP_RESULT_ERROR
;
3208 #ifdef USE_TEMPERATURE_SENSOR
3209 case MSP2_INAV_SET_TEMP_SENSOR_CONFIG
:
3210 if (dataSize
== sizeof(tempSensorConfig_t
) * MAX_TEMP_SENSORS
) {
3211 for (uint8_t index
= 0; index
< MAX_TEMP_SENSORS
; ++index
) {
3212 tempSensorConfig_t
*sensorConfig
= tempSensorConfigMutable(index
);
3213 sensorConfig
->type
= sbufReadU8(src
);
3214 for (uint8_t addrIndex
= 0; addrIndex
< 8; ++addrIndex
)
3215 ((uint8_t *)&sensorConfig
->address
)[addrIndex
] = sbufReadU8(src
);
3216 sensorConfig
->alarm_min
= sbufReadU16(src
);
3217 sensorConfig
->alarm_max
= sbufReadU16(src
);
3218 tmp_u8
= sbufReadU8(src
);
3219 sensorConfig
->osdSymbol
= tmp_u8
> TEMP_SENSOR_SYM_COUNT
? 0 : tmp_u8
;
3220 for (uint8_t labelIndex
= 0; labelIndex
< TEMPERATURE_LABEL_LEN
; ++labelIndex
)
3221 sensorConfig
->label
[labelIndex
] = toupper(sbufReadU8(src
));
3224 return MSP_RESULT_ERROR
;
3228 #ifdef MSP_FIRMWARE_UPDATE
3229 case MSP2_INAV_FWUPDT_PREPARE
:
3230 if (!firmwareUpdatePrepare(sbufReadU32(src
))) {
3231 return MSP_RESULT_ERROR
;
3234 case MSP2_INAV_FWUPDT_STORE
:
3235 if (!firmwareUpdateStore(sbufPtr(src
), sbufBytesRemaining(src
))) {
3236 return MSP_RESULT_ERROR
;
3239 case MSP2_INAV_FWUPDT_EXEC
:
3240 firmwareUpdateExec(sbufReadU8(src
));
3241 return MSP_RESULT_ERROR
; // will only be reached if the update is not ready
3243 case MSP2_INAV_FWUPDT_ROLLBACK_PREPARE
:
3244 if (!firmwareUpdateRollbackPrepare()) {
3245 return MSP_RESULT_ERROR
;
3248 case MSP2_INAV_FWUPDT_ROLLBACK_EXEC
:
3249 firmwareUpdateRollbackExec();
3250 return MSP_RESULT_ERROR
; // will only be reached if the rollback is not ready
3253 #ifdef USE_SAFE_HOME
3254 case MSP2_INAV_SET_SAFEHOME
:
3255 if (dataSize
== 10) {
3257 if (!sbufReadU8Safe(&i
, src
) || i
>= MAX_SAFE_HOMES
) {
3258 return MSP_RESULT_ERROR
;
3260 safeHomeConfigMutable(i
)->enabled
= sbufReadU8(src
);
3261 safeHomeConfigMutable(i
)->lat
= sbufReadU32(src
);
3262 safeHomeConfigMutable(i
)->lon
= sbufReadU32(src
);
3263 #ifdef USE_FW_AUTOLAND
3264 resetFwAutolandApproach(i
);
3267 return MSP_RESULT_ERROR
;
3272 #ifdef USE_FW_AUTOLAND
3273 case MSP2_INAV_SET_FW_APPROACH
:
3274 if (dataSize
== 15) {
3276 if (!sbufReadU8Safe(&i
, src
) || i
>= MAX_FW_LAND_APPOACH_SETTINGS
) {
3277 return MSP_RESULT_ERROR
;
3279 fwAutolandApproachConfigMutable(i
)->approachAlt
= sbufReadU32(src
);
3280 fwAutolandApproachConfigMutable(i
)->landAlt
= sbufReadU32(src
);
3281 fwAutolandApproachConfigMutable(i
)->approachDirection
= sbufReadU8(src
);
3283 int16_t head1
= 0, head2
= 0;
3284 if (sbufReadI16Safe(&head1
, src
)) {
3285 fwAutolandApproachConfigMutable(i
)->landApproachHeading1
= head1
;
3287 if (sbufReadI16Safe(&head2
, src
)) {
3288 fwAutolandApproachConfigMutable(i
)->landApproachHeading2
= head2
;
3290 fwAutolandApproachConfigMutable(i
)->isSeaLevelRef
= sbufReadU8(src
);
3292 return MSP_RESULT_ERROR
;
3296 case MSP2_INAV_GPS_UBLOX_COMMAND
:
3297 if(dataSize
< 8 || !isGpsUblox()) {
3298 return MSP_RESULT_ERROR
;
3301 gpsUbloxSendCommand(src
->ptr
, dataSize
, 0);
3306 case MSP2_INAV_EZ_TUNE_SET
:
3309 if (dataSize
< 10 || dataSize
> 11) {
3310 return MSP_RESULT_ERROR
;
3313 ezTuneMutable()->enabled
= sbufReadU8(src
);
3314 ezTuneMutable()->filterHz
= sbufReadU16(src
);
3315 ezTuneMutable()->axisRatio
= sbufReadU8(src
);
3316 ezTuneMutable()->response
= sbufReadU8(src
);
3317 ezTuneMutable()->damping
= sbufReadU8(src
);
3318 ezTuneMutable()->stability
= sbufReadU8(src
);
3319 ezTuneMutable()->aggressiveness
= sbufReadU8(src
);
3320 ezTuneMutable()->rate
= sbufReadU8(src
);
3321 ezTuneMutable()->expo
= sbufReadU8(src
);
3323 if (dataSize
== 11) {
3324 ezTuneMutable()->snappiness
= sbufReadU8(src
);
3332 #ifdef USE_RATE_DYNAMICS
3334 case MSP2_INAV_SET_RATE_DYNAMICS
:
3336 if (dataSize
== 6) {
3337 ((controlRateConfig_t
*)currentControlRateProfile
)->rateDynamics
.sensitivityCenter
= sbufReadU8(src
);
3338 ((controlRateConfig_t
*)currentControlRateProfile
)->rateDynamics
.sensitivityEnd
= sbufReadU8(src
);
3339 ((controlRateConfig_t
*)currentControlRateProfile
)->rateDynamics
.correctionCenter
= sbufReadU8(src
);
3340 ((controlRateConfig_t
*)currentControlRateProfile
)->rateDynamics
.correctionEnd
= sbufReadU8(src
);
3341 ((controlRateConfig_t
*)currentControlRateProfile
)->rateDynamics
.weightCenter
= sbufReadU8(src
);
3342 ((controlRateConfig_t
*)currentControlRateProfile
)->rateDynamics
.weightEnd
= sbufReadU8(src
);
3345 return MSP_RESULT_ERROR
;
3351 #ifdef USE_PROGRAMMING_FRAMEWORK
3352 case MSP2_INAV_SET_CUSTOM_OSD_ELEMENTS
:
3353 sbufReadU8Safe(&tmp_u8
, src
);
3354 if ((dataSize
== (OSD_CUSTOM_ELEMENT_TEXT_SIZE
- 1) + (MAX_CUSTOM_ELEMENTS
* 3) + 4) && (tmp_u8
< MAX_CUSTOM_ELEMENTS
)) {
3355 for (int i
= 0; i
< CUSTOM_ELEMENTS_PARTS
; i
++) {
3356 osdCustomElementsMutable(tmp_u8
)->part
[i
].type
= sbufReadU8(src
);
3357 osdCustomElementsMutable(tmp_u8
)->part
[i
].value
= sbufReadU16(src
);
3359 osdCustomElementsMutable(tmp_u8
)->visibility
.type
= sbufReadU8(src
);
3360 osdCustomElementsMutable(tmp_u8
)->visibility
.value
= sbufReadU16(src
);
3361 for (int i
= 0; i
< OSD_CUSTOM_ELEMENT_TEXT_SIZE
- 1; i
++) {
3362 osdCustomElementsMutable(tmp_u8
)->osdCustomElementText
[i
] = sbufReadU8(src
);
3364 osdCustomElementsMutable(tmp_u8
)->osdCustomElementText
[OSD_CUSTOM_ELEMENT_TEXT_SIZE
- 1] = '\0';
3366 return MSP_RESULT_ERROR
;
3371 case MSP2_BETAFLIGHT_BIND
:
3372 if (rxConfig()->receiverType
== RX_TYPE_SERIAL
) {
3373 switch (rxConfig()->serialrx_provider
) {
3375 return MSP_RESULT_ERROR
;
3376 #if defined(USE_SERIALRX_SRXL2)
3377 case SERIALRX_SRXL2
:
3381 #if defined(USE_SERIALRX_CRSF)
3388 return MSP_RESULT_ERROR
;
3393 return MSP_RESULT_ERROR
;
3395 return MSP_RESULT_ACK
;
3399 static const setting_t
*mspReadSetting(sbuf_t
*src
)
3401 char name
[SETTING_MAX_NAME_LENGTH
];
3406 if (!sbufReadU8Safe(&c
, src
)) {
3412 // Payload starts with a zero, setting index
3413 // as uint16_t follows
3414 if (sbufReadU16Safe(&id
, src
)) {
3415 return settingGet(id
);
3421 if (s
== SETTING_MAX_NAME_LENGTH
) {
3426 return settingFind(name
);
3429 static bool mspSettingCommand(sbuf_t
*dst
, sbuf_t
*src
)
3431 const setting_t
*setting
= mspReadSetting(src
);
3436 const void *ptr
= settingGetValuePointer(setting
);
3437 size_t size
= settingGetValueSize(setting
);
3438 sbufWriteDataSafe(dst
, ptr
, size
);
3442 static bool mspSetSettingCommand(sbuf_t
*dst
, sbuf_t
*src
)
3446 const setting_t
*setting
= mspReadSetting(src
);
3451 setting_min_t min
= settingGetMin(setting
);
3452 setting_max_t max
= settingGetMax(setting
);
3454 void *ptr
= settingGetValuePointer(setting
);
3455 switch (SETTING_TYPE(setting
)) {
3459 if (!sbufReadU8Safe(&val
, src
)) {
3465 *((uint8_t*)ptr
) = val
;
3471 if (!sbufReadI8Safe(&val
, src
)) {
3474 if (val
< min
|| val
> (int8_t)max
) {
3477 *((int8_t*)ptr
) = val
;
3483 if (!sbufReadU16Safe(&val
, src
)) {
3489 *((uint16_t*)ptr
) = val
;
3495 if (!sbufReadI16Safe(&val
, src
)) {
3498 if (val
< min
|| val
> (int16_t)max
) {
3501 *((int16_t*)ptr
) = val
;
3507 if (!sbufReadU32Safe(&val
, src
)) {
3513 *((uint32_t*)ptr
) = val
;
3519 if (!sbufReadDataSafe(src
, &val
, sizeof(float))) {
3522 if (val
< (float)min
|| val
> (float)max
) {
3525 *((float*)ptr
) = val
;
3530 settingSetString(setting
, (const char*)sbufPtr(src
), sbufBytesRemaining(src
));
3538 static bool mspSettingInfoCommand(sbuf_t
*dst
, sbuf_t
*src
)
3540 const setting_t
*setting
= mspReadSetting(src
);
3545 char name_buf
[SETTING_MAX_WORD_LENGTH
+1];
3546 settingGetName(setting
, name_buf
);
3547 sbufWriteDataSafe(dst
, name_buf
, strlen(name_buf
) + 1);
3549 // Parameter Group ID
3550 sbufWriteU16(dst
, settingGetPgn(setting
));
3552 // Type, section and mode
3553 sbufWriteU8(dst
, SETTING_TYPE(setting
));
3554 sbufWriteU8(dst
, SETTING_SECTION(setting
));
3555 sbufWriteU8(dst
, SETTING_MODE(setting
));
3558 int32_t min
= settingGetMin(setting
);
3559 sbufWriteU32(dst
, (uint32_t)min
);
3561 uint32_t max
= settingGetMax(setting
);
3562 sbufWriteU32(dst
, max
);
3564 // Absolute setting index
3565 sbufWriteU16(dst
, settingGetIndex(setting
));
3567 // If the setting is profile based, send the current one
3568 // and the count, both as uint8_t. For MASTER_VALUE, we
3569 // send two zeroes, so the MSP client can assume there
3570 // will always be two bytes.
3571 switch (SETTING_SECTION(setting
)) {
3573 sbufWriteU8(dst
, 0);
3574 sbufWriteU8(dst
, 0);
3580 case CONTROL_RATE_VALUE
:
3581 sbufWriteU8(dst
, getConfigProfile());
3582 sbufWriteU8(dst
, MAX_PROFILE_COUNT
);
3584 case BATTERY_CONFIG_VALUE
:
3585 sbufWriteU8(dst
, getConfigBatteryProfile());
3586 sbufWriteU8(dst
, MAX_BATTERY_PROFILE_COUNT
);
3588 case MIXER_CONFIG_VALUE
:
3589 sbufWriteU8(dst
, getConfigMixerProfile());
3590 sbufWriteU8(dst
, MAX_MIXER_PROFILE_COUNT
);
3594 // If the setting uses a table, send each possible string (null terminated)
3595 if (SETTING_MODE(setting
) == MODE_LOOKUP
) {
3596 for (int ii
= (int)min
; ii
<= (int)max
; ii
++) {
3597 const char *name
= settingLookupValueName(setting
, ii
);
3598 sbufWriteDataSafe(dst
, name
, strlen(name
) + 1);
3602 // Finally, include the setting value. This way resource constrained callers
3603 // (e.g. a script in the radio) don't need to perform another call to retrieve
3605 const void *ptr
= settingGetValuePointer(setting
);
3606 size_t size
= settingGetValueSize(setting
);
3607 sbufWriteDataSafe(dst
, ptr
, size
);
3612 static bool mspParameterGroupsCommand(sbuf_t
*dst
, sbuf_t
*src
)
3619 if (sbufReadU16Safe(&first
, src
)) {
3622 first
= PG_ID_FIRST
;
3626 for (int ii
= first
; ii
<= last
; ii
++) {
3627 if (settingsGetParameterGroupIndexes(ii
, &start
, &end
)) {
3628 sbufWriteU16(dst
, ii
);
3629 sbufWriteU16(dst
, start
);
3630 sbufWriteU16(dst
, end
);
3636 #ifdef USE_SIMULATOR
3637 bool isOSDTypeSupportedBySimulator(void)
3640 displayPort_t
*osdDisplayPort
= osdGetDisplayPort();
3641 return (!!osdDisplayPort
&& !!osdDisplayPort
->vTable
->readChar
);
3647 void mspWriteSimulatorOSD(sbuf_t
*dst
)
3650 //scan displayBuffer iteratively
3651 //no more than 80+3+2 bytes output in single run
3652 //0 and 255 are special symbols
3653 //255 [char] - font bank switch
3654 //0 [flags,count] [char] - font bank switch, blink switch and character repeat
3655 //original 0 is sent as 32
3656 //original 0xff, 0x100 and 0x1ff are forcibly sent inside command 0
3658 static uint8_t osdPos_y
= 0;
3659 static uint8_t osdPos_x
= 0;
3661 //indicate new format hitl 1.4.0
3662 sbufWriteU8(dst
, 255);
3664 if (isOSDTypeSupportedBySimulator())
3666 displayPort_t
*osdDisplayPort
= osdGetDisplayPort();
3668 sbufWriteU8(dst
, osdDisplayPort
->rows
);
3669 sbufWriteU8(dst
, osdDisplayPort
->cols
);
3671 sbufWriteU8(dst
, osdPos_y
);
3672 sbufWriteU8(dst
, osdPos_x
);
3677 textAttributes_t attr
= 0;
3678 bool highBank
= false;
3682 int processedRows
= osdDisplayPort
->rows
;
3684 while (bytesCount
< 80) //whole response should be less 155 bytes at worst.
3692 displayReadCharWithAttr(osdDisplayPort
, osdPos_x
, osdPos_y
, &c
, &attr
);
3695 //REVIEW: displayReadCharWithAttr() should return mode with _TEXT_ATTRIBUTES_BLINK_BIT !
3696 //for max7456 it returns mode with MAX7456_MODE_BLINK instead (wrong)
3697 //because max7456ReadChar() does not decode from MAX7456_MODE_BLINK to _TEXT_ATTRIBUTES_BLINK_BIT
3700 //bool blink2 = TEXT_ATTRIBUTES_HAVE_BLINK(attr);
3701 bool blink2
= attr
& (1<<4); //MAX7456_MODE_BLINK
3708 else if ((lastChar
!= c
) || (blink2
!= blink1
) || (count
== 63))
3716 if (osdPos_x
== osdDisplayPort
->cols
)
3721 if (osdPos_y
== osdDisplayPort
->rows
)
3729 uint8_t lastCharLow
= (uint8_t)(lastChar
& 0xff);
3730 if (blink1
!= blink
)
3732 cmd
|= 128;//switch blink attr
3736 bool highBank1
= lastChar
> 255;
3737 if (highBank1
!= highBank
)
3739 cmd
|= 64;//switch bank attr
3740 highBank
= highBank1
;
3743 if (count
== 1 && cmd
== 64)
3745 sbufWriteU8(dst
, 255); //short command for bank switch with char following
3746 sbufWriteU8(dst
, lastChar
& 0xff);
3749 else if ((count
> 2) || (cmd
!=0) || (lastChar
== 255) || (lastChar
== 0x100) || (lastChar
== 0x1ff))
3751 cmd
|= count
; //long command for blink/bank switch and symbol repeat
3752 sbufWriteU8(dst
, 0);
3753 sbufWriteU8(dst
, cmd
);
3754 sbufWriteU8(dst
, lastCharLow
);
3757 else if (count
== 2) //cmd == 0 here
3759 sbufWriteU8(dst
, lastCharLow
);
3760 sbufWriteU8(dst
, lastCharLow
);
3765 sbufWriteU8(dst
, lastCharLow
);
3769 if ( processedRows
<= 0 )
3774 sbufWriteU8(dst
, 0); //command 0 with length=0 -> stop
3775 sbufWriteU8(dst
, 0);
3779 sbufWriteU8(dst
, 0);
3784 bool mspFCProcessInOutCommand(uint16_t cmdMSP
, sbuf_t
*dst
, sbuf_t
*src
, mspResult_e
*ret
)
3787 const unsigned int dataSize
= sbufBytesRemaining(src
);
3792 mspFcWaypointOutCommand(dst
, src
);
3793 *ret
= MSP_RESULT_ACK
;
3796 #if defined(USE_FLASHFS)
3797 case MSP_DATAFLASH_READ
:
3798 mspFcDataFlashReadCommand(dst
, src
);
3799 *ret
= MSP_RESULT_ACK
;
3803 case MSP2_COMMON_SETTING
:
3804 *ret
= mspSettingCommand(dst
, src
) ? MSP_RESULT_ACK
: MSP_RESULT_ERROR
;
3807 case MSP2_COMMON_SET_SETTING
:
3808 *ret
= mspSetSettingCommand(dst
, src
) ? MSP_RESULT_ACK
: MSP_RESULT_ERROR
;
3811 case MSP2_COMMON_SETTING_INFO
:
3812 *ret
= mspSettingInfoCommand(dst
, src
) ? MSP_RESULT_ACK
: MSP_RESULT_ERROR
;
3815 case MSP2_COMMON_PG_LIST
:
3816 *ret
= mspParameterGroupsCommand(dst
, src
) ? MSP_RESULT_ACK
: MSP_RESULT_ERROR
;
3819 #if defined(USE_OSD)
3820 case MSP2_INAV_OSD_LAYOUTS
:
3821 if (sbufBytesRemaining(src
) >= 1) {
3822 uint8_t layout
= sbufReadU8(src
);
3823 if (layout
>= OSD_LAYOUT_COUNT
) {
3824 *ret
= MSP_RESULT_ERROR
;
3827 if (sbufBytesRemaining(src
) >= 2) {
3828 // Asking for an specific item in a layout
3829 uint16_t item
= sbufReadU16(src
);
3830 if (item
>= OSD_ITEM_COUNT
) {
3831 *ret
= MSP_RESULT_ERROR
;
3834 sbufWriteU16(dst
, osdLayoutsConfig()->item_pos
[layout
][item
]);
3836 // Asking for an specific layout
3837 for (unsigned ii
= 0; ii
< OSD_ITEM_COUNT
; ii
++) {
3838 sbufWriteU16(dst
, osdLayoutsConfig()->item_pos
[layout
][ii
]);
3842 // Return the number of layouts and items
3843 sbufWriteU8(dst
, OSD_LAYOUT_COUNT
);
3844 sbufWriteU8(dst
, OSD_ITEM_COUNT
);
3846 *ret
= MSP_RESULT_ACK
;
3850 #ifdef USE_PROGRAMMING_FRAMEWORK
3851 case MSP2_INAV_LOGIC_CONDITIONS_SINGLE
:
3852 *ret
= mspFcLogicConditionCommand(dst
, src
);
3855 #ifdef USE_SAFE_HOME
3856 case MSP2_INAV_SAFEHOME
:
3857 *ret
= mspFcSafeHomeOutCommand(dst
, src
);
3860 #ifdef USE_FW_AUTOLAND
3861 case MSP2_INAV_FW_APPROACH
:
3862 *ret
= mspFwApproachOutCommand(dst
, src
);
3865 #ifdef USE_SIMULATOR
3867 tmp_u8
= sbufReadU8(src
); // Get the Simulator MSP version
3869 // Check the MSP version of simulator
3870 if (tmp_u8
!= SIMULATOR_MSP_VERSION
) {
3874 simulatorData
.flags
= sbufReadU8(src
);
3876 if (!SIMULATOR_HAS_OPTION(HITL_ENABLE
)) {
3878 if (ARMING_FLAG(SIMULATOR_MODE_HITL
)) { // Just once
3879 DISABLE_ARMING_FLAG(SIMULATOR_MODE_HITL
);
3882 if ( requestedSensors
[SENSOR_INDEX_BARO
] != BARO_NONE
) {
3883 baroStartCalibration();
3887 DISABLE_STATE(COMPASS_CALIBRATED
);
3890 simulatorData
.flags
= HITL_RESET_FLAGS
;
3891 // Review: Many states were affected. Reboot?
3893 disarm(DISARM_SWITCH
); // Disarm to prevent motor output!!!
3896 if (!ARMING_FLAG(SIMULATOR_MODE_HITL
)) { // Just once
3898 if ( requestedSensors
[SENSOR_INDEX_BARO
] != BARO_NONE
) {
3899 sensorsSet(SENSOR_BARO
);
3900 setTaskEnabled(TASK_BARO
, true);
3901 DISABLE_ARMING_FLAG(ARMING_DISABLED_HARDWARE_FAILURE
);
3902 baroStartCalibration();
3907 if (compassConfig()->mag_hardware
!= MAG_NONE
) {
3908 sensorsSet(SENSOR_MAG
);
3909 ENABLE_STATE(COMPASS_CALIBRATED
);
3910 DISABLE_ARMING_FLAG(ARMING_DISABLED_HARDWARE_FAILURE
);
3911 mag
.magADC
[X
] = 800;
3916 ENABLE_ARMING_FLAG(SIMULATOR_MODE_HITL
);
3917 ENABLE_STATE(ACCELEROMETER_CALIBRATED
);
3918 LOG_DEBUG(SYSTEM
, "Simulator enabled");
3921 if (dataSize
>= 14) {
3923 if (feature(FEATURE_GPS
) && SIMULATOR_HAS_OPTION(HITL_HAS_NEW_GPS_DATA
)) {
3924 gpsSolDRV
.fixType
= sbufReadU8(src
);
3925 gpsSolDRV
.hdop
= gpsSolDRV
.fixType
== GPS_NO_FIX
? 9999 : 100;
3926 gpsSolDRV
.numSat
= sbufReadU8(src
);
3928 if (gpsSolDRV
.fixType
!= GPS_NO_FIX
) {
3929 gpsSolDRV
.flags
.validVelNE
= true;
3930 gpsSolDRV
.flags
.validVelD
= true;
3931 gpsSolDRV
.flags
.validEPE
= true;
3932 gpsSolDRV
.flags
.validTime
= false;
3934 gpsSolDRV
.llh
.lat
= sbufReadU32(src
);
3935 gpsSolDRV
.llh
.lon
= sbufReadU32(src
);
3936 gpsSolDRV
.llh
.alt
= sbufReadU32(src
);
3937 gpsSolDRV
.groundSpeed
= (int16_t)sbufReadU16(src
);
3938 gpsSolDRV
.groundCourse
= (int16_t)sbufReadU16(src
);
3940 gpsSolDRV
.velNED
[X
] = (int16_t)sbufReadU16(src
);
3941 gpsSolDRV
.velNED
[Y
] = (int16_t)sbufReadU16(src
);
3942 gpsSolDRV
.velNED
[Z
] = (int16_t)sbufReadU16(src
);
3944 gpsSolDRV
.eph
= 100;
3945 gpsSolDRV
.epv
= 100;
3947 sbufAdvance(src
, sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint16_t) * 3);
3949 // Feed data to navigation
3950 gpsProcessNewDriverData();
3951 gpsProcessNewSolutionData(false);
3953 sbufAdvance(src
, sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint16_t) * 3);
3956 if (!SIMULATOR_HAS_OPTION(HITL_USE_IMU
)) {
3957 attitude
.values
.roll
= (int16_t)sbufReadU16(src
);
3958 attitude
.values
.pitch
= (int16_t)sbufReadU16(src
);
3959 attitude
.values
.yaw
= (int16_t)sbufReadU16(src
);
3961 sbufAdvance(src
, sizeof(uint16_t) * XYZ_AXIS_COUNT
);
3964 // Get the acceleration in 1G units
3965 acc
.accADCf
[X
] = ((int16_t)sbufReadU16(src
)) / 1000.0f
;
3966 acc
.accADCf
[Y
] = ((int16_t)sbufReadU16(src
)) / 1000.0f
;
3967 acc
.accADCf
[Z
] = ((int16_t)sbufReadU16(src
)) / 1000.0f
;
3968 acc
.accVibeSq
[X
] = 0.0f
;
3969 acc
.accVibeSq
[Y
] = 0.0f
;
3970 acc
.accVibeSq
[Z
] = 0.0f
;
3972 // Get the angular velocity in DPS
3973 gyro
.gyroADCf
[X
] = ((int16_t)sbufReadU16(src
)) / 16.0f
;
3974 gyro
.gyroADCf
[Y
] = ((int16_t)sbufReadU16(src
)) / 16.0f
;
3975 gyro
.gyroADCf
[Z
] = ((int16_t)sbufReadU16(src
)) / 16.0f
;
3977 if (sensors(SENSOR_BARO
)) {
3978 baro
.baroPressure
= (int32_t)sbufReadU32(src
);
3979 baro
.baroTemperature
= DEGREES_TO_CENTIDEGREES(SIMULATOR_BARO_TEMP
);
3981 sbufAdvance(src
, sizeof(uint32_t));
3984 if (sensors(SENSOR_MAG
)) {
3985 mag
.magADC
[X
] = ((int16_t)sbufReadU16(src
)) / 20; // 16000 / 20 = 800uT
3986 mag
.magADC
[Y
] = ((int16_t)sbufReadU16(src
)) / 20; //note that mag failure is simulated by setting all readings to zero
3987 mag
.magADC
[Z
] = ((int16_t)sbufReadU16(src
)) / 20;
3989 sbufAdvance(src
, sizeof(uint16_t) * XYZ_AXIS_COUNT
);
3992 if (SIMULATOR_HAS_OPTION(HITL_EXT_BATTERY_VOLTAGE
)) {
3993 simulatorData
.vbat
= sbufReadU8(src
);
3995 simulatorData
.vbat
= SIMULATOR_FULL_BATTERY
;
3998 if (SIMULATOR_HAS_OPTION(HITL_AIRSPEED
)) {
3999 simulatorData
.airSpeed
= sbufReadU16(src
);
4001 if (SIMULATOR_HAS_OPTION(HITL_EXTENDED_FLAGS
)) {
4006 if (SIMULATOR_HAS_OPTION(HITL_EXTENDED_FLAGS
)) {
4007 simulatorData
.flags
|= ((uint16_t)sbufReadU8(src
)) << 8;
4010 DISABLE_STATE(GPS_FIX
);
4014 sbufWriteU16(dst
, (uint16_t)simulatorData
.input
[INPUT_STABILIZED_ROLL
]);
4015 sbufWriteU16(dst
, (uint16_t)simulatorData
.input
[INPUT_STABILIZED_PITCH
]);
4016 sbufWriteU16(dst
, (uint16_t)simulatorData
.input
[INPUT_STABILIZED_YAW
]);
4017 sbufWriteU16(dst
, (uint16_t)(ARMING_FLAG(ARMED
) ? simulatorData
.input
[INPUT_STABILIZED_THROTTLE
] : -500));
4019 simulatorData
.debugIndex
++;
4020 if (simulatorData
.debugIndex
== 8) {
4021 simulatorData
.debugIndex
= 0;
4024 tmp_u8
= simulatorData
.debugIndex
|
4025 ((mixerConfig()->platformType
== PLATFORM_AIRPLANE
) ? 128 : 0) |
4026 (ARMING_FLAG(ARMED
) ? 64 : 0) |
4027 (!feature(FEATURE_OSD
) ? 32: 0) |
4028 (!isOSDTypeSupportedBySimulator() ? 16 : 0);
4030 sbufWriteU8(dst
, tmp_u8
);
4031 sbufWriteU32(dst
, debug
[simulatorData
.debugIndex
]);
4033 sbufWriteU16(dst
, attitude
.values
.roll
);
4034 sbufWriteU16(dst
, attitude
.values
.pitch
);
4035 sbufWriteU16(dst
, attitude
.values
.yaw
);
4037 mspWriteSimulatorOSD(dst
);
4039 *ret
= MSP_RESULT_ACK
;
4043 case MSP2_INAV_TIMER_OUTPUT_MODE
:
4044 if (dataSize
== 0) {
4045 for (int i
= 0; i
< HARDWARE_TIMER_DEFINITION_COUNT
; ++i
) {
4046 sbufWriteU8(dst
, i
);
4047 sbufWriteU8(dst
, timerOverrides(i
)->outputMode
);
4049 *ret
= MSP_RESULT_ACK
;
4050 } else if(dataSize
== 1) {
4051 uint8_t timer
= sbufReadU8(src
);
4052 if(timer
< HARDWARE_TIMER_DEFINITION_COUNT
) {
4053 sbufWriteU8(dst
, timer
);
4054 sbufWriteU8(dst
, timerOverrides(timer
)->outputMode
);
4055 *ret
= MSP_RESULT_ACK
;
4057 *ret
= MSP_RESULT_ERROR
;
4060 *ret
= MSP_RESULT_ERROR
;
4063 case MSP2_INAV_SET_TIMER_OUTPUT_MODE
:
4065 uint8_t timer
= sbufReadU8(src
);
4066 uint8_t outputMode
= sbufReadU8(src
);
4067 if(timer
< HARDWARE_TIMER_DEFINITION_COUNT
) {
4068 timerOverridesMutable(timer
)->outputMode
= outputMode
;
4069 *ret
= MSP_RESULT_ACK
;
4071 *ret
= MSP_RESULT_ERROR
;
4074 *ret
= MSP_RESULT_ERROR
;
4086 static mspResult_e
mspProcessSensorCommand(uint16_t cmdMSP
, sbuf_t
*src
)
4088 int dataSize
= sbufBytesRemaining(src
);
4092 #if defined(USE_RANGEFINDER_MSP)
4093 case MSP2_SENSOR_RANGEFINDER
:
4094 mspRangefinderReceiveNewData(sbufPtr(src
));
4098 #if defined(USE_OPFLOW_MSP)
4099 case MSP2_SENSOR_OPTIC_FLOW
:
4100 mspOpflowReceiveNewData(sbufPtr(src
));
4104 #if defined(USE_GPS_PROTO_MSP)
4105 case MSP2_SENSOR_GPS
:
4106 mspGPSReceiveNewData(sbufPtr(src
));
4110 #if defined(USE_MAG_MSP)
4111 case MSP2_SENSOR_COMPASS
:
4112 mspMagReceiveNewData(sbufPtr(src
));
4116 #if defined(USE_BARO_MSP)
4117 case MSP2_SENSOR_BAROMETER
:
4118 mspBaroReceiveNewData(sbufPtr(src
));
4122 #if defined(USE_PITOT_MSP)
4123 case MSP2_SENSOR_AIRSPEED
:
4124 mspPitotmeterReceiveNewData(sbufPtr(src
));
4128 #if (defined(USE_HEADTRACKER) && defined(USE_HEADTRACKER_MSP))
4129 case MSP2_SENSOR_HEADTRACKER
:
4130 mspHeadTrackerReceiverNewData(sbufPtr(src
), dataSize
);
4135 return MSP_RESULT_NO_REPLY
;
4139 * Returns MSP_RESULT_ACK, MSP_RESULT_ERROR or MSP_RESULT_NO_REPLY
4141 mspResult_e
mspFcProcessCommand(mspPacket_t
*cmd
, mspPacket_t
*reply
, mspPostProcessFnPtr
*mspPostProcessFn
)
4143 mspResult_e ret
= MSP_RESULT_ACK
;
4144 sbuf_t
*dst
= &reply
->buf
;
4145 sbuf_t
*src
= &cmd
->buf
;
4146 const uint16_t cmdMSP
= cmd
->cmd
;
4147 // initialize reply by default
4148 reply
->cmd
= cmd
->cmd
;
4150 if (MSP2_IS_SENSOR_MESSAGE(cmdMSP
)) {
4151 ret
= mspProcessSensorCommand(cmdMSP
, src
);
4152 } else if (mspFcProcessOutCommand(cmdMSP
, dst
, mspPostProcessFn
)) {
4153 ret
= MSP_RESULT_ACK
;
4154 } else if (cmdMSP
== MSP_SET_PASSTHROUGH
) {
4155 mspFcSetPassthroughCommand(dst
, src
, mspPostProcessFn
);
4156 ret
= MSP_RESULT_ACK
;
4158 if (!mspFCProcessInOutCommand(cmdMSP
, dst
, src
, &ret
)) {
4159 ret
= mspFcProcessInCommand(cmdMSP
, src
);
4163 // Process DONT_REPLY flag
4164 if (cmd
->flags
& MSP_FLAG_DONT_REPLY
) {
4165 ret
= MSP_RESULT_NO_REPLY
;
4168 reply
->result
= ret
;
4173 * Return a pointer to the process command function
4175 void mspFcInit(void)