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/>.
24 #include "common/log.h" //for MSP_SIMULATOR
27 #include "blackbox/blackbox.h"
29 #include "build/debug.h"
30 #include "build/version.h"
32 #include "common/axis.h"
33 #include "common/color.h"
34 #include "common/maths.h"
35 #include "common/streambuf.h"
36 #include "common/bitarray.h"
37 #include "common/time.h"
38 #include "common/utils.h"
39 #include "programming/global_variables.h"
40 #include "programming/pid.h"
42 #include "config/parameter_group_ids.h"
44 #include "drivers/accgyro/accgyro.h"
45 #include "drivers/compass/compass.h"
46 #include "drivers/compass/compass_msp.h"
47 #include "drivers/barometer/barometer_msp.h"
48 #include "drivers/pitotmeter/pitotmeter_msp.h"
49 #include "sensors/battery_sensor_fake.h"
50 #include "drivers/bus_i2c.h"
51 #include "drivers/display.h"
52 #include "drivers/flash.h"
53 #include "drivers/osd.h"
54 #include "drivers/osd_symbols.h"
55 #include "drivers/pwm_mapping.h"
56 #include "drivers/sdcard/sdcard.h"
57 #include "drivers/serial.h"
58 #include "drivers/system.h"
59 #include "drivers/time.h"
60 #include "drivers/timer.h"
61 #include "drivers/vtx_common.h"
63 #include "fc/fc_core.h"
64 #include "fc/config.h"
65 #include "fc/controlrate_profile.h"
66 #include "fc/fc_msp.h"
67 #include "fc/fc_msp_box.h"
68 #include "fc/firmware_update.h"
69 #include "fc/rc_adjustments.h"
70 #include "fc/rc_controls.h"
71 #include "fc/rc_modes.h"
72 #include "fc/runtime_config.h"
73 #include "fc/settings.h"
75 #include "flight/failsafe.h"
76 #include "flight/imu.h"
77 #include "flight/mixer.h"
78 #include "flight/pid.h"
79 #include "flight/servos.h"
81 #include "config/config_eeprom.h"
82 #include "config/feature.h"
84 #include "io/asyncfatfs/asyncfatfs.h"
85 #include "io/flashfs.h"
87 #include "io/opflow.h"
88 #include "io/rangefinder.h"
89 #include "io/ledstrip.h"
91 #include "io/serial.h"
92 #include "io/serial_4way.h"
94 #include "io/vtx_string.h"
95 #include "io/gps_private.h" //for MSP_SIMULATOR
98 #include "msp/msp_protocol.h"
99 #include "msp/msp_serial.h"
101 #include "navigation/navigation.h"
102 #include "navigation/navigation_private.h" //for MSP_SIMULATOR
103 #include "navigation/navigation_pos_estimator_private.h" //for MSP_SIMULATOR
108 #include "scheduler/scheduler.h"
110 #include "sensors/boardalignment.h"
111 #include "sensors/sensors.h"
112 #include "sensors/diagnostics.h"
113 #include "sensors/battery.h"
114 #include "sensors/rangefinder.h"
115 #include "sensors/acceleration.h"
116 #include "sensors/barometer.h"
117 #include "sensors/pitotmeter.h"
118 #include "sensors/compass.h"
119 #include "sensors/gyro.h"
120 #include "sensors/opflow.h"
121 #include "sensors/temperature.h"
122 #include "sensors/esc_sensor.h"
124 #include "telemetry/telemetry.h"
126 #ifdef USE_HARDWARE_REVISION_DETECTION
127 #include "hardware_revision.h"
130 extern timeDelta_t cycleTime
; // FIXME dependency on mw.c
132 static const char * const flightControllerIdentifier
= INAV_IDENTIFIER
; // 4 UPPER CASE alpha numeric characters that identify the flight controller.
133 static const char * const boardIdentifier
= TARGET_BOARD_IDENTIFIER
;
136 extern int16_t motor_disarmed
[MAX_SUPPORTED_MOTORS
];
138 static const char pidnames
[] =
151 MSP_SDCARD_STATE_NOT_PRESENT
= 0,
152 MSP_SDCARD_STATE_FATAL
= 1,
153 MSP_SDCARD_STATE_CARD_INIT
= 2,
154 MSP_SDCARD_STATE_FS_INIT
= 3,
155 MSP_SDCARD_STATE_READY
= 4
159 MSP_SDCARD_FLAG_SUPPORTTED
= 1
163 MSP_FLASHFS_BIT_READY
= 1,
164 MSP_FLASHFS_BIT_SUPPORTED
= 2
168 MSP_PASSTHROUGH_SERIAL_ID
= 0xFD,
169 MSP_PASSTHROUGH_SERIAL_FUNCTION_ID
= 0xFE,
170 MSP_PASSTHROUGH_ESC_4WAY
= 0xFF,
171 } mspPassthroughType_e
;
173 static uint8_t mspPassthroughMode
;
174 static uint8_t mspPassthroughArgument
;
176 static serialPort_t
*mspFindPassthroughSerialPort(void)
178 serialPortUsage_t
*portUsage
= NULL
;
180 switch (mspPassthroughMode
) {
181 case MSP_PASSTHROUGH_SERIAL_ID
:
183 portUsage
= findSerialPortUsageByIdentifier(mspPassthroughArgument
);
186 case MSP_PASSTHROUGH_SERIAL_FUNCTION_ID
:
188 const serialPortConfig_t
*portConfig
= findSerialPortConfig(1 << mspPassthroughArgument
);
190 portUsage
= findSerialPortUsageByIdentifier(portConfig
->identifier
);
195 return portUsage
? portUsage
->serialPort
: NULL
;
198 static void mspSerialPassthroughFn(serialPort_t
*serialPort
)
200 serialPort_t
*passthroughPort
= mspFindPassthroughSerialPort();
201 if (passthroughPort
&& serialPort
) {
202 serialPassthrough(passthroughPort
, serialPort
, NULL
, NULL
);
206 static void mspFcSetPassthroughCommand(sbuf_t
*dst
, sbuf_t
*src
, mspPostProcessFnPtr
*mspPostProcessFn
)
208 const unsigned int dataSize
= sbufBytesRemaining(src
);
212 mspPassthroughMode
= MSP_PASSTHROUGH_ESC_4WAY
;
214 mspPassthroughMode
= sbufReadU8(src
);
215 if (!sbufReadU8Safe(&mspPassthroughArgument
, src
)) {
216 mspPassthroughArgument
= 0;
220 switch (mspPassthroughMode
) {
221 case MSP_PASSTHROUGH_SERIAL_ID
:
222 case MSP_PASSTHROUGH_SERIAL_FUNCTION_ID
:
223 if (mspFindPassthroughSerialPort()) {
224 if (mspPostProcessFn
) {
225 *mspPostProcessFn
= mspSerialPassthroughFn
;
232 #ifdef USE_SERIAL_4WAY_BLHELI_INTERFACE
233 case MSP_PASSTHROUGH_ESC_4WAY
:
234 // get channel number
235 // switch all motor lines HI
236 // reply with the count of ESC found
237 sbufWriteU8(dst
, esc4wayInit());
239 if (mspPostProcessFn
) {
240 *mspPostProcessFn
= esc4wayProcess
;
249 static void mspRebootFn(serialPort_t
*serialPort
)
255 static void serializeSDCardSummaryReply(sbuf_t
*dst
)
258 uint8_t flags
= MSP_SDCARD_FLAG_SUPPORTTED
;
261 sbufWriteU8(dst
, flags
);
263 // Merge the card and filesystem states together
264 if (!sdcard_isInserted()) {
265 state
= MSP_SDCARD_STATE_NOT_PRESENT
;
266 } else if (!sdcard_isFunctional()) {
267 state
= MSP_SDCARD_STATE_FATAL
;
269 switch (afatfs_getFilesystemState()) {
270 case AFATFS_FILESYSTEM_STATE_READY
:
271 state
= MSP_SDCARD_STATE_READY
;
273 case AFATFS_FILESYSTEM_STATE_INITIALIZATION
:
274 if (sdcard_isInitialized()) {
275 state
= MSP_SDCARD_STATE_FS_INIT
;
277 state
= MSP_SDCARD_STATE_CARD_INIT
;
280 case AFATFS_FILESYSTEM_STATE_FATAL
:
281 case AFATFS_FILESYSTEM_STATE_UNKNOWN
:
283 state
= MSP_SDCARD_STATE_FATAL
;
288 sbufWriteU8(dst
, state
);
289 sbufWriteU8(dst
, afatfs_getLastError());
290 // Write free space and total space in kilobytes
291 sbufWriteU32(dst
, afatfs_getContiguousFreeSpace() / 1024);
292 sbufWriteU32(dst
, sdcard_getMetadata()->numBlocks
/ 2); // Block size is half a kilobyte
297 sbufWriteU32(dst
, 0);
298 sbufWriteU32(dst
, 0);
302 static void serializeDataflashSummaryReply(sbuf_t
*dst
)
305 const flashGeometry_t
*geometry
= flashGetGeometry();
306 sbufWriteU8(dst
, flashIsReady() ? 1 : 0);
307 sbufWriteU32(dst
, geometry
->sectors
);
308 sbufWriteU32(dst
, geometry
->totalSize
);
309 sbufWriteU32(dst
, flashfsGetOffset()); // Effectively the current number of bytes stored on the volume
312 sbufWriteU32(dst
, 0);
313 sbufWriteU32(dst
, 0);
314 sbufWriteU32(dst
, 0);
319 static void serializeDataflashReadReply(sbuf_t
*dst
, uint32_t address
, uint16_t size
)
321 // Check how much bytes we can read
322 const int bytesRemainingInBuf
= sbufBytesRemaining(dst
);
323 uint16_t readLen
= (size
> bytesRemainingInBuf
) ? bytesRemainingInBuf
: size
;
325 // size will be lower than that requested if we reach end of volume
326 const uint32_t flashfsSize
= flashfsGetSize();
327 if (readLen
> flashfsSize
- address
) {
328 // truncate the request
329 readLen
= flashfsSize
- address
;
333 sbufWriteU32(dst
, address
);
335 // Read into streambuf directly
336 const int bytesRead
= flashfsReadAbs(address
, sbufPtr(dst
), readLen
);
337 sbufAdvance(dst
, bytesRead
);
342 * Returns true if the command was processd, false otherwise.
343 * May set mspPostProcessFunc to a function to be called once the command has been processed
345 static bool mspFcProcessOutCommand(uint16_t cmdMSP
, sbuf_t
*dst
, mspPostProcessFnPtr
*mspPostProcessFn
)
348 case MSP_API_VERSION
:
349 sbufWriteU8(dst
, MSP_PROTOCOL_VERSION
);
350 sbufWriteU8(dst
, API_VERSION_MAJOR
);
351 sbufWriteU8(dst
, API_VERSION_MINOR
);
355 sbufWriteData(dst
, flightControllerIdentifier
, FLIGHT_CONTROLLER_IDENTIFIER_LENGTH
);
359 sbufWriteU8(dst
, FC_VERSION_MAJOR
);
360 sbufWriteU8(dst
, FC_VERSION_MINOR
);
361 sbufWriteU8(dst
, FC_VERSION_PATCH_LEVEL
);
366 sbufWriteData(dst
, boardIdentifier
, BOARD_IDENTIFIER_LENGTH
);
367 #ifdef USE_HARDWARE_REVISION_DETECTION
368 sbufWriteU16(dst
, hardwareRevision
);
370 sbufWriteU16(dst
, 0); // No other build targets currently have hardware revision detection.
372 // OSD support (for BF compatibility):
374 // 1 = OSD slave (not supported in INAV)
375 // 2 = OSD chip on board
381 // Board communication capabilities (uint8)
382 // Bit 0: 1 iff the board has VCP
383 // Bit 1: 1 iff the board supports software serial
384 uint8_t commCapabilities
= 0;
386 commCapabilities
|= 1 << 0;
388 #if defined(USE_SOFTSERIAL1) || defined(USE_SOFTSERIAL2)
389 commCapabilities
|= 1 << 1;
391 sbufWriteU8(dst
, commCapabilities
);
393 sbufWriteU8(dst
, strlen(targetName
));
394 sbufWriteData(dst
, targetName
, strlen(targetName
));
399 sbufWriteData(dst
, buildDate
, BUILD_DATE_LENGTH
);
400 sbufWriteData(dst
, buildTime
, BUILD_TIME_LENGTH
);
401 sbufWriteData(dst
, shortGitRevision
, GIT_SHORT_REVISION_LENGTH
);
404 case MSP_SENSOR_STATUS
:
405 sbufWriteU8(dst
, isHardwareHealthy() ? 1 : 0);
406 sbufWriteU8(dst
, getHwGyroStatus());
407 sbufWriteU8(dst
, getHwAccelerometerStatus());
408 sbufWriteU8(dst
, getHwCompassStatus());
409 sbufWriteU8(dst
, getHwBarometerStatus());
410 sbufWriteU8(dst
, getHwGPSStatus());
411 sbufWriteU8(dst
, getHwRangefinderStatus());
412 sbufWriteU8(dst
, getHwPitotmeterStatus());
413 sbufWriteU8(dst
, getHwOpticalFlowStatus());
416 case MSP_ACTIVEBOXES
:
418 boxBitmask_t mspBoxModeFlags
;
419 packBoxModeFlags(&mspBoxModeFlags
);
420 sbufWriteData(dst
, &mspBoxModeFlags
, sizeof(mspBoxModeFlags
));
427 boxBitmask_t mspBoxModeFlags
;
428 packBoxModeFlags(&mspBoxModeFlags
);
430 sbufWriteU16(dst
, (uint16_t)cycleTime
);
432 sbufWriteU16(dst
, i2cGetErrorCounter());
434 sbufWriteU16(dst
, 0);
436 sbufWriteU16(dst
, packSensorStatus());
437 sbufWriteData(dst
, &mspBoxModeFlags
, 4);
438 sbufWriteU8(dst
, getConfigProfile());
440 if (cmdMSP
== MSP_STATUS_EX
) {
441 sbufWriteU16(dst
, averageSystemLoadPercent
);
442 sbufWriteU16(dst
, armingFlags
);
443 sbufWriteU8(dst
, accGetCalibrationAxisFlags());
448 case MSP2_INAV_STATUS
:
450 // Preserves full arming flags and box modes
451 boxBitmask_t mspBoxModeFlags
;
452 packBoxModeFlags(&mspBoxModeFlags
);
454 sbufWriteU16(dst
, (uint16_t)cycleTime
);
456 sbufWriteU16(dst
, i2cGetErrorCounter());
458 sbufWriteU16(dst
, 0);
460 sbufWriteU16(dst
, packSensorStatus());
461 sbufWriteU16(dst
, averageSystemLoadPercent
);
462 sbufWriteU8(dst
, (getConfigBatteryProfile() << 4) | getConfigProfile());
463 sbufWriteU32(dst
, armingFlags
);
464 sbufWriteData(dst
, &mspBoxModeFlags
, sizeof(mspBoxModeFlags
));
470 for (int i
= 0; i
< 3; i
++) {
471 sbufWriteU16(dst
, (int16_t)lrintf(acc
.accADCf
[i
] * 512));
473 for (int i
= 0; i
< 3; i
++) {
474 sbufWriteU16(dst
, gyroRateDps(i
));
476 for (int i
= 0; i
< 3; i
++) {
478 sbufWriteU16(dst
, mag
.magADC
[i
]);
480 sbufWriteU16(dst
, 0);
487 sbufWriteData(dst
, &servo
, MAX_SUPPORTED_SERVOS
* 2);
489 case MSP_SERVO_CONFIGURATIONS
:
490 for (int i
= 0; i
< MAX_SUPPORTED_SERVOS
; i
++) {
491 sbufWriteU16(dst
, servoParams(i
)->min
);
492 sbufWriteU16(dst
, servoParams(i
)->max
);
493 sbufWriteU16(dst
, servoParams(i
)->middle
);
494 sbufWriteU8(dst
, servoParams(i
)->rate
);
497 sbufWriteU8(dst
, 255); // used to be forwardFromChannel, not used anymore, send 0xff for compatibility reasons
498 sbufWriteU32(dst
, 0); //Input reversing is not required since it can be done on mixer level
501 case MSP_SERVO_MIX_RULES
:
502 for (int i
= 0; i
< MAX_SERVO_RULES
; i
++) {
503 sbufWriteU8(dst
, customServoMixers(i
)->targetChannel
);
504 sbufWriteU8(dst
, customServoMixers(i
)->inputSource
);
505 sbufWriteU16(dst
, customServoMixers(i
)->rate
);
506 sbufWriteU8(dst
, customServoMixers(i
)->speed
);
508 sbufWriteU8(dst
, 100);
512 case MSP2_INAV_SERVO_MIXER
:
513 for (int i
= 0; i
< MAX_SERVO_RULES
; i
++) {
514 sbufWriteU8(dst
, customServoMixers(i
)->targetChannel
);
515 sbufWriteU8(dst
, customServoMixers(i
)->inputSource
);
516 sbufWriteU16(dst
, customServoMixers(i
)->rate
);
517 sbufWriteU8(dst
, customServoMixers(i
)->speed
);
518 #ifdef USE_PROGRAMMING_FRAMEWORK
519 sbufWriteU8(dst
, customServoMixers(i
)->conditionId
);
521 sbufWriteU8(dst
, -1);
525 #ifdef USE_PROGRAMMING_FRAMEWORK
526 case MSP2_INAV_LOGIC_CONDITIONS
:
527 for (int i
= 0; i
< MAX_LOGIC_CONDITIONS
; i
++) {
528 sbufWriteU8(dst
, logicConditions(i
)->enabled
);
529 sbufWriteU8(dst
, logicConditions(i
)->activatorId
);
530 sbufWriteU8(dst
, logicConditions(i
)->operation
);
531 sbufWriteU8(dst
, logicConditions(i
)->operandA
.type
);
532 sbufWriteU32(dst
, logicConditions(i
)->operandA
.value
);
533 sbufWriteU8(dst
, logicConditions(i
)->operandB
.type
);
534 sbufWriteU32(dst
, logicConditions(i
)->operandB
.value
);
535 sbufWriteU8(dst
, logicConditions(i
)->flags
);
538 case MSP2_INAV_LOGIC_CONDITIONS_STATUS
:
539 for (int i
= 0; i
< MAX_LOGIC_CONDITIONS
; i
++) {
540 sbufWriteU32(dst
, logicConditionGetValue(i
));
543 case MSP2_INAV_GVAR_STATUS
:
544 for (int i
= 0; i
< MAX_GLOBAL_VARIABLES
; i
++) {
545 sbufWriteU32(dst
, gvGet(i
));
548 case MSP2_INAV_PROGRAMMING_PID
:
549 for (int i
= 0; i
< MAX_PROGRAMMING_PID_COUNT
; i
++) {
550 sbufWriteU8(dst
, programmingPids(i
)->enabled
);
551 sbufWriteU8(dst
, programmingPids(i
)->setpoint
.type
);
552 sbufWriteU32(dst
, programmingPids(i
)->setpoint
.value
);
553 sbufWriteU8(dst
, programmingPids(i
)->measurement
.type
);
554 sbufWriteU32(dst
, programmingPids(i
)->measurement
.value
);
555 sbufWriteU16(dst
, programmingPids(i
)->gains
.P
);
556 sbufWriteU16(dst
, programmingPids(i
)->gains
.I
);
557 sbufWriteU16(dst
, programmingPids(i
)->gains
.D
);
558 sbufWriteU16(dst
, programmingPids(i
)->gains
.FF
);
561 case MSP2_INAV_PROGRAMMING_PID_STATUS
:
562 for (int i
= 0; i
< MAX_PROGRAMMING_PID_COUNT
; i
++) {
563 sbufWriteU32(dst
, programmingPidGetOutput(i
));
567 case MSP2_COMMON_MOTOR_MIXER
:
568 for (uint8_t i
= 0; i
< MAX_SUPPORTED_MOTORS
; i
++) {
569 sbufWriteU16(dst
, primaryMotorMixer(i
)->throttle
* 1000);
570 sbufWriteU16(dst
, constrainf(primaryMotorMixer(i
)->roll
+ 2.0f
, 0.0f
, 4.0f
) * 1000);
571 sbufWriteU16(dst
, constrainf(primaryMotorMixer(i
)->pitch
+ 2.0f
, 0.0f
, 4.0f
) * 1000);
572 sbufWriteU16(dst
, constrainf(primaryMotorMixer(i
)->yaw
+ 2.0f
, 0.0f
, 4.0f
) * 1000);
577 for (unsigned i
= 0; i
< 8; i
++) {
578 sbufWriteU16(dst
, i
< MAX_SUPPORTED_MOTORS
? motor
[i
] : 0);
583 for (int i
= 0; i
< rxRuntimeConfig
.channelCount
; i
++) {
584 sbufWriteU16(dst
, rxGetChannelValue(i
));
589 sbufWriteU16(dst
, attitude
.values
.roll
);
590 sbufWriteU16(dst
, attitude
.values
.pitch
);
591 sbufWriteU16(dst
, DECIDEGREES_TO_DEGREES(attitude
.values
.yaw
));
595 sbufWriteU32(dst
, lrintf(getEstimatedActualPosition(Z
)));
596 sbufWriteU16(dst
, lrintf(getEstimatedActualVelocity(Z
)));
597 #if defined(USE_BARO)
598 sbufWriteU32(dst
, baroGetLatestAltitude());
600 sbufWriteU32(dst
, 0);
604 case MSP_SONAR_ALTITUDE
:
605 #ifdef USE_RANGEFINDER
606 sbufWriteU32(dst
, rangefinderGetLatestAltitude());
608 sbufWriteU32(dst
, 0);
612 case MSP2_INAV_OPTICAL_FLOW
:
614 sbufWriteU8(dst
, opflow
.rawQuality
);
615 sbufWriteU16(dst
, RADIANS_TO_DEGREES(opflow
.flowRate
[X
]));
616 sbufWriteU16(dst
, RADIANS_TO_DEGREES(opflow
.flowRate
[Y
]));
617 sbufWriteU16(dst
, RADIANS_TO_DEGREES(opflow
.bodyRate
[X
]));
618 sbufWriteU16(dst
, RADIANS_TO_DEGREES(opflow
.bodyRate
[Y
]));
621 sbufWriteU16(dst
, 0);
622 sbufWriteU16(dst
, 0);
623 sbufWriteU16(dst
, 0);
624 sbufWriteU16(dst
, 0);
629 sbufWriteU8(dst
, (uint8_t)constrain(getBatteryVoltage() / 10, 0, 255));
630 sbufWriteU16(dst
, (uint16_t)constrain(getMAhDrawn(), 0, 0xFFFF)); // milliamp hours drawn from battery
631 sbufWriteU16(dst
, getRSSI());
632 sbufWriteU16(dst
, (int16_t)constrain(getAmperage(), -0x8000, 0x7FFF)); // send amperage in 0.01 A steps, range is -320A to 320A
635 case MSP2_INAV_ANALOG
:
636 // Bit 1: battery full, Bit 2: use capacity threshold, Bit 3-4: battery state, Bit 5-8: battery cell count
637 sbufWriteU8(dst
, batteryWasFullWhenPluggedIn() | (batteryUsesCapacityThresholds() << 1) | (getBatteryState() << 2) | (getBatteryCellCount() << 4));
638 sbufWriteU16(dst
, getBatteryVoltage());
639 sbufWriteU16(dst
, getAmperage()); // send amperage in 0.01 A steps
640 sbufWriteU32(dst
, getPower()); // power draw
641 sbufWriteU32(dst
, getMAhDrawn()); // milliamp hours drawn from battery
642 sbufWriteU32(dst
, getMWhDrawn()); // milliWatt hours drawn from battery
643 sbufWriteU32(dst
, getBatteryRemainingCapacity());
644 sbufWriteU8(dst
, calculateBatteryPercentage());
645 sbufWriteU16(dst
, getRSSI());
648 case MSP_ARMING_CONFIG
:
650 sbufWriteU8(dst
, armingConfig()->disarm_kill_switch
);
654 sbufWriteU16(dst
, gyroConfig()->looptime
);
658 sbufWriteU8(dst
, 100); //rcRate8 kept for compatibity reasons, this setting is no longer used
659 sbufWriteU8(dst
, currentControlRateProfile
->stabilized
.rcExpo8
);
660 for (int i
= 0 ; i
< 3; i
++) {
661 sbufWriteU8(dst
, currentControlRateProfile
->stabilized
.rates
[i
]); // R,P,Y see flight_dynamics_index_t
663 sbufWriteU8(dst
, currentControlRateProfile
->throttle
.dynPID
);
664 sbufWriteU8(dst
, currentControlRateProfile
->throttle
.rcMid8
);
665 sbufWriteU8(dst
, currentControlRateProfile
->throttle
.rcExpo8
);
666 sbufWriteU16(dst
, currentControlRateProfile
->throttle
.pa_breakpoint
);
667 sbufWriteU8(dst
, currentControlRateProfile
->stabilized
.rcYawExpo8
);
670 case MSP2_INAV_RATE_PROFILE
:
672 sbufWriteU8(dst
, currentControlRateProfile
->throttle
.rcMid8
);
673 sbufWriteU8(dst
, currentControlRateProfile
->throttle
.rcExpo8
);
674 sbufWriteU8(dst
, currentControlRateProfile
->throttle
.dynPID
);
675 sbufWriteU16(dst
, currentControlRateProfile
->throttle
.pa_breakpoint
);
678 sbufWriteU8(dst
, currentControlRateProfile
->stabilized
.rcExpo8
);
679 sbufWriteU8(dst
, currentControlRateProfile
->stabilized
.rcYawExpo8
);
680 for (uint8_t i
= 0 ; i
< 3; ++i
) {
681 sbufWriteU8(dst
, currentControlRateProfile
->stabilized
.rates
[i
]); // R,P,Y see flight_dynamics_index_t
685 sbufWriteU8(dst
, currentControlRateProfile
->manual
.rcExpo8
);
686 sbufWriteU8(dst
, currentControlRateProfile
->manual
.rcYawExpo8
);
687 for (uint8_t i
= 0 ; i
< 3; ++i
) {
688 sbufWriteU8(dst
, currentControlRateProfile
->manual
.rates
[i
]); // R,P,Y see flight_dynamics_index_t
693 for (int i
= 0; i
< PID_ITEM_COUNT
; i
++) {
694 sbufWriteU8(dst
, constrain(pidBank()->pid
[i
].P
, 0, 255));
695 sbufWriteU8(dst
, constrain(pidBank()->pid
[i
].I
, 0, 255));
696 sbufWriteU8(dst
, constrain(pidBank()->pid
[i
].D
, 0, 255));
697 sbufWriteU8(dst
, constrain(pidBank()->pid
[i
].FF
, 0, 255));
705 for (const char *c
= pidnames
; *c
; c
++) {
706 sbufWriteU8(dst
, *c
);
710 case MSP_MODE_RANGES
:
711 for (int i
= 0; i
< MAX_MODE_ACTIVATION_CONDITION_COUNT
; i
++) {
712 const modeActivationCondition_t
*mac
= modeActivationConditions(i
);
713 const box_t
*box
= findBoxByActiveBoxId(mac
->modeId
);
714 sbufWriteU8(dst
, box
? box
->permanentId
: 0);
715 sbufWriteU8(dst
, mac
->auxChannelIndex
);
716 sbufWriteU8(dst
, mac
->range
.startStep
);
717 sbufWriteU8(dst
, mac
->range
.endStep
);
721 case MSP_ADJUSTMENT_RANGES
:
722 for (int i
= 0; i
< MAX_ADJUSTMENT_RANGE_COUNT
; i
++) {
723 const adjustmentRange_t
*adjRange
= adjustmentRanges(i
);
724 sbufWriteU8(dst
, adjRange
->adjustmentIndex
);
725 sbufWriteU8(dst
, adjRange
->auxChannelIndex
);
726 sbufWriteU8(dst
, adjRange
->range
.startStep
);
727 sbufWriteU8(dst
, adjRange
->range
.endStep
);
728 sbufWriteU8(dst
, adjRange
->adjustmentFunction
);
729 sbufWriteU8(dst
, adjRange
->auxSwitchChannelIndex
);
734 if (!serializeBoxNamesReply(dst
)) {
740 serializeBoxReply(dst
);
744 sbufWriteU16(dst
, PWM_RANGE_MIDDLE
);
746 sbufWriteU16(dst
, 0); // Was min_throttle
747 sbufWriteU16(dst
, motorConfig()->maxthrottle
);
748 sbufWriteU16(dst
, motorConfig()->mincommand
);
750 sbufWriteU16(dst
, currentBatteryProfile
->failsafe_throttle
);
753 sbufWriteU8(dst
, gpsConfig()->provider
); // gps_type
754 sbufWriteU8(dst
, 0); // TODO gps_baudrate (an index, cleanflight uses a uint32_t
755 sbufWriteU8(dst
, gpsConfig()->sbasMode
); // gps_ubx_sbas
757 sbufWriteU8(dst
, 0); // gps_type
758 sbufWriteU8(dst
, 0); // TODO gps_baudrate (an index, cleanflight uses a uint32_t
759 sbufWriteU8(dst
, 0); // gps_ubx_sbas
761 sbufWriteU8(dst
, 0); // multiwiiCurrentMeterOutput
762 sbufWriteU8(dst
, rxConfig()->rssi_channel
);
766 sbufWriteU16(dst
, compassConfig()->mag_declination
/ 10);
768 sbufWriteU16(dst
, 0);
772 sbufWriteU8(dst
, batteryMetersConfig()->voltage
.scale
/ 10);
773 sbufWriteU8(dst
, currentBatteryProfile
->voltage
.cellMin
/ 10);
774 sbufWriteU8(dst
, currentBatteryProfile
->voltage
.cellMax
/ 10);
775 sbufWriteU8(dst
, currentBatteryProfile
->voltage
.cellWarning
/ 10);
785 sbufWriteU16(dst
, PWM_RANGE_MIDDLE
);
787 sbufWriteU16(dst
, 0); //Was min_throttle
788 sbufWriteU16(dst
, motorConfig()->maxthrottle
);
789 sbufWriteU16(dst
, motorConfig()->mincommand
);
791 sbufWriteU16(dst
, currentBatteryProfile
->failsafe_throttle
);
794 sbufWriteU8(dst
, gpsConfig()->provider
); // gps_type
795 sbufWriteU8(dst
, 0); // TODO gps_baudrate (an index, cleanflight uses a uint32_t
796 sbufWriteU8(dst
, gpsConfig()->sbasMode
); // gps_ubx_sbas
798 sbufWriteU8(dst
, 0); // gps_type
799 sbufWriteU8(dst
, 0); // TODO gps_baudrate (an index, cleanflight uses a uint32_t
800 sbufWriteU8(dst
, 0); // gps_ubx_sbas
802 sbufWriteU8(dst
, rxConfig()->rssi_channel
);
805 sbufWriteU16(dst
, compassConfig()->mag_declination
/ 10);
807 sbufWriteU16(dst
, 0);
811 sbufWriteU16(dst
, batteryMetersConfig()->voltage
.scale
);
812 sbufWriteU8(dst
, batteryMetersConfig()->voltageSource
);
813 sbufWriteU8(dst
, currentBatteryProfile
->cells
);
814 sbufWriteU16(dst
, currentBatteryProfile
->voltage
.cellDetect
);
815 sbufWriteU16(dst
, currentBatteryProfile
->voltage
.cellMin
);
816 sbufWriteU16(dst
, currentBatteryProfile
->voltage
.cellMax
);
817 sbufWriteU16(dst
, currentBatteryProfile
->voltage
.cellWarning
);
819 sbufWriteU16(dst
, 0);
822 sbufWriteU16(dst
, 0);
823 sbufWriteU16(dst
, 0);
824 sbufWriteU16(dst
, 0);
825 sbufWriteU16(dst
, 0);
828 sbufWriteU32(dst
, currentBatteryProfile
->capacity
.value
);
829 sbufWriteU32(dst
, currentBatteryProfile
->capacity
.warning
);
830 sbufWriteU32(dst
, currentBatteryProfile
->capacity
.critical
);
831 sbufWriteU8(dst
, currentBatteryProfile
->capacity
.unit
);
834 case MSP2_INAV_MISC2
:
836 sbufWriteU32(dst
, micros() / 1000000); // On time (seconds)
837 sbufWriteU32(dst
, getFlightTime()); // Flight time (seconds)
840 sbufWriteU8(dst
, getThrottlePercent(true)); // Throttle Percent
841 sbufWriteU8(dst
, navigationIsControllingThrottle() ? 1 : 0); // Auto Throttle Flag (0 or 1)
845 case MSP2_INAV_BATTERY_CONFIG
:
847 sbufWriteU16(dst
, batteryMetersConfig()->voltage
.scale
);
848 sbufWriteU8(dst
, batteryMetersConfig()->voltageSource
);
849 sbufWriteU8(dst
, currentBatteryProfile
->cells
);
850 sbufWriteU16(dst
, currentBatteryProfile
->voltage
.cellDetect
);
851 sbufWriteU16(dst
, currentBatteryProfile
->voltage
.cellMin
);
852 sbufWriteU16(dst
, currentBatteryProfile
->voltage
.cellMax
);
853 sbufWriteU16(dst
, currentBatteryProfile
->voltage
.cellWarning
);
855 sbufWriteU16(dst
, 0);
858 sbufWriteU16(dst
, 0);
859 sbufWriteU16(dst
, 0);
860 sbufWriteU16(dst
, 0);
861 sbufWriteU16(dst
, 0);
864 sbufWriteU16(dst
, batteryMetersConfig()->current
.offset
);
865 sbufWriteU16(dst
, batteryMetersConfig()->current
.scale
);
867 sbufWriteU32(dst
, currentBatteryProfile
->capacity
.value
);
868 sbufWriteU32(dst
, currentBatteryProfile
->capacity
.warning
);
869 sbufWriteU32(dst
, currentBatteryProfile
->capacity
.critical
);
870 sbufWriteU8(dst
, currentBatteryProfile
->capacity
.unit
);
874 // FIXME This is hardcoded and should not be.
875 for (int i
= 0; i
< 8; i
++) {
876 sbufWriteU8(dst
, i
+ 1);
882 sbufWriteU8(dst
, gpsSol
.fixType
);
883 sbufWriteU8(dst
, gpsSol
.numSat
);
884 sbufWriteU32(dst
, gpsSol
.llh
.lat
);
885 sbufWriteU32(dst
, gpsSol
.llh
.lon
);
886 sbufWriteU16(dst
, gpsSol
.llh
.alt
/100); // meters
887 sbufWriteU16(dst
, gpsSol
.groundSpeed
);
888 sbufWriteU16(dst
, gpsSol
.groundCourse
);
889 sbufWriteU16(dst
, gpsSol
.hdop
);
893 sbufWriteU16(dst
, GPS_distanceToHome
);
894 sbufWriteU16(dst
, GPS_directionToHome
);
895 sbufWriteU8(dst
, gpsSol
.flags
.gpsHeartbeat
? 1 : 0);
898 sbufWriteU8(dst
, NAV_Status
.mode
);
899 sbufWriteU8(dst
, NAV_Status
.state
);
900 sbufWriteU8(dst
, NAV_Status
.activeWpAction
);
901 sbufWriteU8(dst
, NAV_Status
.activeWpNumber
);
902 sbufWriteU8(dst
, NAV_Status
.error
);
903 //sbufWriteU16(dst, (int16_t)(target_bearing/100));
904 sbufWriteU16(dst
, getHeadingHoldTarget());
909 /* Compatibility stub - return zero SVs */
915 sbufWriteU8(dst
, gpsSol
.hdop
/ 100);
916 sbufWriteU8(dst
, gpsSol
.hdop
/ 100);
919 case MSP_GPSSTATISTICS
:
920 sbufWriteU16(dst
, gpsStats
.lastMessageDt
);
921 sbufWriteU32(dst
, gpsStats
.errors
);
922 sbufWriteU32(dst
, gpsStats
.timeouts
);
923 sbufWriteU32(dst
, gpsStats
.packetCount
);
924 sbufWriteU16(dst
, gpsSol
.hdop
);
925 sbufWriteU16(dst
, gpsSol
.eph
);
926 sbufWriteU16(dst
, gpsSol
.epv
);
930 // output some useful QA statistics
931 // debug[x] = ((hse_value / 1000000) * 1000) + (SystemCoreClock / 1000000); // XX0YY [crystal clock : core clock]
933 for (int i
= 0; i
< 4; i
++) {
934 sbufWriteU16(dst
, debug
[i
]); // 4 variables are here for general monitoring purpose
938 case MSP2_INAV_DEBUG
:
939 for (int i
= 0; i
< DEBUG32_VALUE_COUNT
; i
++) {
940 sbufWriteU32(dst
, debug
[i
]); // 8 variables are here for general monitoring purpose
945 sbufWriteU32(dst
, U_ID_0
);
946 sbufWriteU32(dst
, U_ID_1
);
947 sbufWriteU32(dst
, U_ID_2
);
951 sbufWriteU32(dst
, featureMask());
954 case MSP_BOARD_ALIGNMENT
:
955 sbufWriteU16(dst
, boardAlignment()->rollDeciDegrees
);
956 sbufWriteU16(dst
, boardAlignment()->pitchDeciDegrees
);
957 sbufWriteU16(dst
, boardAlignment()->yawDeciDegrees
);
960 case MSP_VOLTAGE_METER_CONFIG
:
962 sbufWriteU8(dst
, batteryMetersConfig()->voltage
.scale
/ 10);
963 sbufWriteU8(dst
, currentBatteryProfile
->voltage
.cellMin
/ 10);
964 sbufWriteU8(dst
, currentBatteryProfile
->voltage
.cellMax
/ 10);
965 sbufWriteU8(dst
, currentBatteryProfile
->voltage
.cellWarning
/ 10);
974 case MSP_CURRENT_METER_CONFIG
:
975 sbufWriteU16(dst
, batteryMetersConfig()->current
.scale
);
976 sbufWriteU16(dst
, batteryMetersConfig()->current
.offset
);
977 sbufWriteU8(dst
, batteryMetersConfig()->current
.type
);
978 sbufWriteU16(dst
, constrain(currentBatteryProfile
->capacity
.value
, 0, 0xFFFF));
982 sbufWriteU8(dst
, 3); // mixerMode no longer supported, send 3 (QuadX) as fallback
986 sbufWriteU8(dst
, rxConfig()->serialrx_provider
);
987 sbufWriteU16(dst
, rxConfig()->maxcheck
);
988 sbufWriteU16(dst
, PWM_RANGE_MIDDLE
);
989 sbufWriteU16(dst
, rxConfig()->mincheck
);
990 #ifdef USE_SPEKTRUM_BIND
991 sbufWriteU8(dst
, rxConfig()->spektrum_sat_bind
);
995 sbufWriteU16(dst
, rxConfig()->rx_min_usec
);
996 sbufWriteU16(dst
, rxConfig()->rx_max_usec
);
997 sbufWriteU8(dst
, 0); // for compatibility with betaflight (rcInterpolation)
998 sbufWriteU8(dst
, 0); // for compatibility with betaflight (rcInterpolationInterval)
999 sbufWriteU16(dst
, 0); // for compatibility with betaflight (airModeActivateThreshold)
1000 sbufWriteU8(dst
, 0);
1001 sbufWriteU32(dst
, 0);
1002 sbufWriteU8(dst
, 0);
1003 sbufWriteU8(dst
, 0); // for compatibility with betaflight (fpvCamAngleDegrees)
1004 sbufWriteU8(dst
, rxConfig()->receiverType
);
1007 case MSP_FAILSAFE_CONFIG
:
1008 sbufWriteU8(dst
, failsafeConfig()->failsafe_delay
);
1009 sbufWriteU8(dst
, failsafeConfig()->failsafe_off_delay
);
1010 sbufWriteU16(dst
, currentBatteryProfile
->failsafe_throttle
);
1011 sbufWriteU8(dst
, 0); // was failsafe_kill_switch
1012 sbufWriteU16(dst
, failsafeConfig()->failsafe_throttle_low_delay
);
1013 sbufWriteU8(dst
, failsafeConfig()->failsafe_procedure
);
1014 sbufWriteU8(dst
, failsafeConfig()->failsafe_recovery_delay
);
1015 sbufWriteU16(dst
, failsafeConfig()->failsafe_fw_roll_angle
);
1016 sbufWriteU16(dst
, failsafeConfig()->failsafe_fw_pitch_angle
);
1017 sbufWriteU16(dst
, failsafeConfig()->failsafe_fw_yaw_rate
);
1018 sbufWriteU16(dst
, failsafeConfig()->failsafe_stick_motion_threshold
);
1019 sbufWriteU16(dst
, failsafeConfig()->failsafe_min_distance
);
1020 sbufWriteU8(dst
, failsafeConfig()->failsafe_min_distance_procedure
);
1023 case MSP_RSSI_CONFIG
:
1024 sbufWriteU8(dst
, rxConfig()->rssi_channel
);
1028 sbufWriteData(dst
, rxConfig()->rcmap
, MAX_MAPPABLE_RX_INPUTS
);
1031 case MSP2_COMMON_SERIAL_CONFIG
:
1032 for (int i
= 0; i
< SERIAL_PORT_COUNT
; i
++) {
1033 if (!serialIsPortAvailable(serialConfig()->portConfigs
[i
].identifier
)) {
1036 sbufWriteU8(dst
, serialConfig()->portConfigs
[i
].identifier
);
1037 sbufWriteU32(dst
, serialConfig()->portConfigs
[i
].functionMask
);
1038 sbufWriteU8(dst
, serialConfig()->portConfigs
[i
].msp_baudrateIndex
);
1039 sbufWriteU8(dst
, serialConfig()->portConfigs
[i
].gps_baudrateIndex
);
1040 sbufWriteU8(dst
, serialConfig()->portConfigs
[i
].telemetry_baudrateIndex
);
1041 sbufWriteU8(dst
, serialConfig()->portConfigs
[i
].peripheral_baudrateIndex
);
1045 #ifdef USE_LED_STRIP
1046 case MSP_LED_COLORS
:
1047 for (int i
= 0; i
< LED_CONFIGURABLE_COLOR_COUNT
; i
++) {
1048 const hsvColor_t
*color
= &ledStripConfig()->colors
[i
];
1049 sbufWriteU16(dst
, color
->h
);
1050 sbufWriteU8(dst
, color
->s
);
1051 sbufWriteU8(dst
, color
->v
);
1055 case MSP_LED_STRIP_CONFIG
:
1056 for (int i
= 0; i
< LED_MAX_STRIP_LENGTH
; i
++) {
1057 const ledConfig_t
*ledConfig
= &ledStripConfig()->ledConfigs
[i
];
1059 uint32_t legacyLedConfig
= ledConfig
->led_position
;
1061 legacyLedConfig
|= ledConfig
->led_function
<< shiftCount
;
1063 legacyLedConfig
|= (ledConfig
->led_overlay
& 0x3F) << (shiftCount
);
1065 legacyLedConfig
|= (ledConfig
->led_color
) << (shiftCount
);
1067 legacyLedConfig
|= (ledConfig
->led_direction
) << (shiftCount
);
1069 legacyLedConfig
|= (ledConfig
->led_params
) << (shiftCount
);
1071 sbufWriteU32(dst
, legacyLedConfig
);
1075 case MSP2_INAV_LED_STRIP_CONFIG_EX
:
1076 for (int i
= 0; i
< LED_MAX_STRIP_LENGTH
; i
++) {
1077 const ledConfig_t
*ledConfig
= &ledStripConfig()->ledConfigs
[i
];
1078 sbufWriteDataSafe(dst
, ledConfig
, sizeof(ledConfig_t
));
1083 case MSP_LED_STRIP_MODECOLOR
:
1084 for (int i
= 0; i
< LED_MODE_COUNT
; i
++) {
1085 for (int j
= 0; j
< LED_DIRECTION_COUNT
; j
++) {
1086 sbufWriteU8(dst
, i
);
1087 sbufWriteU8(dst
, j
);
1088 sbufWriteU8(dst
, ledStripConfig()->modeColors
[i
].color
[j
]);
1092 for (int j
= 0; j
< LED_SPECIAL_COLOR_COUNT
; j
++) {
1093 sbufWriteU8(dst
, LED_MODE_COUNT
);
1094 sbufWriteU8(dst
, j
);
1095 sbufWriteU8(dst
, ledStripConfig()->specialColors
.color
[j
]);
1100 case MSP_DATAFLASH_SUMMARY
:
1101 serializeDataflashSummaryReply(dst
);
1104 case MSP_BLACKBOX_CONFIG
:
1105 sbufWriteU8(dst
, 0); // API no longer supported
1106 sbufWriteU8(dst
, 0);
1107 sbufWriteU8(dst
, 0);
1108 sbufWriteU8(dst
, 0);
1111 case MSP2_BLACKBOX_CONFIG
:
1113 sbufWriteU8(dst
, 1); //Blackbox supported
1114 sbufWriteU8(dst
, blackboxConfig()->device
);
1115 sbufWriteU16(dst
, blackboxConfig()->rate_num
);
1116 sbufWriteU16(dst
, blackboxConfig()->rate_denom
);
1117 sbufWriteU32(dst
,blackboxConfig()->includeFlags
);
1119 sbufWriteU8(dst
, 0); // Blackbox not supported
1120 sbufWriteU8(dst
, 0);
1121 sbufWriteU16(dst
, 0);
1122 sbufWriteU16(dst
, 0);
1126 case MSP_SDCARD_SUMMARY
:
1127 serializeSDCardSummaryReply(dst
);
1130 #if defined (USE_DJI_HD_OSD) || defined (USE_MSP_DISPLAYPORT)
1131 case MSP_BATTERY_STATE
:
1132 // Battery characteristics
1133 sbufWriteU8(dst
, constrain(getBatteryCellCount(), 0, 255));
1134 sbufWriteU16(dst
, currentBatteryProfile
->capacity
.value
);
1137 sbufWriteU8(dst
, constrain(getBatteryVoltage() / 10, 0, 255)); // in 0.1V steps
1138 sbufWriteU16(dst
, constrain(getMAhDrawn(), 0, 0xFFFF));
1139 sbufWriteU16(dst
, constrain(getAmperage(), -0x8000, 0x7FFF));
1141 // Battery alerts - used values match Betaflight's/DJI's
1142 sbufWriteU8(dst
, getBatteryState());
1144 // Additional battery voltage field (in 0.01V steps)
1145 sbufWriteU16(dst
, getBatteryVoltage());
1149 case MSP_OSD_CONFIG
:
1151 sbufWriteU8(dst
, OSD_DRIVER_MAX7456
); // OSD supported
1152 // send video system (AUTO/PAL/NTSC)
1153 sbufWriteU8(dst
, osdConfig()->video_system
);
1154 sbufWriteU8(dst
, osdConfig()->units
);
1155 sbufWriteU8(dst
, osdConfig()->rssi_alarm
);
1156 sbufWriteU16(dst
, currentBatteryProfile
->capacity
.warning
);
1157 sbufWriteU16(dst
, osdConfig()->time_alarm
);
1158 sbufWriteU16(dst
, osdConfig()->alt_alarm
);
1159 sbufWriteU16(dst
, osdConfig()->dist_alarm
);
1160 sbufWriteU16(dst
, osdConfig()->neg_alt_alarm
);
1161 for (int i
= 0; i
< OSD_ITEM_COUNT
; i
++) {
1162 sbufWriteU16(dst
, osdLayoutsConfig()->item_pos
[0][i
]);
1165 sbufWriteU8(dst
, OSD_DRIVER_NONE
); // OSD not supported
1170 sbufWriteU16(dst
, reversibleMotorsConfig()->deadband_low
);
1171 sbufWriteU16(dst
, reversibleMotorsConfig()->deadband_high
);
1172 sbufWriteU16(dst
, reversibleMotorsConfig()->neutral
);
1175 case MSP_RC_DEADBAND
:
1176 sbufWriteU8(dst
, rcControlsConfig()->deadband
);
1177 sbufWriteU8(dst
, rcControlsConfig()->yaw_deadband
);
1178 sbufWriteU8(dst
, rcControlsConfig()->alt_hold_deadband
);
1179 sbufWriteU16(dst
, rcControlsConfig()->mid_throttle_deadband
);
1182 case MSP_SENSOR_ALIGNMENT
:
1183 sbufWriteU8(dst
, 0); // was gyroConfig()->gyro_align
1184 sbufWriteU8(dst
, 0); // was accelerometerConfig()->acc_align
1186 sbufWriteU8(dst
, compassConfig()->mag_align
);
1188 sbufWriteU8(dst
, 0);
1191 sbufWriteU8(dst
, opticalFlowConfig()->opflow_align
);
1193 sbufWriteU8(dst
, 0);
1197 case MSP_ADVANCED_CONFIG
:
1198 sbufWriteU8(dst
, 1); // gyroConfig()->gyroSyncDenominator
1199 sbufWriteU8(dst
, 1); // BF: masterConfig.pid_process_denom
1200 sbufWriteU8(dst
, 1); // BF: motorConfig()->useUnsyncedPwm
1201 sbufWriteU8(dst
, motorConfig()->motorPwmProtocol
);
1202 sbufWriteU16(dst
, motorConfig()->motorPwmRate
);
1203 sbufWriteU16(dst
, servoConfig()->servoPwmRate
);
1204 sbufWriteU8(dst
, 0);
1207 case MSP_FILTER_CONFIG
:
1208 sbufWriteU8(dst
, gyroConfig()->gyro_main_lpf_hz
);
1209 sbufWriteU16(dst
, pidProfile()->dterm_lpf_hz
);
1210 sbufWriteU16(dst
, pidProfile()->yaw_lpf_hz
);
1211 sbufWriteU16(dst
, 0); //Was gyroConfig()->gyro_notch_hz
1212 sbufWriteU16(dst
, 1); //Was gyroConfig()->gyro_notch_cutoff
1213 sbufWriteU16(dst
, 0); //BF: pidProfile()->dterm_notch_hz
1214 sbufWriteU16(dst
, 1); //pidProfile()->dterm_notch_cutoff
1216 sbufWriteU16(dst
, 0); //BF: masterConfig.gyro_soft_notch_hz_2
1217 sbufWriteU16(dst
, 1); //BF: masterConfig.gyro_soft_notch_cutoff_2
1219 sbufWriteU16(dst
, accelerometerConfig()->acc_notch_hz
);
1220 sbufWriteU16(dst
, accelerometerConfig()->acc_notch_cutoff
);
1222 sbufWriteU16(dst
, 0); //Was gyroConfig()->gyro_stage2_lowpass_hz
1225 case MSP_PID_ADVANCED
:
1226 sbufWriteU16(dst
, 0); // pidProfile()->rollPitchItermIgnoreRate
1227 sbufWriteU16(dst
, 0); // pidProfile()->yawItermIgnoreRate
1228 sbufWriteU16(dst
, 0); //pidProfile()->yaw_p_limit
1229 sbufWriteU8(dst
, 0); //BF: pidProfile()->deltaMethod
1230 sbufWriteU8(dst
, 0); //BF: pidProfile()->vbatPidCompensation
1231 sbufWriteU8(dst
, 0); //BF: pidProfile()->setpointRelaxRatio
1232 sbufWriteU8(dst
, 0);
1233 sbufWriteU16(dst
, pidProfile()->pidSumLimit
);
1234 sbufWriteU8(dst
, 0); //BF: pidProfile()->itermThrottleGain
1237 * To keep compatibility on MSP frame length level with Betaflight, axis axisAccelerationLimitYaw
1238 * limit will be sent and received in [dps / 10]
1240 sbufWriteU16(dst
, constrain(pidProfile()->axisAccelerationLimitRollPitch
/ 10, 0, 65535));
1241 sbufWriteU16(dst
, constrain(pidProfile()->axisAccelerationLimitYaw
/ 10, 0, 65535));
1245 sbufWriteU8(dst
, 0); //Legacy, no longer in use async processing value
1246 sbufWriteU16(dst
, 0); //Legacy, no longer in use async processing value
1247 sbufWriteU16(dst
, 0); //Legacy, no longer in use async processing value
1248 sbufWriteU8(dst
, pidProfile()->heading_hold_rate_limit
);
1249 sbufWriteU8(dst
, HEADING_HOLD_ERROR_LPF_FREQ
);
1250 sbufWriteU16(dst
, 0);
1251 sbufWriteU8(dst
, gyroConfig()->gyro_lpf
);
1252 sbufWriteU8(dst
, accelerometerConfig()->acc_lpf_hz
);
1253 sbufWriteU8(dst
, 0); //reserved
1254 sbufWriteU8(dst
, 0); //reserved
1255 sbufWriteU8(dst
, 0); //reserved
1256 sbufWriteU8(dst
, 0); //reserved
1259 case MSP_SENSOR_CONFIG
:
1260 sbufWriteU8(dst
, accelerometerConfig()->acc_hardware
);
1262 sbufWriteU8(dst
, barometerConfig()->baro_hardware
);
1264 sbufWriteU8(dst
, 0);
1267 sbufWriteU8(dst
, compassConfig()->mag_hardware
);
1269 sbufWriteU8(dst
, 0);
1272 sbufWriteU8(dst
, pitotmeterConfig()->pitot_hardware
);
1274 sbufWriteU8(dst
, 0);
1276 #ifdef USE_RANGEFINDER
1277 sbufWriteU8(dst
, rangefinderConfig()->rangefinder_hardware
);
1279 sbufWriteU8(dst
, 0);
1282 sbufWriteU8(dst
, opticalFlowConfig()->opflow_hardware
);
1284 sbufWriteU8(dst
, 0);
1288 case MSP_NAV_POSHOLD
:
1289 sbufWriteU8(dst
, navConfig()->general
.flags
.user_control_mode
);
1290 sbufWriteU16(dst
, navConfig()->general
.max_auto_speed
);
1291 sbufWriteU16(dst
, navConfig()->general
.max_auto_climb_rate
);
1292 sbufWriteU16(dst
, navConfig()->general
.max_manual_speed
);
1293 sbufWriteU16(dst
, navConfig()->general
.max_manual_climb_rate
);
1294 sbufWriteU8(dst
, navConfig()->mc
.max_bank_angle
);
1295 sbufWriteU8(dst
, navConfig()->mc
.althold_throttle_type
);
1296 sbufWriteU16(dst
, currentBatteryProfile
->nav
.mc
.hover_throttle
);
1299 case MSP_RTH_AND_LAND_CONFIG
:
1300 sbufWriteU16(dst
, navConfig()->general
.min_rth_distance
);
1301 sbufWriteU8(dst
, navConfig()->general
.flags
.rth_climb_first
);
1302 sbufWriteU8(dst
, navConfig()->general
.flags
.rth_climb_ignore_emerg
);
1303 sbufWriteU8(dst
, navConfig()->general
.flags
.rth_tail_first
);
1304 sbufWriteU8(dst
, navConfig()->general
.flags
.rth_allow_landing
);
1305 sbufWriteU8(dst
, navConfig()->general
.flags
.rth_alt_control_mode
);
1306 sbufWriteU16(dst
, navConfig()->general
.rth_abort_threshold
);
1307 sbufWriteU16(dst
, navConfig()->general
.rth_altitude
);
1308 sbufWriteU16(dst
, navConfig()->general
.land_minalt_vspd
);
1309 sbufWriteU16(dst
, navConfig()->general
.land_maxalt_vspd
);
1310 sbufWriteU16(dst
, navConfig()->general
.land_slowdown_minalt
);
1311 sbufWriteU16(dst
, navConfig()->general
.land_slowdown_maxalt
);
1312 sbufWriteU16(dst
, navConfig()->general
.emerg_descent_rate
);
1316 sbufWriteU16(dst
, currentBatteryProfile
->nav
.fw
.cruise_throttle
);
1317 sbufWriteU16(dst
, currentBatteryProfile
->nav
.fw
.min_throttle
);
1318 sbufWriteU16(dst
, currentBatteryProfile
->nav
.fw
.max_throttle
);
1319 sbufWriteU8(dst
, navConfig()->fw
.max_bank_angle
);
1320 sbufWriteU8(dst
, navConfig()->fw
.max_climb_angle
);
1321 sbufWriteU8(dst
, navConfig()->fw
.max_dive_angle
);
1322 sbufWriteU8(dst
, currentBatteryProfile
->nav
.fw
.pitch_to_throttle
);
1323 sbufWriteU16(dst
, navConfig()->fw
.loiter_radius
);
1326 case MSP_CALIBRATION_DATA
:
1327 sbufWriteU8(dst
, accGetCalibrationAxisFlags());
1328 sbufWriteU16(dst
, accelerometerConfig()->accZero
.raw
[X
]);
1329 sbufWriteU16(dst
, accelerometerConfig()->accZero
.raw
[Y
]);
1330 sbufWriteU16(dst
, accelerometerConfig()->accZero
.raw
[Z
]);
1331 sbufWriteU16(dst
, accelerometerConfig()->accGain
.raw
[X
]);
1332 sbufWriteU16(dst
, accelerometerConfig()->accGain
.raw
[Y
]);
1333 sbufWriteU16(dst
, accelerometerConfig()->accGain
.raw
[Z
]);
1336 sbufWriteU16(dst
, compassConfig()->magZero
.raw
[X
]);
1337 sbufWriteU16(dst
, compassConfig()->magZero
.raw
[Y
]);
1338 sbufWriteU16(dst
, compassConfig()->magZero
.raw
[Z
]);
1340 sbufWriteU16(dst
, 0);
1341 sbufWriteU16(dst
, 0);
1342 sbufWriteU16(dst
, 0);
1346 sbufWriteU16(dst
, opticalFlowConfig()->opflow_scale
* 256);
1348 sbufWriteU16(dst
, 0);
1352 sbufWriteU16(dst
, compassConfig()->magGain
[X
]);
1353 sbufWriteU16(dst
, compassConfig()->magGain
[Y
]);
1354 sbufWriteU16(dst
, compassConfig()->magGain
[Z
]);
1356 sbufWriteU16(dst
, 0);
1357 sbufWriteU16(dst
, 0);
1358 sbufWriteU16(dst
, 0);
1363 case MSP_POSITION_ESTIMATION_CONFIG
:
1365 sbufWriteU16(dst
, positionEstimationConfig()->w_z_baro_p
* 100); // inav_w_z_baro_p float as value * 100
1366 sbufWriteU16(dst
, positionEstimationConfig()->w_z_gps_p
* 100); // 2 inav_w_z_gps_p float as value * 100
1367 sbufWriteU16(dst
, positionEstimationConfig()->w_z_gps_v
* 100); // 2 inav_w_z_gps_v float as value * 100
1368 sbufWriteU16(dst
, positionEstimationConfig()->w_xy_gps_p
* 100); // 2 inav_w_xy_gps_p float as value * 100
1369 sbufWriteU16(dst
, positionEstimationConfig()->w_xy_gps_v
* 100); // 2 inav_w_xy_gps_v float as value * 100
1370 sbufWriteU8(dst
, gpsConfigMutable()->gpsMinSats
); // 1
1371 sbufWriteU8(dst
, positionEstimationConfig()->use_gps_velned
); // 1 inav_use_gps_velned ON/OFF
1376 if (!ARMING_FLAG(ARMED
)) {
1377 if (mspPostProcessFn
) {
1378 *mspPostProcessFn
= mspRebootFn
;
1383 case MSP_WP_GETINFO
:
1384 sbufWriteU8(dst
, 0); // Reserved for waypoint capabilities
1385 sbufWriteU8(dst
, NAV_MAX_WAYPOINTS
); // Maximum number of waypoints supported
1386 sbufWriteU8(dst
, isWaypointListValid()); // Is current mission valid
1387 sbufWriteU8(dst
, getWaypointCount()); // Number of waypoints in current mission
1391 sbufWriteU8(dst
, getRSSISource());
1392 uint8_t rtcDateTimeIsSet
= 0;
1394 if (rtcGetDateTime(&dt
)) {
1395 rtcDateTimeIsSet
= 1;
1397 sbufWriteU8(dst
, rtcDateTimeIsSet
);
1402 int32_t seconds
= 0;
1403 uint16_t millis
= 0;
1406 seconds
= rtcTimeGetSeconds(&t
);
1407 millis
= rtcTimeGetMillis(&t
);
1409 sbufWriteU32(dst
, (uint32_t)seconds
);
1410 sbufWriteU16(dst
, millis
);
1414 case MSP_VTX_CONFIG
:
1415 #ifdef USE_VTX_CONTROL
1417 vtxDevice_t
*vtxDevice
= vtxCommonDevice();
1420 uint8_t deviceType
= vtxCommonGetDeviceType(vtxDevice
);
1422 // Return band, channel and power from vtxSettingsConfig_t
1423 // since the VTX might be configured but temporarily offline.
1424 uint8_t pitmode
= 0;
1425 vtxCommonGetPitMode(vtxDevice
, &pitmode
);
1427 sbufWriteU8(dst
, deviceType
);
1428 sbufWriteU8(dst
, vtxSettingsConfig()->band
);
1429 sbufWriteU8(dst
, vtxSettingsConfig()->channel
);
1430 sbufWriteU8(dst
, vtxSettingsConfig()->power
);
1431 sbufWriteU8(dst
, pitmode
);
1433 // Betaflight < 4 doesn't send these fields
1434 sbufWriteU8(dst
, vtxCommonDeviceIsReady(vtxDevice
) ? 1 : 0);
1435 sbufWriteU8(dst
, vtxSettingsConfig()->lowPowerDisarm
);
1436 // future extensions here...
1439 sbufWriteU8(dst
, VTXDEV_UNKNOWN
); // no VTX configured
1443 sbufWriteU8(dst
, VTXDEV_UNKNOWN
); // no VTX configured
1449 const char *name
= systemConfig()->craftName
;
1451 sbufWriteU8(dst
, *name
++);
1456 case MSP2_COMMON_TZ
:
1457 sbufWriteU16(dst
, (uint16_t)timeConfig()->tz_offset
);
1458 sbufWriteU8(dst
, (uint8_t)timeConfig()->tz_automatic_dst
);
1461 case MSP2_INAV_AIR_SPEED
:
1463 sbufWriteU32(dst
, getAirspeedEstimate());
1465 sbufWriteU32(dst
, 0);
1469 case MSP2_INAV_MIXER
:
1470 sbufWriteU8(dst
, mixerConfig()->motorDirectionInverted
);
1471 sbufWriteU16(dst
, 0);
1472 sbufWriteU8(dst
, mixerConfig()->platformType
);
1473 sbufWriteU8(dst
, mixerConfig()->hasFlaps
);
1474 sbufWriteU16(dst
, mixerConfig()->appliedMixerPreset
);
1475 sbufWriteU8(dst
, MAX_SUPPORTED_MOTORS
);
1476 sbufWriteU8(dst
, MAX_SUPPORTED_SERVOS
);
1479 #if defined(USE_OSD)
1480 case MSP2_INAV_OSD_ALARMS
:
1481 sbufWriteU8(dst
, osdConfig()->rssi_alarm
);
1482 sbufWriteU16(dst
, osdConfig()->time_alarm
);
1483 sbufWriteU16(dst
, osdConfig()->alt_alarm
);
1484 sbufWriteU16(dst
, osdConfig()->dist_alarm
);
1485 sbufWriteU16(dst
, osdConfig()->neg_alt_alarm
);
1486 sbufWriteU16(dst
, osdConfig()->gforce_alarm
* 1000);
1487 sbufWriteU16(dst
, (int16_t)(osdConfig()->gforce_axis_alarm_min
* 1000));
1488 sbufWriteU16(dst
, (int16_t)(osdConfig()->gforce_axis_alarm_max
* 1000));
1489 sbufWriteU8(dst
, osdConfig()->current_alarm
);
1490 sbufWriteU16(dst
, osdConfig()->imu_temp_alarm_min
);
1491 sbufWriteU16(dst
, osdConfig()->imu_temp_alarm_max
);
1493 sbufWriteU16(dst
, osdConfig()->baro_temp_alarm_min
);
1494 sbufWriteU16(dst
, osdConfig()->baro_temp_alarm_max
);
1496 sbufWriteU16(dst
, 0);
1497 sbufWriteU16(dst
, 0);
1501 case MSP2_INAV_OSD_PREFERENCES
:
1502 sbufWriteU8(dst
, osdConfig()->video_system
);
1503 sbufWriteU8(dst
, osdConfig()->main_voltage_decimals
);
1504 sbufWriteU8(dst
, osdConfig()->ahi_reverse_roll
);
1505 sbufWriteU8(dst
, osdConfig()->crosshairs_style
);
1506 sbufWriteU8(dst
, osdConfig()->left_sidebar_scroll
);
1507 sbufWriteU8(dst
, osdConfig()->right_sidebar_scroll
);
1508 sbufWriteU8(dst
, osdConfig()->sidebar_scroll_arrows
);
1509 sbufWriteU8(dst
, osdConfig()->units
);
1510 sbufWriteU8(dst
, osdConfig()->stats_energy_unit
);
1515 case MSP2_INAV_OUTPUT_MAPPING
:
1516 for (uint8_t i
= 0; i
< timerHardwareCount
; ++i
)
1517 if (!(timerHardware
[i
].usageFlags
& (TIM_USE_PPM
| TIM_USE_PWM
))) {
1518 sbufWriteU8(dst
, timerHardware
[i
].usageFlags
);
1522 case MSP2_INAV_OUTPUT_MAPPING_EXT
:
1523 for (uint8_t i
= 0; i
< timerHardwareCount
; ++i
)
1524 if (!(timerHardware
[i
].usageFlags
& (TIM_USE_PPM
| TIM_USE_PWM
))) {
1525 #if defined(SITL_BUILD)
1526 sbufWriteU8(dst
, i
);
1528 sbufWriteU8(dst
, timer2id(timerHardware
[i
].tim
));
1530 sbufWriteU8(dst
, timerHardware
[i
].usageFlags
);
1534 case MSP2_INAV_MC_BRAKING
:
1535 #ifdef USE_MR_BRAKING_MODE
1536 sbufWriteU16(dst
, navConfig()->mc
.braking_speed_threshold
);
1537 sbufWriteU16(dst
, navConfig()->mc
.braking_disengage_speed
);
1538 sbufWriteU16(dst
, navConfig()->mc
.braking_timeout
);
1539 sbufWriteU8(dst
, navConfig()->mc
.braking_boost_factor
);
1540 sbufWriteU16(dst
, navConfig()->mc
.braking_boost_timeout
);
1541 sbufWriteU16(dst
, navConfig()->mc
.braking_boost_speed_threshold
);
1542 sbufWriteU16(dst
, navConfig()->mc
.braking_boost_disengage_speed
);
1543 sbufWriteU8(dst
, navConfig()->mc
.braking_bank_angle
);
1547 #ifdef USE_TEMPERATURE_SENSOR
1548 case MSP2_INAV_TEMP_SENSOR_CONFIG
:
1549 for (uint8_t index
= 0; index
< MAX_TEMP_SENSORS
; ++index
) {
1550 const tempSensorConfig_t
*sensorConfig
= tempSensorConfig(index
);
1551 sbufWriteU8(dst
, sensorConfig
->type
);
1552 for (uint8_t addrIndex
= 0; addrIndex
< 8; ++addrIndex
)
1553 sbufWriteU8(dst
, ((uint8_t *)&sensorConfig
->address
)[addrIndex
]);
1554 sbufWriteU16(dst
, sensorConfig
->alarm_min
);
1555 sbufWriteU16(dst
, sensorConfig
->alarm_max
);
1556 sbufWriteU8(dst
, sensorConfig
->osdSymbol
);
1557 for (uint8_t labelIndex
= 0; labelIndex
< TEMPERATURE_LABEL_LEN
; ++labelIndex
)
1558 sbufWriteU8(dst
, sensorConfig
->label
[labelIndex
]);
1563 #ifdef USE_TEMPERATURE_SENSOR
1564 case MSP2_INAV_TEMPERATURES
:
1565 for (uint8_t index
= 0; index
< MAX_TEMP_SENSORS
; ++index
) {
1566 int16_t temperature
;
1567 bool valid
= getSensorTemperature(index
, &temperature
);
1568 sbufWriteU16(dst
, valid
? temperature
: -1000);
1573 #ifdef USE_ESC_SENSOR
1574 case MSP2_INAV_ESC_RPM
:
1576 uint8_t motorCount
= getMotorCount();
1578 for (uint8_t i
= 0; i
< motorCount
; i
++){
1579 const escSensorData_t
*escState
= getEscTelemetry(i
); //Get ESC telemetry
1580 sbufWriteU32(dst
, escState
->rpm
);
1592 #ifdef USE_SAFE_HOME
1593 static mspResult_e
mspFcSafeHomeOutCommand(sbuf_t
*dst
, sbuf_t
*src
)
1595 const uint8_t safe_home_no
= sbufReadU8(src
); // get the home number
1596 if(safe_home_no
< MAX_SAFE_HOMES
) {
1597 sbufWriteU8(dst
, safe_home_no
);
1598 sbufWriteU8(dst
, safeHomeConfig(safe_home_no
)->enabled
);
1599 sbufWriteU32(dst
, safeHomeConfig(safe_home_no
)->lat
);
1600 sbufWriteU32(dst
, safeHomeConfig(safe_home_no
)->lon
);
1601 return MSP_RESULT_ACK
;
1603 return MSP_RESULT_ERROR
;
1609 static mspResult_e
mspFcLogicConditionCommand(sbuf_t
*dst
, sbuf_t
*src
) {
1610 const uint8_t idx
= sbufReadU8(src
);
1611 if (idx
< MAX_LOGIC_CONDITIONS
) {
1612 sbufWriteU8(dst
, logicConditions(idx
)->enabled
);
1613 sbufWriteU8(dst
, logicConditions(idx
)->activatorId
);
1614 sbufWriteU8(dst
, logicConditions(idx
)->operation
);
1615 sbufWriteU8(dst
, logicConditions(idx
)->operandA
.type
);
1616 sbufWriteU32(dst
, logicConditions(idx
)->operandA
.value
);
1617 sbufWriteU8(dst
, logicConditions(idx
)->operandB
.type
);
1618 sbufWriteU32(dst
, logicConditions(idx
)->operandB
.value
);
1619 sbufWriteU8(dst
, logicConditions(idx
)->flags
);
1620 return MSP_RESULT_ACK
;
1622 return MSP_RESULT_ERROR
;
1626 static void mspFcWaypointOutCommand(sbuf_t
*dst
, sbuf_t
*src
)
1628 const uint8_t msp_wp_no
= sbufReadU8(src
); // get the wp number
1629 navWaypoint_t msp_wp
;
1630 getWaypoint(msp_wp_no
, &msp_wp
);
1631 sbufWriteU8(dst
, msp_wp_no
); // wp_no
1632 sbufWriteU8(dst
, msp_wp
.action
); // action (WAYPOINT)
1633 sbufWriteU32(dst
, msp_wp
.lat
); // lat
1634 sbufWriteU32(dst
, msp_wp
.lon
); // lon
1635 sbufWriteU32(dst
, msp_wp
.alt
); // altitude (cm)
1636 sbufWriteU16(dst
, msp_wp
.p1
); // P1
1637 sbufWriteU16(dst
, msp_wp
.p2
); // P2
1638 sbufWriteU16(dst
, msp_wp
.p3
); // P3
1639 sbufWriteU8(dst
, msp_wp
.flag
); // flags
1643 static void mspFcDataFlashReadCommand(sbuf_t
*dst
, sbuf_t
*src
)
1645 const unsigned int dataSize
= sbufBytesRemaining(src
);
1646 uint16_t readLength
;
1648 const uint32_t readAddress
= sbufReadU32(src
);
1651 // uint32_t - address to read from
1652 // uint16_t - size of block to read (optional)
1653 if (dataSize
== sizeof(uint32_t) + sizeof(uint16_t)) {
1654 readLength
= sbufReadU16(src
);
1660 serializeDataflashReadReply(dst
, readAddress
, readLength
);
1664 static mspResult_e
mspFcProcessInCommand(uint16_t cmdMSP
, sbuf_t
*src
)
1669 const unsigned int dataSize
= sbufBytesRemaining(src
);
1672 case MSP_SELECT_SETTING
:
1673 if (sbufReadU8Safe(&tmp_u8
, src
) && (!ARMING_FLAG(ARMED
)))
1674 setConfigProfileAndWriteEEPROM(tmp_u8
);
1676 return MSP_RESULT_ERROR
;
1680 if (sbufReadU16Safe(&tmp_u16
, src
))
1681 updateHeadingHoldTarget(tmp_u16
);
1683 return MSP_RESULT_ERROR
;
1687 case MSP_SET_RAW_RC
:
1689 uint8_t channelCount
= dataSize
/ sizeof(uint16_t);
1690 if ((channelCount
> MAX_SUPPORTED_RC_CHANNEL_COUNT
) || (dataSize
> channelCount
* sizeof(uint16_t))) {
1691 return MSP_RESULT_ERROR
;
1693 uint16_t frame
[MAX_SUPPORTED_RC_CHANNEL_COUNT
];
1694 for (int i
= 0; i
< channelCount
; i
++) {
1695 frame
[i
] = sbufReadU16(src
);
1697 rxMspFrameReceive(frame
, channelCount
);
1703 case MSP_SET_ARMING_CONFIG
:
1704 if (dataSize
== 2) {
1705 sbufReadU8(src
); //Swallow the first byte, used to be auto_disarm_delay
1706 armingConfigMutable()->disarm_kill_switch
= !!sbufReadU8(src
);
1708 return MSP_RESULT_ERROR
;
1711 case MSP_SET_LOOP_TIME
:
1712 if (sbufReadU16Safe(&tmp_u16
, src
))
1713 gyroConfigMutable()->looptime
= tmp_u16
;
1715 return MSP_RESULT_ERROR
;
1719 if (dataSize
== PID_ITEM_COUNT
* 4) {
1720 for (int i
= 0; i
< PID_ITEM_COUNT
; i
++) {
1721 pidBankMutable()->pid
[i
].P
= sbufReadU8(src
);
1722 pidBankMutable()->pid
[i
].I
= sbufReadU8(src
);
1723 pidBankMutable()->pid
[i
].D
= sbufReadU8(src
);
1724 pidBankMutable()->pid
[i
].FF
= sbufReadU8(src
);
1726 schedulePidGainsUpdate();
1727 navigationUsePIDs();
1729 return MSP_RESULT_ERROR
;
1732 case MSP_SET_MODE_RANGE
:
1733 sbufReadU8Safe(&tmp_u8
, src
);
1734 if ((dataSize
== 5) && (tmp_u8
< MAX_MODE_ACTIVATION_CONDITION_COUNT
)) {
1735 modeActivationCondition_t
*mac
= modeActivationConditionsMutable(tmp_u8
);
1736 tmp_u8
= sbufReadU8(src
);
1737 const box_t
*box
= findBoxByPermanentId(tmp_u8
);
1739 mac
->modeId
= box
->boxId
;
1740 mac
->auxChannelIndex
= sbufReadU8(src
);
1741 mac
->range
.startStep
= sbufReadU8(src
);
1742 mac
->range
.endStep
= sbufReadU8(src
);
1744 updateUsedModeActivationConditionFlags();
1746 return MSP_RESULT_ERROR
;
1749 return MSP_RESULT_ERROR
;
1753 case MSP_SET_ADJUSTMENT_RANGE
:
1754 sbufReadU8Safe(&tmp_u8
, src
);
1755 if ((dataSize
== 7) && (tmp_u8
< MAX_ADJUSTMENT_RANGE_COUNT
)) {
1756 adjustmentRange_t
*adjRange
= adjustmentRangesMutable(tmp_u8
);
1757 tmp_u8
= sbufReadU8(src
);
1758 if (tmp_u8
< MAX_SIMULTANEOUS_ADJUSTMENT_COUNT
) {
1759 adjRange
->adjustmentIndex
= tmp_u8
;
1760 adjRange
->auxChannelIndex
= sbufReadU8(src
);
1761 adjRange
->range
.startStep
= sbufReadU8(src
);
1762 adjRange
->range
.endStep
= sbufReadU8(src
);
1763 adjRange
->adjustmentFunction
= sbufReadU8(src
);
1764 adjRange
->auxSwitchChannelIndex
= sbufReadU8(src
);
1766 return MSP_RESULT_ERROR
;
1769 return MSP_RESULT_ERROR
;
1773 case MSP_SET_RC_TUNING
:
1774 if ((dataSize
== 10) || (dataSize
== 11)) {
1775 sbufReadU8(src
); //Read rcRate8, kept for protocol compatibility reasons
1776 // need to cast away const to set controlRateProfile
1777 ((controlRateConfig_t
*)currentControlRateProfile
)->stabilized
.rcExpo8
= sbufReadU8(src
);
1778 for (int i
= 0; i
< 3; i
++) {
1779 tmp_u8
= sbufReadU8(src
);
1781 ((controlRateConfig_t
*)currentControlRateProfile
)->stabilized
.rates
[i
] = constrain(tmp_u8
, SETTING_YAW_RATE_MIN
, SETTING_YAW_RATE_MAX
);
1784 ((controlRateConfig_t
*)currentControlRateProfile
)->stabilized
.rates
[i
] = constrain(tmp_u8
, SETTING_CONSTANT_ROLL_PITCH_RATE_MIN
, SETTING_CONSTANT_ROLL_PITCH_RATE_MAX
);
1787 tmp_u8
= sbufReadU8(src
);
1788 ((controlRateConfig_t
*)currentControlRateProfile
)->throttle
.dynPID
= MIN(tmp_u8
, SETTING_TPA_RATE_MAX
);
1789 ((controlRateConfig_t
*)currentControlRateProfile
)->throttle
.rcMid8
= sbufReadU8(src
);
1790 ((controlRateConfig_t
*)currentControlRateProfile
)->throttle
.rcExpo8
= sbufReadU8(src
);
1791 ((controlRateConfig_t
*)currentControlRateProfile
)->throttle
.pa_breakpoint
= sbufReadU16(src
);
1792 if (dataSize
> 10) {
1793 ((controlRateConfig_t
*)currentControlRateProfile
)->stabilized
.rcYawExpo8
= sbufReadU8(src
);
1796 schedulePidGainsUpdate();
1798 return MSP_RESULT_ERROR
;
1802 case MSP2_INAV_SET_RATE_PROFILE
:
1803 if (dataSize
== 15) {
1804 controlRateConfig_t
*currentControlRateProfile_p
= (controlRateConfig_t
*)currentControlRateProfile
; // need to cast away const to set controlRateProfile
1807 currentControlRateProfile_p
->throttle
.rcMid8
= sbufReadU8(src
);
1808 currentControlRateProfile_p
->throttle
.rcExpo8
= sbufReadU8(src
);
1809 currentControlRateProfile_p
->throttle
.dynPID
= sbufReadU8(src
);
1810 currentControlRateProfile_p
->throttle
.pa_breakpoint
= sbufReadU16(src
);
1813 currentControlRateProfile_p
->stabilized
.rcExpo8
= sbufReadU8(src
);
1814 currentControlRateProfile_p
->stabilized
.rcYawExpo8
= sbufReadU8(src
);
1815 for (uint8_t i
= 0; i
< 3; ++i
) {
1816 tmp_u8
= sbufReadU8(src
);
1818 currentControlRateProfile_p
->stabilized
.rates
[i
] = constrain(tmp_u8
, SETTING_YAW_RATE_MIN
, SETTING_YAW_RATE_MAX
);
1820 currentControlRateProfile_p
->stabilized
.rates
[i
] = constrain(tmp_u8
, SETTING_CONSTANT_ROLL_PITCH_RATE_MIN
, SETTING_CONSTANT_ROLL_PITCH_RATE_MAX
);
1825 currentControlRateProfile_p
->manual
.rcExpo8
= sbufReadU8(src
);
1826 currentControlRateProfile_p
->manual
.rcYawExpo8
= sbufReadU8(src
);
1827 for (uint8_t i
= 0; i
< 3; ++i
) {
1828 tmp_u8
= sbufReadU8(src
);
1830 currentControlRateProfile_p
->manual
.rates
[i
] = constrain(tmp_u8
, SETTING_YAW_RATE_MIN
, SETTING_YAW_RATE_MAX
);
1832 currentControlRateProfile_p
->manual
.rates
[i
] = constrain(tmp_u8
, SETTING_CONSTANT_ROLL_PITCH_RATE_MIN
, SETTING_CONSTANT_ROLL_PITCH_RATE_MAX
);
1837 return MSP_RESULT_ERROR
;
1842 if (dataSize
== 22) {
1843 sbufReadU16(src
); // midrc
1845 sbufReadU16(src
); //Was min_throttle
1846 motorConfigMutable()->maxthrottle
= constrain(sbufReadU16(src
), PWM_RANGE_MIN
, PWM_RANGE_MAX
);
1847 motorConfigMutable()->mincommand
= constrain(sbufReadU16(src
), 0, PWM_RANGE_MAX
);
1849 currentBatteryProfileMutable
->failsafe_throttle
= constrain(sbufReadU16(src
), PWM_RANGE_MIN
, PWM_RANGE_MAX
);
1852 gpsConfigMutable()->provider
= sbufReadU8(src
); // gps_type
1853 sbufReadU8(src
); // gps_baudrate
1854 gpsConfigMutable()->sbasMode
= sbufReadU8(src
); // gps_ubx_sbas
1856 sbufReadU8(src
); // gps_type
1857 sbufReadU8(src
); // gps_baudrate
1858 sbufReadU8(src
); // gps_ubx_sbas
1860 sbufReadU8(src
); // multiwiiCurrentMeterOutput
1861 tmp_u8
= sbufReadU8(src
);
1862 if (tmp_u8
<= MAX_SUPPORTED_RC_CHANNEL_COUNT
) {
1863 rxConfigMutable()->rssi_channel
= tmp_u8
;
1864 rxUpdateRSSISource(); // Changing rssi_channel might change the RSSI source
1869 compassConfigMutable()->mag_declination
= sbufReadU16(src
) * 10;
1875 batteryMetersConfigMutable()->voltage
.scale
= sbufReadU8(src
) * 10;
1876 currentBatteryProfileMutable
->voltage
.cellMin
= sbufReadU8(src
) * 10; // vbatlevel_warn1 in MWC2.3 GUI
1877 currentBatteryProfileMutable
->voltage
.cellMax
= sbufReadU8(src
) * 10; // vbatlevel_warn2 in MWC2.3 GUI
1878 currentBatteryProfileMutable
->voltage
.cellWarning
= sbufReadU8(src
) * 10; // vbatlevel when buzzer starts to alert
1886 return MSP_RESULT_ERROR
;
1889 case MSP2_INAV_SET_MISC
:
1890 if (dataSize
== 41) {
1891 sbufReadU16(src
); // midrc
1893 sbufReadU16(src
); // was min_throttle
1894 motorConfigMutable()->maxthrottle
= constrain(sbufReadU16(src
), PWM_RANGE_MIN
, PWM_RANGE_MAX
);
1895 motorConfigMutable()->mincommand
= constrain(sbufReadU16(src
), 0, PWM_RANGE_MAX
);
1897 currentBatteryProfileMutable
->failsafe_throttle
= constrain(sbufReadU16(src
), PWM_RANGE_MIN
, PWM_RANGE_MAX
);
1900 gpsConfigMutable()->provider
= sbufReadU8(src
); // gps_type
1901 sbufReadU8(src
); // gps_baudrate
1902 gpsConfigMutable()->sbasMode
= sbufReadU8(src
); // gps_ubx_sbas
1904 sbufReadU8(src
); // gps_type
1905 sbufReadU8(src
); // gps_baudrate
1906 sbufReadU8(src
); // gps_ubx_sbas
1909 tmp_u8
= sbufReadU8(src
);
1910 if (tmp_u8
<= MAX_SUPPORTED_RC_CHANNEL_COUNT
)
1911 rxConfigMutable()->rssi_channel
= tmp_u8
;
1914 compassConfigMutable()->mag_declination
= sbufReadU16(src
) * 10;
1920 batteryMetersConfigMutable()->voltage
.scale
= sbufReadU16(src
);
1921 batteryMetersConfigMutable()->voltageSource
= sbufReadU8(src
);
1922 currentBatteryProfileMutable
->cells
= sbufReadU8(src
);
1923 currentBatteryProfileMutable
->voltage
.cellDetect
= sbufReadU16(src
);
1924 currentBatteryProfileMutable
->voltage
.cellMin
= sbufReadU16(src
);
1925 currentBatteryProfileMutable
->voltage
.cellMax
= sbufReadU16(src
);
1926 currentBatteryProfileMutable
->voltage
.cellWarning
= sbufReadU16(src
);
1937 currentBatteryProfileMutable
->capacity
.value
= sbufReadU32(src
);
1938 currentBatteryProfileMutable
->capacity
.warning
= sbufReadU32(src
);
1939 currentBatteryProfileMutable
->capacity
.critical
= sbufReadU32(src
);
1940 currentBatteryProfileMutable
->capacity
.unit
= sbufReadU8(src
);
1941 if ((batteryMetersConfig()->voltageSource
!= BAT_VOLTAGE_RAW
) && (batteryMetersConfig()->voltageSource
!= BAT_VOLTAGE_SAG_COMP
)) {
1942 batteryMetersConfigMutable()->voltageSource
= BAT_VOLTAGE_RAW
;
1943 return MSP_RESULT_ERROR
;
1945 if ((currentBatteryProfile
->capacity
.unit
!= BAT_CAPACITY_UNIT_MAH
) && (currentBatteryProfile
->capacity
.unit
!= BAT_CAPACITY_UNIT_MWH
)) {
1946 currentBatteryProfileMutable
->capacity
.unit
= BAT_CAPACITY_UNIT_MAH
;
1947 return MSP_RESULT_ERROR
;
1950 return MSP_RESULT_ERROR
;
1953 case MSP2_INAV_SET_BATTERY_CONFIG
:
1954 if (dataSize
== 29) {
1956 batteryMetersConfigMutable()->voltage
.scale
= sbufReadU16(src
);
1957 batteryMetersConfigMutable()->voltageSource
= sbufReadU8(src
);
1958 currentBatteryProfileMutable
->cells
= sbufReadU8(src
);
1959 currentBatteryProfileMutable
->voltage
.cellDetect
= sbufReadU16(src
);
1960 currentBatteryProfileMutable
->voltage
.cellMin
= sbufReadU16(src
);
1961 currentBatteryProfileMutable
->voltage
.cellMax
= sbufReadU16(src
);
1962 currentBatteryProfileMutable
->voltage
.cellWarning
= sbufReadU16(src
);
1973 batteryMetersConfigMutable()->current
.offset
= sbufReadU16(src
);
1974 batteryMetersConfigMutable()->current
.scale
= sbufReadU16(src
);
1976 currentBatteryProfileMutable
->capacity
.value
= sbufReadU32(src
);
1977 currentBatteryProfileMutable
->capacity
.warning
= sbufReadU32(src
);
1978 currentBatteryProfileMutable
->capacity
.critical
= sbufReadU32(src
);
1979 currentBatteryProfileMutable
->capacity
.unit
= sbufReadU8(src
);
1980 if ((batteryMetersConfig()->voltageSource
!= BAT_VOLTAGE_RAW
) && (batteryMetersConfig()->voltageSource
!= BAT_VOLTAGE_SAG_COMP
)) {
1981 batteryMetersConfigMutable()->voltageSource
= BAT_VOLTAGE_RAW
;
1982 return MSP_RESULT_ERROR
;
1984 if ((currentBatteryProfile
->capacity
.unit
!= BAT_CAPACITY_UNIT_MAH
) && (currentBatteryProfile
->capacity
.unit
!= BAT_CAPACITY_UNIT_MWH
)) {
1985 currentBatteryProfileMutable
->capacity
.unit
= BAT_CAPACITY_UNIT_MAH
;
1986 return MSP_RESULT_ERROR
;
1989 return MSP_RESULT_ERROR
;
1993 if (dataSize
== 8 * sizeof(uint16_t)) {
1994 for (int i
= 0; i
< 8; i
++) {
1995 const int16_t disarmed
= sbufReadU16(src
);
1996 if (i
< MAX_SUPPORTED_MOTORS
) {
1997 motor_disarmed
[i
] = disarmed
;
2001 return MSP_RESULT_ERROR
;
2004 case MSP_SET_SERVO_CONFIGURATION
:
2005 if (dataSize
!= (1 + 14)) {
2006 return MSP_RESULT_ERROR
;
2008 tmp_u8
= sbufReadU8(src
);
2009 if (tmp_u8
>= MAX_SUPPORTED_SERVOS
) {
2010 return MSP_RESULT_ERROR
;
2012 servoParamsMutable(tmp_u8
)->min
= sbufReadU16(src
);
2013 servoParamsMutable(tmp_u8
)->max
= sbufReadU16(src
);
2014 servoParamsMutable(tmp_u8
)->middle
= sbufReadU16(src
);
2015 servoParamsMutable(tmp_u8
)->rate
= sbufReadU8(src
);
2018 sbufReadU8(src
); // used to be forwardFromChannel, ignored
2019 sbufReadU32(src
); // used to be reversedSources
2020 servoComputeScalingFactors(tmp_u8
);
2024 case MSP_SET_SERVO_MIX_RULE
:
2025 sbufReadU8Safe(&tmp_u8
, src
);
2026 if ((dataSize
== 9) && (tmp_u8
< MAX_SERVO_RULES
)) {
2027 customServoMixersMutable(tmp_u8
)->targetChannel
= sbufReadU8(src
);
2028 customServoMixersMutable(tmp_u8
)->inputSource
= sbufReadU8(src
);
2029 customServoMixersMutable(tmp_u8
)->rate
= sbufReadU16(src
);
2030 customServoMixersMutable(tmp_u8
)->speed
= sbufReadU8(src
);
2031 sbufReadU16(src
); //Read 2bytes for min/max and ignore it
2032 sbufReadU8(src
); //Read 1 byte for `box` and ignore it
2033 loadCustomServoMixer();
2035 return MSP_RESULT_ERROR
;
2038 case MSP2_INAV_SET_SERVO_MIXER
:
2039 sbufReadU8Safe(&tmp_u8
, src
);
2040 if ((dataSize
== 7) && (tmp_u8
< MAX_SERVO_RULES
)) {
2041 customServoMixersMutable(tmp_u8
)->targetChannel
= sbufReadU8(src
);
2042 customServoMixersMutable(tmp_u8
)->inputSource
= sbufReadU8(src
);
2043 customServoMixersMutable(tmp_u8
)->rate
= sbufReadU16(src
);
2044 customServoMixersMutable(tmp_u8
)->speed
= sbufReadU8(src
);
2045 #ifdef USE_PROGRAMMING_FRAMEWORK
2046 customServoMixersMutable(tmp_u8
)->conditionId
= sbufReadU8(src
);
2050 loadCustomServoMixer();
2052 return MSP_RESULT_ERROR
;
2054 #ifdef USE_PROGRAMMING_FRAMEWORK
2055 case MSP2_INAV_SET_LOGIC_CONDITIONS
:
2056 sbufReadU8Safe(&tmp_u8
, src
);
2057 if ((dataSize
== 15) && (tmp_u8
< MAX_LOGIC_CONDITIONS
)) {
2058 logicConditionsMutable(tmp_u8
)->enabled
= sbufReadU8(src
);
2059 logicConditionsMutable(tmp_u8
)->activatorId
= sbufReadU8(src
);
2060 logicConditionsMutable(tmp_u8
)->operation
= sbufReadU8(src
);
2061 logicConditionsMutable(tmp_u8
)->operandA
.type
= sbufReadU8(src
);
2062 logicConditionsMutable(tmp_u8
)->operandA
.value
= sbufReadU32(src
);
2063 logicConditionsMutable(tmp_u8
)->operandB
.type
= sbufReadU8(src
);
2064 logicConditionsMutable(tmp_u8
)->operandB
.value
= sbufReadU32(src
);
2065 logicConditionsMutable(tmp_u8
)->flags
= sbufReadU8(src
);
2067 return MSP_RESULT_ERROR
;
2070 case MSP2_INAV_SET_PROGRAMMING_PID
:
2071 sbufReadU8Safe(&tmp_u8
, src
);
2072 if ((dataSize
== 20) && (tmp_u8
< MAX_PROGRAMMING_PID_COUNT
)) {
2073 programmingPidsMutable(tmp_u8
)->enabled
= sbufReadU8(src
);
2074 programmingPidsMutable(tmp_u8
)->setpoint
.type
= sbufReadU8(src
);
2075 programmingPidsMutable(tmp_u8
)->setpoint
.value
= sbufReadU32(src
);
2076 programmingPidsMutable(tmp_u8
)->measurement
.type
= sbufReadU8(src
);
2077 programmingPidsMutable(tmp_u8
)->measurement
.value
= sbufReadU32(src
);
2078 programmingPidsMutable(tmp_u8
)->gains
.P
= sbufReadU16(src
);
2079 programmingPidsMutable(tmp_u8
)->gains
.I
= sbufReadU16(src
);
2080 programmingPidsMutable(tmp_u8
)->gains
.D
= sbufReadU16(src
);
2081 programmingPidsMutable(tmp_u8
)->gains
.FF
= sbufReadU16(src
);
2083 return MSP_RESULT_ERROR
;
2086 case MSP2_COMMON_SET_MOTOR_MIXER
:
2087 sbufReadU8Safe(&tmp_u8
, src
);
2088 if ((dataSize
== 9) && (tmp_u8
< MAX_SUPPORTED_MOTORS
)) {
2089 primaryMotorMixerMutable(tmp_u8
)->throttle
= constrainf(sbufReadU16(src
) / 1000.0f
, 0.0f
, 1.0f
);
2090 primaryMotorMixerMutable(tmp_u8
)->roll
= constrainf(sbufReadU16(src
) / 1000.0f
, 0.0f
, 4.0f
) - 2.0f
;
2091 primaryMotorMixerMutable(tmp_u8
)->pitch
= constrainf(sbufReadU16(src
) / 1000.0f
, 0.0f
, 4.0f
) - 2.0f
;
2092 primaryMotorMixerMutable(tmp_u8
)->yaw
= constrainf(sbufReadU16(src
) / 1000.0f
, 0.0f
, 4.0f
) - 2.0f
;
2094 return MSP_RESULT_ERROR
;
2098 if (dataSize
== 6) {
2099 reversibleMotorsConfigMutable()->deadband_low
= sbufReadU16(src
);
2100 reversibleMotorsConfigMutable()->deadband_high
= sbufReadU16(src
);
2101 reversibleMotorsConfigMutable()->neutral
= sbufReadU16(src
);
2103 return MSP_RESULT_ERROR
;
2106 case MSP_SET_RC_DEADBAND
:
2107 if (dataSize
== 5) {
2108 rcControlsConfigMutable()->deadband
= sbufReadU8(src
);
2109 rcControlsConfigMutable()->yaw_deadband
= sbufReadU8(src
);
2110 rcControlsConfigMutable()->alt_hold_deadband
= sbufReadU8(src
);
2111 rcControlsConfigMutable()->mid_throttle_deadband
= sbufReadU16(src
);
2113 return MSP_RESULT_ERROR
;
2116 case MSP_SET_RESET_CURR_PID
:
2117 PG_RESET_CURRENT(pidProfile
);
2120 case MSP_SET_SENSOR_ALIGNMENT
:
2121 if (dataSize
== 4) {
2122 sbufReadU8(src
); // was gyroConfigMutable()->gyro_align
2123 sbufReadU8(src
); // was accelerometerConfigMutable()->acc_align
2125 compassConfigMutable()->mag_align
= sbufReadU8(src
);
2130 opticalFlowConfigMutable()->opflow_align
= sbufReadU8(src
);
2135 return MSP_RESULT_ERROR
;
2138 case MSP_SET_ADVANCED_CONFIG
:
2139 if (dataSize
== 9) {
2140 sbufReadU8(src
); // gyroConfig()->gyroSyncDenominator
2141 sbufReadU8(src
); // BF: masterConfig.pid_process_denom
2142 sbufReadU8(src
); // BF: motorConfig()->useUnsyncedPwm
2143 motorConfigMutable()->motorPwmProtocol
= sbufReadU8(src
);
2144 motorConfigMutable()->motorPwmRate
= sbufReadU16(src
);
2145 servoConfigMutable()->servoPwmRate
= sbufReadU16(src
);
2146 sbufReadU8(src
); //Was gyroSync
2148 return MSP_RESULT_ERROR
;
2151 case MSP_SET_FILTER_CONFIG
:
2152 if (dataSize
>= 5) {
2153 gyroConfigMutable()->gyro_main_lpf_hz
= sbufReadU8(src
);
2154 pidProfileMutable()->dterm_lpf_hz
= constrain(sbufReadU16(src
), 0, 500);
2155 pidProfileMutable()->yaw_lpf_hz
= constrain(sbufReadU16(src
), 0, 255);
2156 if (dataSize
>= 9) {
2157 sbufReadU16(src
); //Was gyroConfigMutable()->gyro_notch_hz
2158 sbufReadU16(src
); //Was gyroConfigMutable()->gyro_notch_cutoff
2160 return MSP_RESULT_ERROR
;
2162 if (dataSize
>= 13) {
2167 return MSP_RESULT_ERROR
;
2169 if (dataSize
>= 17) {
2170 sbufReadU16(src
); // Was gyroConfigMutable()->gyro_soft_notch_hz_2
2171 sbufReadU16(src
); // Was gyroConfigMutable()->gyro_soft_notch_cutoff_2
2173 return MSP_RESULT_ERROR
;
2176 if (dataSize
>= 21) {
2177 accelerometerConfigMutable()->acc_notch_hz
= constrain(sbufReadU16(src
), 0, 255);
2178 accelerometerConfigMutable()->acc_notch_cutoff
= constrain(sbufReadU16(src
), 1, 255);
2180 return MSP_RESULT_ERROR
;
2183 if (dataSize
>= 22) {
2184 sbufReadU16(src
); //Was gyro_stage2_lowpass_hz
2186 return MSP_RESULT_ERROR
;
2189 return MSP_RESULT_ERROR
;
2192 case MSP_SET_PID_ADVANCED
:
2193 if (dataSize
== 17) {
2194 sbufReadU16(src
); // pidProfileMutable()->rollPitchItermIgnoreRate
2195 sbufReadU16(src
); // pidProfileMutable()->yawItermIgnoreRate
2196 sbufReadU16(src
); //pidProfile()->yaw_p_limit
2198 sbufReadU8(src
); //BF: pidProfileMutable()->deltaMethod
2199 sbufReadU8(src
); //BF: pidProfileMutable()->vbatPidCompensation
2200 sbufReadU8(src
); //BF: pidProfileMutable()->setpointRelaxRatio
2202 pidProfileMutable()->pidSumLimit
= sbufReadU16(src
);
2203 sbufReadU8(src
); //BF: pidProfileMutable()->itermThrottleGain
2206 * To keep compatibility on MSP frame length level with Betaflight, axis axisAccelerationLimitYaw
2207 * limit will be sent and received in [dps / 10]
2209 pidProfileMutable()->axisAccelerationLimitRollPitch
= sbufReadU16(src
) * 10;
2210 pidProfileMutable()->axisAccelerationLimitYaw
= sbufReadU16(src
) * 10;
2212 return MSP_RESULT_ERROR
;
2215 case MSP_SET_INAV_PID
:
2216 if (dataSize
== 15) {
2217 sbufReadU8(src
); //Legacy, no longer in use async processing value
2218 sbufReadU16(src
); //Legacy, no longer in use async processing value
2219 sbufReadU16(src
); //Legacy, no longer in use async processing value
2220 pidProfileMutable()->heading_hold_rate_limit
= sbufReadU8(src
);
2221 sbufReadU8(src
); //HEADING_HOLD_ERROR_LPF_FREQ
2222 sbufReadU16(src
); //Legacy yaw_jump_prevention_limit
2223 gyroConfigMutable()->gyro_lpf
= sbufReadU8(src
);
2224 accelerometerConfigMutable()->acc_lpf_hz
= sbufReadU8(src
);
2225 sbufReadU8(src
); //reserved
2226 sbufReadU8(src
); //reserved
2227 sbufReadU8(src
); //reserved
2228 sbufReadU8(src
); //reserved
2230 return MSP_RESULT_ERROR
;
2233 case MSP_SET_SENSOR_CONFIG
:
2234 if (dataSize
== 6) {
2235 accelerometerConfigMutable()->acc_hardware
= sbufReadU8(src
);
2237 barometerConfigMutable()->baro_hardware
= sbufReadU8(src
);
2242 compassConfigMutable()->mag_hardware
= sbufReadU8(src
);
2247 pitotmeterConfigMutable()->pitot_hardware
= sbufReadU8(src
);
2251 #ifdef USE_RANGEFINDER
2252 rangefinderConfigMutable()->rangefinder_hardware
= sbufReadU8(src
);
2254 sbufReadU8(src
); // rangefinder hardware
2257 opticalFlowConfigMutable()->opflow_hardware
= sbufReadU8(src
);
2259 sbufReadU8(src
); // optical flow hardware
2262 return MSP_RESULT_ERROR
;
2265 case MSP_SET_NAV_POSHOLD
:
2266 if (dataSize
== 13) {
2267 navConfigMutable()->general
.flags
.user_control_mode
= sbufReadU8(src
);
2268 navConfigMutable()->general
.max_auto_speed
= sbufReadU16(src
);
2269 navConfigMutable()->general
.max_auto_climb_rate
= sbufReadU16(src
);
2270 navConfigMutable()->general
.max_manual_speed
= sbufReadU16(src
);
2271 navConfigMutable()->general
.max_manual_climb_rate
= sbufReadU16(src
);
2272 navConfigMutable()->mc
.max_bank_angle
= sbufReadU8(src
);
2273 navConfigMutable()->mc
.althold_throttle_type
= sbufReadU8(src
);
2274 currentBatteryProfileMutable
->nav
.mc
.hover_throttle
= sbufReadU16(src
);
2276 return MSP_RESULT_ERROR
;
2279 case MSP_SET_RTH_AND_LAND_CONFIG
:
2280 if (dataSize
== 21) {
2281 navConfigMutable()->general
.min_rth_distance
= sbufReadU16(src
);
2282 navConfigMutable()->general
.flags
.rth_climb_first
= sbufReadU8(src
);
2283 navConfigMutable()->general
.flags
.rth_climb_ignore_emerg
= sbufReadU8(src
);
2284 navConfigMutable()->general
.flags
.rth_tail_first
= sbufReadU8(src
);
2285 navConfigMutable()->general
.flags
.rth_allow_landing
= sbufReadU8(src
);
2286 navConfigMutable()->general
.flags
.rth_alt_control_mode
= sbufReadU8(src
);
2287 navConfigMutable()->general
.rth_abort_threshold
= sbufReadU16(src
);
2288 navConfigMutable()->general
.rth_altitude
= sbufReadU16(src
);
2289 navConfigMutable()->general
.land_minalt_vspd
= sbufReadU16(src
);
2290 navConfigMutable()->general
.land_maxalt_vspd
= sbufReadU16(src
);
2291 navConfigMutable()->general
.land_slowdown_minalt
= sbufReadU16(src
);
2292 navConfigMutable()->general
.land_slowdown_maxalt
= sbufReadU16(src
);
2293 navConfigMutable()->general
.emerg_descent_rate
= sbufReadU16(src
);
2295 return MSP_RESULT_ERROR
;
2298 case MSP_SET_FW_CONFIG
:
2299 if (dataSize
== 12) {
2300 currentBatteryProfileMutable
->nav
.fw
.cruise_throttle
= sbufReadU16(src
);
2301 currentBatteryProfileMutable
->nav
.fw
.min_throttle
= sbufReadU16(src
);
2302 currentBatteryProfileMutable
->nav
.fw
.max_throttle
= sbufReadU16(src
);
2303 navConfigMutable()->fw
.max_bank_angle
= sbufReadU8(src
);
2304 navConfigMutable()->fw
.max_climb_angle
= sbufReadU8(src
);
2305 navConfigMutable()->fw
.max_dive_angle
= sbufReadU8(src
);
2306 currentBatteryProfileMutable
->nav
.fw
.pitch_to_throttle
= sbufReadU8(src
);
2307 navConfigMutable()->fw
.loiter_radius
= sbufReadU16(src
);
2309 return MSP_RESULT_ERROR
;
2312 case MSP_SET_CALIBRATION_DATA
:
2313 if (dataSize
>= 18) {
2314 accelerometerConfigMutable()->accZero
.raw
[X
] = sbufReadU16(src
);
2315 accelerometerConfigMutable()->accZero
.raw
[Y
] = sbufReadU16(src
);
2316 accelerometerConfigMutable()->accZero
.raw
[Z
] = sbufReadU16(src
);
2317 accelerometerConfigMutable()->accGain
.raw
[X
] = sbufReadU16(src
);
2318 accelerometerConfigMutable()->accGain
.raw
[Y
] = sbufReadU16(src
);
2319 accelerometerConfigMutable()->accGain
.raw
[Z
] = sbufReadU16(src
);
2322 compassConfigMutable()->magZero
.raw
[X
] = sbufReadU16(src
);
2323 compassConfigMutable()->magZero
.raw
[Y
] = sbufReadU16(src
);
2324 compassConfigMutable()->magZero
.raw
[Z
] = sbufReadU16(src
);
2331 if (dataSize
>= 20) {
2332 opticalFlowConfigMutable()->opflow_scale
= sbufReadU16(src
) / 256.0f
;
2336 if (dataSize
>= 22) {
2337 compassConfigMutable()->magGain
[X
] = sbufReadU16(src
);
2338 compassConfigMutable()->magGain
[Y
] = sbufReadU16(src
);
2339 compassConfigMutable()->magGain
[Z
] = sbufReadU16(src
);
2342 if (dataSize
>= 22) {
2349 return MSP_RESULT_ERROR
;
2352 case MSP_SET_POSITION_ESTIMATION_CONFIG
:
2353 if (dataSize
== 12) {
2354 positionEstimationConfigMutable()->w_z_baro_p
= constrainf(sbufReadU16(src
) / 100.0f
, 0.0f
, 10.0f
);
2355 positionEstimationConfigMutable()->w_z_gps_p
= constrainf(sbufReadU16(src
) / 100.0f
, 0.0f
, 10.0f
);
2356 positionEstimationConfigMutable()->w_z_gps_v
= constrainf(sbufReadU16(src
) / 100.0f
, 0.0f
, 10.0f
);
2357 positionEstimationConfigMutable()->w_xy_gps_p
= constrainf(sbufReadU16(src
) / 100.0f
, 0.0f
, 10.0f
);
2358 positionEstimationConfigMutable()->w_xy_gps_v
= constrainf(sbufReadU16(src
) / 100.0f
, 0.0f
, 10.0f
);
2359 gpsConfigMutable()->gpsMinSats
= constrain(sbufReadU8(src
), 5, 10);
2360 positionEstimationConfigMutable()->use_gps_velned
= constrain(sbufReadU8(src
), 0, 1);
2362 return MSP_RESULT_ERROR
;
2365 case MSP_RESET_CONF
:
2366 if (!ARMING_FLAG(ARMED
)) {
2373 return MSP_RESULT_ERROR
;
2376 case MSP_ACC_CALIBRATION
:
2377 if (!ARMING_FLAG(ARMED
))
2378 accStartCalibration();
2380 return MSP_RESULT_ERROR
;
2383 case MSP_MAG_CALIBRATION
:
2384 if (!ARMING_FLAG(ARMED
))
2385 ENABLE_STATE(CALIBRATE_MAG
);
2387 return MSP_RESULT_ERROR
;
2391 case MSP2_INAV_OPFLOW_CALIBRATION
:
2392 if (!ARMING_FLAG(ARMED
))
2393 opflowStartCalibration();
2395 return MSP_RESULT_ERROR
;
2399 case MSP_EEPROM_WRITE
:
2400 if (!ARMING_FLAG(ARMED
)) {
2406 return MSP_RESULT_ERROR
;
2410 case MSP2_SET_BLACKBOX_CONFIG
:
2411 // Don't allow config to be updated while Blackbox is logging
2412 if ((dataSize
== 9) && blackboxMayEditConfig()) {
2413 blackboxConfigMutable()->device
= sbufReadU8(src
);
2414 blackboxConfigMutable()->rate_num
= sbufReadU16(src
);
2415 blackboxConfigMutable()->rate_denom
= sbufReadU16(src
);
2416 blackboxConfigMutable()->includeFlags
= sbufReadU32(src
);
2418 return MSP_RESULT_ERROR
;
2423 case MSP_SET_OSD_CONFIG
:
2424 sbufReadU8Safe(&tmp_u8
, src
);
2425 // set all the other settings
2426 if ((int8_t)tmp_u8
== -1) {
2427 if (dataSize
>= 10) {
2428 osdConfigMutable()->video_system
= sbufReadU8(src
);
2429 osdConfigMutable()->units
= sbufReadU8(src
);
2430 osdConfigMutable()->rssi_alarm
= sbufReadU8(src
);
2431 currentBatteryProfileMutable
->capacity
.warning
= sbufReadU16(src
);
2432 osdConfigMutable()->time_alarm
= sbufReadU16(src
);
2433 osdConfigMutable()->alt_alarm
= sbufReadU16(src
);
2434 // Won't be read if they weren't provided
2435 sbufReadU16Safe(&osdConfigMutable()->dist_alarm
, src
);
2436 sbufReadU16Safe(&osdConfigMutable()->neg_alt_alarm
, src
);
2438 return MSP_RESULT_ERROR
;
2440 // set a position setting
2441 if ((dataSize
>= 3) && (tmp_u8
< OSD_ITEM_COUNT
)) // tmp_u8 == addr
2442 osdLayoutsConfigMutable()->item_pos
[0][tmp_u8
] = sbufReadU16(src
);
2444 return MSP_RESULT_ERROR
;
2446 // Either a element position change or a units change needs
2447 // a full redraw, since an element can change size significantly
2448 // and the old position or the now unused space due to the
2449 // size change need to be erased.
2450 osdStartFullRedraw();
2453 case MSP_OSD_CHAR_WRITE
:
2454 if (dataSize
>= 55) {
2456 size_t osdCharacterBytes
;
2458 if (dataSize
>= OSD_CHAR_VISIBLE_BYTES
+ 2) {
2459 if (dataSize
>= OSD_CHAR_BYTES
+ 2) {
2460 // 16 bit address, full char with metadata
2461 addr
= sbufReadU16(src
);
2462 osdCharacterBytes
= OSD_CHAR_BYTES
;
2463 } else if (dataSize
>= OSD_CHAR_BYTES
+ 1) {
2464 // 8 bit address, full char with metadata
2465 addr
= sbufReadU8(src
);
2466 osdCharacterBytes
= OSD_CHAR_BYTES
;
2468 // 16 bit character address, only visible char bytes
2469 addr
= sbufReadU16(src
);
2470 osdCharacterBytes
= OSD_CHAR_VISIBLE_BYTES
;
2473 // 8 bit character address, only visible char bytes
2474 addr
= sbufReadU8(src
);
2475 osdCharacterBytes
= OSD_CHAR_VISIBLE_BYTES
;
2477 for (unsigned ii
= 0; ii
< MIN(osdCharacterBytes
, sizeof(chr
.data
)); ii
++) {
2478 chr
.data
[ii
] = sbufReadU8(src
);
2480 displayPort_t
*osdDisplayPort
= osdGetDisplayPort();
2481 if (osdDisplayPort
) {
2482 displayWriteFontCharacter(osdDisplayPort
, addr
, &chr
);
2485 return MSP_RESULT_ERROR
;
2490 #ifdef USE_VTX_CONTROL
2491 case MSP_SET_VTX_CONFIG
:
2492 if (dataSize
>= 2) {
2493 vtxDevice_t
*vtxDevice
= vtxCommonDevice();
2495 if (vtxCommonGetDeviceType(vtxDevice
) != VTXDEV_UNKNOWN
) {
2496 uint16_t newFrequency
= sbufReadU16(src
);
2497 if (newFrequency
<= VTXCOMMON_MSP_BANDCHAN_CHKVAL
) { //value is band and channel
2498 const uint8_t newBand
= (newFrequency
/ 8) + 1;
2499 const uint8_t newChannel
= (newFrequency
% 8) + 1;
2501 if(vtxSettingsConfig()->band
!= newBand
|| vtxSettingsConfig()->channel
!= newChannel
) {
2502 vtxCommonSetBandAndChannel(vtxDevice
, newBand
, newChannel
);
2505 vtxSettingsConfigMutable()->band
= newBand
;
2506 vtxSettingsConfigMutable()->channel
= newChannel
;
2509 if (sbufBytesRemaining(src
) > 1) {
2510 uint8_t newPower
= sbufReadU8(src
);
2511 uint8_t currentPower
= 0;
2512 vtxCommonGetPowerIndex(vtxDevice
, ¤tPower
);
2513 if (newPower
!= currentPower
) {
2514 vtxCommonSetPowerByIndex(vtxDevice
, newPower
);
2515 vtxSettingsConfigMutable()->power
= newPower
;
2518 // Delegate pitmode to vtx directly
2519 const uint8_t newPitmode
= sbufReadU8(src
);
2520 uint8_t currentPitmode
= 0;
2521 vtxCommonGetPitMode(vtxDevice
, ¤tPitmode
);
2522 if (currentPitmode
!= newPitmode
) {
2523 vtxCommonSetPitMode(vtxDevice
, newPitmode
);
2526 if (sbufBytesRemaining(src
) > 0) {
2527 vtxSettingsConfigMutable()->lowPowerDisarm
= sbufReadU8(src
);
2533 return MSP_RESULT_ERROR
;
2539 case MSP_DATAFLASH_ERASE
:
2540 flashfsEraseCompletely();
2545 case MSP_SET_RAW_GPS
:
2546 if (dataSize
== 14) {
2547 gpsSol
.fixType
= sbufReadU8(src
);
2548 if (gpsSol
.fixType
) {
2549 ENABLE_STATE(GPS_FIX
);
2551 DISABLE_STATE(GPS_FIX
);
2553 gpsSol
.flags
.validVelNE
= false;
2554 gpsSol
.flags
.validVelD
= false;
2555 gpsSol
.flags
.validEPE
= false;
2556 gpsSol
.flags
.validTime
= false;
2557 gpsSol
.numSat
= sbufReadU8(src
);
2558 gpsSol
.llh
.lat
= sbufReadU32(src
);
2559 gpsSol
.llh
.lon
= sbufReadU32(src
);
2560 gpsSol
.llh
.alt
= 100 * sbufReadU16(src
); // require cm
2561 gpsSol
.groundSpeed
= sbufReadU16(src
);
2562 gpsSol
.velNED
[X
] = 0;
2563 gpsSol
.velNED
[Y
] = 0;
2564 gpsSol
.velNED
[Z
] = 0;
2567 // Feed data to navigation
2568 sensorsSet(SENSOR_GPS
);
2571 return MSP_RESULT_ERROR
;
2576 if (dataSize
== 21) {
2577 const uint8_t msp_wp_no
= sbufReadU8(src
); // get the waypoint number
2578 navWaypoint_t msp_wp
;
2579 msp_wp
.action
= sbufReadU8(src
); // action
2580 msp_wp
.lat
= sbufReadU32(src
); // lat
2581 msp_wp
.lon
= sbufReadU32(src
); // lon
2582 msp_wp
.alt
= sbufReadU32(src
); // to set altitude (cm)
2583 msp_wp
.p1
= sbufReadU16(src
); // P1
2584 msp_wp
.p2
= sbufReadU16(src
); // P2
2585 msp_wp
.p3
= sbufReadU16(src
); // P3
2586 msp_wp
.flag
= sbufReadU8(src
); // future: to set nav flag
2587 setWaypoint(msp_wp_no
, &msp_wp
);
2589 return MSP_RESULT_ERROR
;
2591 case MSP2_COMMON_SET_RADAR_POS
:
2592 if (dataSize
== 19) {
2593 const uint8_t msp_radar_no
= MIN(sbufReadU8(src
), RADAR_MAX_POIS
- 1); // Radar poi number, 0 to 3
2594 radar_pois
[msp_radar_no
].state
= sbufReadU8(src
); // 0=undefined, 1=armed, 2=lost
2595 radar_pois
[msp_radar_no
].gps
.lat
= sbufReadU32(src
); // lat 10E7
2596 radar_pois
[msp_radar_no
].gps
.lon
= sbufReadU32(src
); // lon 10E7
2597 radar_pois
[msp_radar_no
].gps
.alt
= sbufReadU32(src
); // altitude (cm)
2598 radar_pois
[msp_radar_no
].heading
= sbufReadU16(src
); // °
2599 radar_pois
[msp_radar_no
].speed
= sbufReadU16(src
); // cm/s
2600 radar_pois
[msp_radar_no
].lq
= sbufReadU8(src
); // Link quality, from 0 to 4
2602 return MSP_RESULT_ERROR
;
2605 case MSP_SET_FEATURE
:
2606 if (dataSize
== 4) {
2608 featureSet(sbufReadU32(src
)); // features bitmap
2609 rxUpdateRSSISource(); // For FEATURE_RSSI_ADC
2611 return MSP_RESULT_ERROR
;
2614 case MSP_SET_BOARD_ALIGNMENT
:
2615 if (dataSize
== 6) {
2616 boardAlignmentMutable()->rollDeciDegrees
= sbufReadU16(src
);
2617 boardAlignmentMutable()->pitchDeciDegrees
= sbufReadU16(src
);
2618 boardAlignmentMutable()->yawDeciDegrees
= sbufReadU16(src
);
2620 return MSP_RESULT_ERROR
;
2623 case MSP_SET_VOLTAGE_METER_CONFIG
:
2624 if (dataSize
== 4) {
2626 batteryMetersConfigMutable()->voltage
.scale
= sbufReadU8(src
) * 10;
2627 currentBatteryProfileMutable
->voltage
.cellMin
= sbufReadU8(src
) * 10;
2628 currentBatteryProfileMutable
->voltage
.cellMax
= sbufReadU8(src
) * 10;
2629 currentBatteryProfileMutable
->voltage
.cellWarning
= sbufReadU8(src
) * 10;
2637 return MSP_RESULT_ERROR
;
2640 case MSP_SET_CURRENT_METER_CONFIG
:
2641 if (dataSize
== 7) {
2642 batteryMetersConfigMutable()->current
.scale
= sbufReadU16(src
);
2643 batteryMetersConfigMutable()->current
.offset
= sbufReadU16(src
);
2644 batteryMetersConfigMutable()->current
.type
= sbufReadU8(src
);
2645 currentBatteryProfileMutable
->capacity
.value
= sbufReadU16(src
);
2647 return MSP_RESULT_ERROR
;
2651 if (dataSize
== 1) {
2652 sbufReadU8(src
); //This is ignored, no longer supporting mixerMode
2653 mixerUpdateStateFlags(); // Required for correct preset functionality
2655 return MSP_RESULT_ERROR
;
2658 case MSP_SET_RX_CONFIG
:
2659 if (dataSize
== 24) {
2660 rxConfigMutable()->serialrx_provider
= sbufReadU8(src
);
2661 rxConfigMutable()->maxcheck
= sbufReadU16(src
);
2662 sbufReadU16(src
); // midrc
2663 rxConfigMutable()->mincheck
= sbufReadU16(src
);
2664 #ifdef USE_SPEKTRUM_BIND
2665 rxConfigMutable()->spektrum_sat_bind
= sbufReadU8(src
);
2669 rxConfigMutable()->rx_min_usec
= sbufReadU16(src
);
2670 rxConfigMutable()->rx_max_usec
= sbufReadU16(src
);
2671 sbufReadU8(src
); // for compatibility with betaflight (rcInterpolation)
2672 sbufReadU8(src
); // for compatibility with betaflight (rcInterpolationInterval)
2673 sbufReadU16(src
); // for compatibility with betaflight (airModeActivateThreshold)
2677 sbufReadU8(src
); // for compatibility with betaflight (fpvCamAngleDegrees)
2678 rxConfigMutable()->receiverType
= sbufReadU8(src
); // Won't be modified if buffer is not large enough
2680 return MSP_RESULT_ERROR
;
2683 case MSP_SET_FAILSAFE_CONFIG
:
2684 if (dataSize
== 20) {
2685 failsafeConfigMutable()->failsafe_delay
= sbufReadU8(src
);
2686 failsafeConfigMutable()->failsafe_off_delay
= sbufReadU8(src
);
2687 currentBatteryProfileMutable
->failsafe_throttle
= sbufReadU16(src
);
2688 sbufReadU8(src
); // was failsafe_kill_switch
2689 failsafeConfigMutable()->failsafe_throttle_low_delay
= sbufReadU16(src
);
2690 failsafeConfigMutable()->failsafe_procedure
= sbufReadU8(src
);
2691 failsafeConfigMutable()->failsafe_recovery_delay
= sbufReadU8(src
);
2692 failsafeConfigMutable()->failsafe_fw_roll_angle
= (int16_t)sbufReadU16(src
);
2693 failsafeConfigMutable()->failsafe_fw_pitch_angle
= (int16_t)sbufReadU16(src
);
2694 failsafeConfigMutable()->failsafe_fw_yaw_rate
= (int16_t)sbufReadU16(src
);
2695 failsafeConfigMutable()->failsafe_stick_motion_threshold
= sbufReadU16(src
);
2696 failsafeConfigMutable()->failsafe_min_distance
= sbufReadU16(src
);
2697 failsafeConfigMutable()->failsafe_min_distance_procedure
= sbufReadU8(src
);
2699 return MSP_RESULT_ERROR
;
2702 case MSP_SET_RSSI_CONFIG
:
2703 sbufReadU8Safe(&tmp_u8
, src
);
2704 if ((dataSize
== 1) && (tmp_u8
<= MAX_SUPPORTED_RC_CHANNEL_COUNT
)) {
2705 rxConfigMutable()->rssi_channel
= tmp_u8
;
2706 rxUpdateRSSISource();
2708 return MSP_RESULT_ERROR
;
2712 case MSP_SET_RX_MAP
:
2713 if (dataSize
== MAX_MAPPABLE_RX_INPUTS
) {
2714 for (int i
= 0; i
< MAX_MAPPABLE_RX_INPUTS
; i
++) {
2715 rxConfigMutable()->rcmap
[i
] = sbufReadU8(src
);
2718 return MSP_RESULT_ERROR
;
2721 case MSP2_COMMON_SET_SERIAL_CONFIG
:
2723 uint8_t portConfigSize
= sizeof(uint8_t) + sizeof(uint32_t) + (sizeof(uint8_t) * 4);
2725 if (dataSize
% portConfigSize
!= 0) {
2726 return MSP_RESULT_ERROR
;
2729 uint8_t remainingPortsInPacket
= dataSize
/ portConfigSize
;
2731 while (remainingPortsInPacket
--) {
2732 uint8_t identifier
= sbufReadU8(src
);
2734 serialPortConfig_t
*portConfig
= serialFindPortConfiguration(identifier
);
2736 return MSP_RESULT_ERROR
;
2739 portConfig
->identifier
= identifier
;
2740 portConfig
->functionMask
= sbufReadU32(src
);
2741 portConfig
->msp_baudrateIndex
= constrain(sbufReadU8(src
), BAUD_MIN
, BAUD_MAX
);
2742 portConfig
->gps_baudrateIndex
= constrain(sbufReadU8(src
), BAUD_MIN
, BAUD_MAX
);
2743 portConfig
->telemetry_baudrateIndex
= constrain(sbufReadU8(src
), BAUD_MIN
, BAUD_MAX
);
2744 portConfig
->peripheral_baudrateIndex
= constrain(sbufReadU8(src
), BAUD_MIN
, BAUD_MAX
);
2749 #ifdef USE_LED_STRIP
2750 case MSP_SET_LED_COLORS
:
2751 if (dataSize
== LED_CONFIGURABLE_COLOR_COUNT
* 4) {
2752 for (int i
= 0; i
< LED_CONFIGURABLE_COLOR_COUNT
; i
++) {
2753 hsvColor_t
*color
= &ledStripConfigMutable()->colors
[i
];
2754 color
->h
= sbufReadU16(src
);
2755 color
->s
= sbufReadU8(src
);
2756 color
->v
= sbufReadU8(src
);
2759 return MSP_RESULT_ERROR
;
2762 case MSP_SET_LED_STRIP_CONFIG
:
2763 if (dataSize
== (1 + sizeof(uint32_t))) {
2764 tmp_u8
= sbufReadU8(src
);
2765 if (tmp_u8
>= LED_MAX_STRIP_LENGTH
) {
2766 return MSP_RESULT_ERROR
;
2768 ledConfig_t
*ledConfig
= &ledStripConfigMutable()->ledConfigs
[tmp_u8
];
2770 uint32_t legacyConfig
= sbufReadU32(src
);
2772 ledConfig
->led_position
= legacyConfig
& 0xFF;
2773 ledConfig
->led_function
= (legacyConfig
>> 8) & 0xF;
2774 ledConfig
->led_overlay
= (legacyConfig
>> 12) & 0x3F;
2775 ledConfig
->led_color
= (legacyConfig
>> 18) & 0xF;
2776 ledConfig
->led_direction
= (legacyConfig
>> 22) & 0x3F;
2777 ledConfig
->led_params
= (legacyConfig
>> 28) & 0xF;
2779 reevaluateLedConfig();
2781 return MSP_RESULT_ERROR
;
2784 case MSP2_INAV_SET_LED_STRIP_CONFIG_EX
:
2785 if (dataSize
== (1 + sizeof(ledConfig_t
))) {
2786 tmp_u8
= sbufReadU8(src
);
2787 if (tmp_u8
>= LED_MAX_STRIP_LENGTH
) {
2788 return MSP_RESULT_ERROR
;
2790 ledConfig_t
*ledConfig
= &ledStripConfigMutable()->ledConfigs
[tmp_u8
];
2791 sbufReadDataSafe(src
, ledConfig
, sizeof(ledConfig_t
));
2792 reevaluateLedConfig();
2794 return MSP_RESULT_ERROR
;
2797 case MSP_SET_LED_STRIP_MODECOLOR
:
2798 if (dataSize
== 3) {
2799 ledModeIndex_e modeIdx
= sbufReadU8(src
);
2800 int funIdx
= sbufReadU8(src
);
2801 int color
= sbufReadU8(src
);
2803 if (!setModeColor(modeIdx
, funIdx
, color
))
2804 return MSP_RESULT_ERROR
;
2806 return MSP_RESULT_ERROR
;
2810 #ifdef NAV_NON_VOLATILE_WAYPOINT_STORAGE
2811 case MSP_WP_MISSION_LOAD
:
2812 sbufReadU8Safe(NULL
, src
); // Mission ID (reserved)
2813 if ((dataSize
!= 1) || (!loadNonVolatileWaypointList(false)))
2814 return MSP_RESULT_ERROR
;
2817 case MSP_WP_MISSION_SAVE
:
2818 sbufReadU8Safe(NULL
, src
); // Mission ID (reserved)
2819 if ((dataSize
!= 1) || (!saveNonVolatileWaypointList()))
2820 return MSP_RESULT_ERROR
;
2825 if (dataSize
== 6) {
2826 // Use seconds and milliseconds to make senders
2827 // easier to implement. Generating a 64 bit value
2828 // might not be trivial in some platforms.
2829 int32_t secs
= (int32_t)sbufReadU32(src
);
2830 uint16_t millis
= sbufReadU16(src
);
2831 rtcTime_t t
= rtcTimeMake(secs
, millis
);
2834 return MSP_RESULT_ERROR
;
2837 case MSP_SET_TX_INFO
:
2839 // This message will be sent while the aircraft is
2840 // armed. Better to guard ourselves against potentially
2841 // malformed requests.
2843 if (sbufReadU8Safe(&rssi
, src
)) {
2844 setRSSIFromMSP(rssi
);
2850 if (dataSize
<= MAX_NAME_LENGTH
) {
2851 char *name
= systemConfigMutable()->craftName
;
2852 int len
= MIN(MAX_NAME_LENGTH
, (int)dataSize
);
2853 sbufReadData(src
, name
, len
);
2854 memset(&name
[len
], '\0', (MAX_NAME_LENGTH
+ 1) - len
);
2856 return MSP_RESULT_ERROR
;
2859 case MSP2_COMMON_SET_TZ
:
2861 timeConfigMutable()->tz_offset
= (int16_t)sbufReadU16(src
);
2862 else if (dataSize
== 3) {
2863 timeConfigMutable()->tz_offset
= (int16_t)sbufReadU16(src
);
2864 timeConfigMutable()->tz_automatic_dst
= (uint8_t)sbufReadU8(src
);
2866 return MSP_RESULT_ERROR
;
2869 case MSP2_INAV_SET_MIXER
:
2870 if (dataSize
== 9) {
2871 mixerConfigMutable()->motorDirectionInverted
= sbufReadU8(src
);
2872 sbufReadU16(src
); // Was yaw_jump_prevention_limit
2873 mixerConfigMutable()->platformType
= sbufReadU8(src
);
2874 mixerConfigMutable()->hasFlaps
= sbufReadU8(src
);
2875 mixerConfigMutable()->appliedMixerPreset
= sbufReadU16(src
);
2876 sbufReadU8(src
); //Read and ignore MAX_SUPPORTED_MOTORS
2877 sbufReadU8(src
); //Read and ignore MAX_SUPPORTED_SERVOS
2878 mixerUpdateStateFlags();
2880 return MSP_RESULT_ERROR
;
2883 #if defined(USE_OSD)
2884 case MSP2_INAV_OSD_SET_LAYOUT_ITEM
:
2887 if (!sbufReadU8Safe(&layout
, src
)) {
2888 return MSP_RESULT_ERROR
;
2891 if (!sbufReadU8Safe(&item
, src
)) {
2892 return MSP_RESULT_ERROR
;
2894 if (!sbufReadU16Safe(&osdLayoutsConfigMutable()->item_pos
[layout
][item
], src
)) {
2895 return MSP_RESULT_ERROR
;
2897 // If the layout is not already overriden and it's different
2898 // than the layout for the item that was just configured,
2899 // override it for 10 seconds.
2901 int activeLayout
= osdGetActiveLayout(&overridden
);
2902 if (activeLayout
!= layout
&& !overridden
) {
2903 osdOverrideLayout(layout
, 10000);
2905 osdStartFullRedraw();
2911 case MSP2_INAV_OSD_SET_ALARMS
:
2913 if (dataSize
== 24) {
2914 osdConfigMutable()->rssi_alarm
= sbufReadU8(src
);
2915 osdConfigMutable()->time_alarm
= sbufReadU16(src
);
2916 osdConfigMutable()->alt_alarm
= sbufReadU16(src
);
2917 osdConfigMutable()->dist_alarm
= sbufReadU16(src
);
2918 osdConfigMutable()->neg_alt_alarm
= sbufReadU16(src
);
2919 tmp_u16
= sbufReadU16(src
);
2920 osdConfigMutable()->gforce_alarm
= tmp_u16
/ 1000.0f
;
2921 tmp_u16
= sbufReadU16(src
);
2922 osdConfigMutable()->gforce_axis_alarm_min
= (int16_t)tmp_u16
/ 1000.0f
;
2923 tmp_u16
= sbufReadU16(src
);
2924 osdConfigMutable()->gforce_axis_alarm_max
= (int16_t)tmp_u16
/ 1000.0f
;
2925 osdConfigMutable()->current_alarm
= sbufReadU8(src
);
2926 osdConfigMutable()->imu_temp_alarm_min
= sbufReadU16(src
);
2927 osdConfigMutable()->imu_temp_alarm_max
= sbufReadU16(src
);
2929 osdConfigMutable()->baro_temp_alarm_min
= sbufReadU16(src
);
2930 osdConfigMutable()->baro_temp_alarm_max
= sbufReadU16(src
);
2933 return MSP_RESULT_ERROR
;
2938 case MSP2_INAV_OSD_SET_PREFERENCES
:
2940 if (dataSize
== 9) {
2941 osdConfigMutable()->video_system
= sbufReadU8(src
);
2942 osdConfigMutable()->main_voltage_decimals
= sbufReadU8(src
);
2943 osdConfigMutable()->ahi_reverse_roll
= sbufReadU8(src
);
2944 osdConfigMutable()->crosshairs_style
= sbufReadU8(src
);
2945 osdConfigMutable()->left_sidebar_scroll
= sbufReadU8(src
);
2946 osdConfigMutable()->right_sidebar_scroll
= sbufReadU8(src
);
2947 osdConfigMutable()->sidebar_scroll_arrows
= sbufReadU8(src
);
2948 osdConfigMutable()->units
= sbufReadU8(src
);
2949 osdConfigMutable()->stats_energy_unit
= sbufReadU8(src
);
2950 osdStartFullRedraw();
2952 return MSP_RESULT_ERROR
;
2958 case MSP2_INAV_SET_MC_BRAKING
:
2959 #ifdef USE_MR_BRAKING_MODE
2960 if (dataSize
== 14) {
2961 navConfigMutable()->mc
.braking_speed_threshold
= sbufReadU16(src
);
2962 navConfigMutable()->mc
.braking_disengage_speed
= sbufReadU16(src
);
2963 navConfigMutable()->mc
.braking_timeout
= sbufReadU16(src
);
2964 navConfigMutable()->mc
.braking_boost_factor
= sbufReadU8(src
);
2965 navConfigMutable()->mc
.braking_boost_timeout
= sbufReadU16(src
);
2966 navConfigMutable()->mc
.braking_boost_speed_threshold
= sbufReadU16(src
);
2967 navConfigMutable()->mc
.braking_boost_disengage_speed
= sbufReadU16(src
);
2968 navConfigMutable()->mc
.braking_bank_angle
= sbufReadU8(src
);
2971 return MSP_RESULT_ERROR
;
2974 case MSP2_INAV_SELECT_BATTERY_PROFILE
:
2975 if (!ARMING_FLAG(ARMED
) && sbufReadU8Safe(&tmp_u8
, src
)) {
2976 setConfigBatteryProfileAndWriteEEPROM(tmp_u8
);
2978 return MSP_RESULT_ERROR
;
2982 #ifdef USE_TEMPERATURE_SENSOR
2983 case MSP2_INAV_SET_TEMP_SENSOR_CONFIG
:
2984 if (dataSize
== sizeof(tempSensorConfig_t
) * MAX_TEMP_SENSORS
) {
2985 for (uint8_t index
= 0; index
< MAX_TEMP_SENSORS
; ++index
) {
2986 tempSensorConfig_t
*sensorConfig
= tempSensorConfigMutable(index
);
2987 sensorConfig
->type
= sbufReadU8(src
);
2988 for (uint8_t addrIndex
= 0; addrIndex
< 8; ++addrIndex
)
2989 ((uint8_t *)&sensorConfig
->address
)[addrIndex
] = sbufReadU8(src
);
2990 sensorConfig
->alarm_min
= sbufReadU16(src
);
2991 sensorConfig
->alarm_max
= sbufReadU16(src
);
2992 tmp_u8
= sbufReadU8(src
);
2993 sensorConfig
->osdSymbol
= tmp_u8
> TEMP_SENSOR_SYM_COUNT
? 0 : tmp_u8
;
2994 for (uint8_t labelIndex
= 0; labelIndex
< TEMPERATURE_LABEL_LEN
; ++labelIndex
)
2995 sensorConfig
->label
[labelIndex
] = toupper(sbufReadU8(src
));
2998 return MSP_RESULT_ERROR
;
3002 #ifdef MSP_FIRMWARE_UPDATE
3003 case MSP2_INAV_FWUPDT_PREPARE
:
3004 if (!firmwareUpdatePrepare(sbufReadU32(src
))) {
3005 return MSP_RESULT_ERROR
;
3008 case MSP2_INAV_FWUPDT_STORE
:
3009 if (!firmwareUpdateStore(sbufPtr(src
), sbufBytesRemaining(src
))) {
3010 return MSP_RESULT_ERROR
;
3013 case MSP2_INAV_FWUPDT_EXEC
:
3014 firmwareUpdateExec(sbufReadU8(src
));
3015 return MSP_RESULT_ERROR
; // will only be reached if the update is not ready
3017 case MSP2_INAV_FWUPDT_ROLLBACK_PREPARE
:
3018 if (!firmwareUpdateRollbackPrepare()) {
3019 return MSP_RESULT_ERROR
;
3022 case MSP2_INAV_FWUPDT_ROLLBACK_EXEC
:
3023 firmwareUpdateRollbackExec();
3024 return MSP_RESULT_ERROR
; // will only be reached if the rollback is not ready
3027 #ifdef USE_SAFE_HOME
3028 case MSP2_INAV_SET_SAFEHOME
:
3029 if (dataSize
== 10) {
3031 if (!sbufReadU8Safe(&i
, src
) || i
>= MAX_SAFE_HOMES
) {
3032 return MSP_RESULT_ERROR
;
3034 safeHomeConfigMutable(i
)->enabled
= sbufReadU8(src
);
3035 safeHomeConfigMutable(i
)->lat
= sbufReadU32(src
);
3036 safeHomeConfigMutable(i
)->lon
= sbufReadU32(src
);
3038 return MSP_RESULT_ERROR
;
3044 return MSP_RESULT_ERROR
;
3046 return MSP_RESULT_ACK
;
3049 static const setting_t
*mspReadSetting(sbuf_t
*src
)
3051 char name
[SETTING_MAX_NAME_LENGTH
];
3056 if (!sbufReadU8Safe(&c
, src
)) {
3062 // Payload starts with a zero, setting index
3063 // as uint16_t follows
3064 if (sbufReadU16Safe(&id
, src
)) {
3065 return settingGet(id
);
3071 if (s
== SETTING_MAX_NAME_LENGTH
) {
3076 return settingFind(name
);
3079 static bool mspSettingCommand(sbuf_t
*dst
, sbuf_t
*src
)
3081 const setting_t
*setting
= mspReadSetting(src
);
3086 const void *ptr
= settingGetValuePointer(setting
);
3087 size_t size
= settingGetValueSize(setting
);
3088 sbufWriteDataSafe(dst
, ptr
, size
);
3092 static bool mspSetSettingCommand(sbuf_t
*dst
, sbuf_t
*src
)
3096 const setting_t
*setting
= mspReadSetting(src
);
3101 setting_min_t min
= settingGetMin(setting
);
3102 setting_max_t max
= settingGetMax(setting
);
3104 void *ptr
= settingGetValuePointer(setting
);
3105 switch (SETTING_TYPE(setting
)) {
3109 if (!sbufReadU8Safe(&val
, src
)) {
3115 *((uint8_t*)ptr
) = val
;
3121 if (!sbufReadI8Safe(&val
, src
)) {
3124 if (val
< min
|| val
> (int8_t)max
) {
3127 *((int8_t*)ptr
) = val
;
3133 if (!sbufReadU16Safe(&val
, src
)) {
3139 *((uint16_t*)ptr
) = val
;
3145 if (!sbufReadI16Safe(&val
, src
)) {
3148 if (val
< min
|| val
> (int16_t)max
) {
3151 *((int16_t*)ptr
) = val
;
3157 if (!sbufReadU32Safe(&val
, src
)) {
3163 *((uint32_t*)ptr
) = val
;
3169 if (!sbufReadDataSafe(src
, &val
, sizeof(float))) {
3172 if (val
< (float)min
|| val
> (float)max
) {
3175 *((float*)ptr
) = val
;
3180 settingSetString(setting
, (const char*)sbufPtr(src
), sbufBytesRemaining(src
));
3188 static bool mspSettingInfoCommand(sbuf_t
*dst
, sbuf_t
*src
)
3190 const setting_t
*setting
= mspReadSetting(src
);
3195 char name_buf
[SETTING_MAX_WORD_LENGTH
+1];
3196 settingGetName(setting
, name_buf
);
3197 sbufWriteDataSafe(dst
, name_buf
, strlen(name_buf
) + 1);
3199 // Parameter Group ID
3200 sbufWriteU16(dst
, settingGetPgn(setting
));
3202 // Type, section and mode
3203 sbufWriteU8(dst
, SETTING_TYPE(setting
));
3204 sbufWriteU8(dst
, SETTING_SECTION(setting
));
3205 sbufWriteU8(dst
, SETTING_MODE(setting
));
3208 int32_t min
= settingGetMin(setting
);
3209 sbufWriteU32(dst
, (uint32_t)min
);
3211 uint32_t max
= settingGetMax(setting
);
3212 sbufWriteU32(dst
, max
);
3214 // Absolute setting index
3215 sbufWriteU16(dst
, settingGetIndex(setting
));
3217 // If the setting is profile based, send the current one
3218 // and the count, both as uint8_t. For MASTER_VALUE, we
3219 // send two zeroes, so the MSP client can assume there
3220 // will always be two bytes.
3221 switch (SETTING_SECTION(setting
)) {
3223 sbufWriteU8(dst
, 0);
3224 sbufWriteU8(dst
, 0);
3230 case CONTROL_RATE_VALUE
:
3231 sbufWriteU8(dst
, getConfigProfile());
3232 sbufWriteU8(dst
, MAX_PROFILE_COUNT
);
3234 case BATTERY_CONFIG_VALUE
:
3235 sbufWriteU8(dst
, getConfigBatteryProfile());
3236 sbufWriteU8(dst
, MAX_BATTERY_PROFILE_COUNT
);
3240 // If the setting uses a table, send each possible string (null terminated)
3241 if (SETTING_MODE(setting
) == MODE_LOOKUP
) {
3242 for (int ii
= (int)min
; ii
<= (int)max
; ii
++) {
3243 const char *name
= settingLookupValueName(setting
, ii
);
3244 sbufWriteDataSafe(dst
, name
, strlen(name
) + 1);
3248 // Finally, include the setting value. This way resource constrained callers
3249 // (e.g. a script in the radio) don't need to perform another call to retrieve
3251 const void *ptr
= settingGetValuePointer(setting
);
3252 size_t size
= settingGetValueSize(setting
);
3253 sbufWriteDataSafe(dst
, ptr
, size
);
3258 static bool mspParameterGroupsCommand(sbuf_t
*dst
, sbuf_t
*src
)
3265 if (sbufReadU16Safe(&first
, src
)) {
3268 first
= PG_ID_FIRST
;
3272 for (int ii
= first
; ii
<= last
; ii
++) {
3273 if (settingsGetParameterGroupIndexes(ii
, &start
, &end
)) {
3274 sbufWriteU16(dst
, ii
);
3275 sbufWriteU16(dst
, start
);
3276 sbufWriteU16(dst
, end
);
3282 #ifdef USE_SIMULATOR
3283 bool isOSDTypeSupportedBySimulator(void)
3286 displayPort_t
*osdDisplayPort
= osdGetDisplayPort();
3287 return (osdDisplayPort
&& osdDisplayPort
->cols
== 30 && (osdDisplayPort
->rows
== 13 || osdDisplayPort
->rows
== 16));
3293 void mspWriteSimulatorOSD(sbuf_t
*dst
)
3296 //scan displayBuffer iteratively
3297 //no more than 80+3+2 bytes output in single run
3298 //0 and 255 are special symbols
3299 //255 - font bank switch
3300 //0 - font bank switch, blink switch and character repeat
3302 static uint8_t osdPos_y
= 0;
3303 static uint8_t osdPos_x
= 0;
3306 if (isOSDTypeSupportedBySimulator())
3308 displayPort_t
*osdDisplayPort
= osdGetDisplayPort();
3310 sbufWriteU8(dst
, osdPos_y
| (osdDisplayPort
->rows
== 16 ? 128: 0));
3311 sbufWriteU8(dst
, osdPos_x
);
3316 textAttributes_t attr
= 0;
3317 bool highBank
= false;
3321 int processedRows
= 16;
3323 while (bytesCount
< 80) //whole response should be less 155 bytes at worst.
3331 displayReadCharWithAttr(osdDisplayPort
, osdPos_x
, osdPos_y
, &c
, &attr
);
3332 if (c
== 0 || c
== 255) c
= 32;
3334 //REVIEW: displayReadCharWithAttr() should return mode with _TEXT_ATTRIBUTES_BLINK_BIT !
3335 //for max7456 it returns mode with MAX7456_MODE_BLINK instead (wrong)
3336 //because max7456ReadChar() does not decode from MAX7456_MODE_BLINK to _TEXT_ATTRIBUTES_BLINK_BIT
3339 //bool blink2 = TEXT_ATTRIBUTES_HAVE_BLINK(attr);
3340 bool blink2
= attr
& (1<<4); //MAX7456_MODE_BLINK
3347 else if (lastChar
!= c
|| blink2
!= blink1
|| count
== 63)
3368 if (blink1
!= blink
)
3370 cmd
|= 128;//switch blink attr
3374 bool highBank1
= lastChar
> 255;
3375 if (highBank1
!= highBank
)
3377 cmd
|= 64;//switch bank attr
3378 highBank
= highBank1
;
3381 if (count
== 1 && cmd
== 64)
3383 sbufWriteU8(dst
, 255); //short command for bank switch
3384 sbufWriteU8(dst
, lastChar
& 0xff);
3387 else if (count
> 2 || cmd
!=0 )
3389 cmd
|= count
; //long command for blink/bank switch and symbol repeat
3390 sbufWriteU8(dst
, 0);
3391 sbufWriteU8(dst
, cmd
);
3392 sbufWriteU8(dst
, lastChar
& 0xff);
3395 else if (count
== 2) //cmd == 0 here
3397 sbufWriteU8(dst
, lastChar
& 0xff);
3398 sbufWriteU8(dst
, lastChar
& 0xff);
3403 sbufWriteU8(dst
, lastChar
& 0xff);
3407 if ( processedRows
<= 0 )
3412 sbufWriteU8(dst
, 0); //command 0 with length=0 -> stop
3413 sbufWriteU8(dst
, 0);
3417 sbufWriteU8(dst
, 255);
3422 bool mspFCProcessInOutCommand(uint16_t cmdMSP
, sbuf_t
*dst
, sbuf_t
*src
, mspResult_e
*ret
)
3425 const unsigned int dataSize
= sbufBytesRemaining(src
);
3430 mspFcWaypointOutCommand(dst
, src
);
3431 *ret
= MSP_RESULT_ACK
;
3434 #if defined(USE_FLASHFS)
3435 case MSP_DATAFLASH_READ
:
3436 mspFcDataFlashReadCommand(dst
, src
);
3437 *ret
= MSP_RESULT_ACK
;
3441 case MSP2_COMMON_SETTING
:
3442 *ret
= mspSettingCommand(dst
, src
) ? MSP_RESULT_ACK
: MSP_RESULT_ERROR
;
3445 case MSP2_COMMON_SET_SETTING
:
3446 *ret
= mspSetSettingCommand(dst
, src
) ? MSP_RESULT_ACK
: MSP_RESULT_ERROR
;
3449 case MSP2_COMMON_SETTING_INFO
:
3450 *ret
= mspSettingInfoCommand(dst
, src
) ? MSP_RESULT_ACK
: MSP_RESULT_ERROR
;
3453 case MSP2_COMMON_PG_LIST
:
3454 *ret
= mspParameterGroupsCommand(dst
, src
) ? MSP_RESULT_ACK
: MSP_RESULT_ERROR
;
3457 #if defined(USE_OSD)
3458 case MSP2_INAV_OSD_LAYOUTS
:
3459 if (sbufBytesRemaining(src
) >= 1) {
3460 uint8_t layout
= sbufReadU8(src
);
3461 if (layout
>= OSD_LAYOUT_COUNT
) {
3462 *ret
= MSP_RESULT_ERROR
;
3465 if (sbufBytesRemaining(src
) >= 2) {
3466 // Asking for an specific item in a layout
3467 uint16_t item
= sbufReadU16(src
);
3468 if (item
>= OSD_ITEM_COUNT
) {
3469 *ret
= MSP_RESULT_ERROR
;
3472 sbufWriteU16(dst
, osdLayoutsConfig()->item_pos
[layout
][item
]);
3474 // Asking for an specific layout
3475 for (unsigned ii
= 0; ii
< OSD_ITEM_COUNT
; ii
++) {
3476 sbufWriteU16(dst
, osdLayoutsConfig()->item_pos
[layout
][ii
]);
3480 // Return the number of layouts and items
3481 sbufWriteU8(dst
, OSD_LAYOUT_COUNT
);
3482 sbufWriteU8(dst
, OSD_ITEM_COUNT
);
3484 *ret
= MSP_RESULT_ACK
;
3488 #ifdef USE_PROGRAMMING_FRAMEWORK
3489 case MSP2_INAV_LOGIC_CONDITIONS_SINGLE
:
3490 *ret
= mspFcLogicConditionCommand(dst
, src
);
3493 #ifdef USE_SAFE_HOME
3494 case MSP2_INAV_SAFEHOME
:
3495 *ret
= mspFcSafeHomeOutCommand(dst
, src
);
3499 #ifdef USE_SIMULATOR
3501 tmp_u8
= sbufReadU8(src
); // Get the Simulator MSP version
3503 // Check the MSP version of simulator
3504 if (tmp_u8
!= SIMULATOR_MSP_VERSION
) {
3508 simulatorData
.flags
= sbufReadU8(src
);
3510 if (!SIMULATOR_HAS_OPTION(HITL_ENABLE
)) {
3512 if (ARMING_FLAG(SIMULATOR_MODE_HITL
)) { // Just once
3513 DISABLE_ARMING_FLAG(SIMULATOR_MODE_HITL
);
3516 if ( requestedSensors
[SENSOR_INDEX_BARO
] != BARO_NONE
) {
3517 baroStartCalibration();
3521 DISABLE_STATE(COMPASS_CALIBRATED
);
3524 simulatorData
.flags
= HITL_RESET_FLAGS
;
3525 // Review: Many states were affected. Reboot?
3527 disarm(DISARM_SWITCH
); // Disarm to prevent motor output!!!
3530 if (!ARMING_FLAG(SIMULATOR_MODE_HITL
)) { // Just once
3532 if ( requestedSensors
[SENSOR_INDEX_BARO
] != BARO_NONE
) {
3533 sensorsSet(SENSOR_BARO
);
3534 setTaskEnabled(TASK_BARO
, true);
3535 DISABLE_ARMING_FLAG(ARMING_DISABLED_HARDWARE_FAILURE
);
3536 baroStartCalibration();
3541 if (compassConfig()->mag_hardware
!= MAG_NONE
) {
3542 sensorsSet(SENSOR_MAG
);
3543 ENABLE_STATE(COMPASS_CALIBRATED
);
3544 DISABLE_ARMING_FLAG(ARMING_DISABLED_HARDWARE_FAILURE
);
3545 mag
.magADC
[X
] = 800;
3550 ENABLE_ARMING_FLAG(SIMULATOR_MODE_HITL
);
3551 LOG_DEBUG(SYSTEM
, "Simulator enabled");
3554 if (dataSize
>= 14) {
3556 if (feature(FEATURE_GPS
) && SIMULATOR_HAS_OPTION(HITL_HAS_NEW_GPS_DATA
)) {
3557 gpsSol
.fixType
= sbufReadU8(src
);
3558 gpsSol
.hdop
= gpsSol
.fixType
== GPS_NO_FIX
? 9999 : 100;
3559 gpsSol
.flags
.hasNewData
= true;
3560 gpsSol
.numSat
= sbufReadU8(src
);
3562 if (gpsSol
.fixType
!= GPS_NO_FIX
) {
3563 gpsSol
.flags
.validVelNE
= true;
3564 gpsSol
.flags
.validVelD
= true;
3565 gpsSol
.flags
.validEPE
= true;
3566 gpsSol
.flags
.validTime
= false;
3568 gpsSol
.llh
.lat
= sbufReadU32(src
);
3569 gpsSol
.llh
.lon
= sbufReadU32(src
);
3570 gpsSol
.llh
.alt
= sbufReadU32(src
);
3571 gpsSol
.groundSpeed
= (int16_t)sbufReadU16(src
);
3572 gpsSol
.groundCourse
= (int16_t)sbufReadU16(src
);
3574 gpsSol
.velNED
[X
] = (int16_t)sbufReadU16(src
);
3575 gpsSol
.velNED
[Y
] = (int16_t)sbufReadU16(src
);
3576 gpsSol
.velNED
[Z
] = (int16_t)sbufReadU16(src
);
3581 ENABLE_STATE(GPS_FIX
);
3583 sbufAdvance(src
, sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint16_t) * 3);
3585 // Feed data to navigation
3586 gpsProcessNewSolutionData();
3588 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);
3591 if (!SIMULATOR_HAS_OPTION(HITL_USE_IMU
)) {
3592 attitude
.values
.roll
= (int16_t)sbufReadU16(src
);
3593 attitude
.values
.pitch
= (int16_t)sbufReadU16(src
);
3594 attitude
.values
.yaw
= (int16_t)sbufReadU16(src
);
3596 sbufAdvance(src
, sizeof(uint16_t) * XYZ_AXIS_COUNT
);
3599 // Get the acceleration in 1G units
3600 acc
.accADCf
[X
] = ((int16_t)sbufReadU16(src
)) / 1000.0f
;
3601 acc
.accADCf
[Y
] = ((int16_t)sbufReadU16(src
)) / 1000.0f
;
3602 acc
.accADCf
[Z
] = ((int16_t)sbufReadU16(src
)) / 1000.0f
;
3603 acc
.accVibeSq
[X
] = 0.0f
;
3604 acc
.accVibeSq
[Y
] = 0.0f
;
3605 acc
.accVibeSq
[Z
] = 0.0f
;
3607 // Get the angular velocity in DPS
3608 gyro
.gyroADCf
[X
] = ((int16_t)sbufReadU16(src
)) / 16.0f
;
3609 gyro
.gyroADCf
[Y
] = ((int16_t)sbufReadU16(src
)) / 16.0f
;
3610 gyro
.gyroADCf
[Z
] = ((int16_t)sbufReadU16(src
)) / 16.0f
;
3612 if (sensors(SENSOR_BARO
)) {
3613 baro
.baroPressure
= (int32_t)sbufReadU32(src
);
3614 baro
.baroTemperature
= DEGREES_TO_CENTIDEGREES(SIMULATOR_BARO_TEMP
);
3616 sbufAdvance(src
, sizeof(uint32_t));
3619 if (sensors(SENSOR_MAG
)) {
3620 mag
.magADC
[X
] = ((int16_t)sbufReadU16(src
)) / 20; // 16000 / 20 = 800uT
3621 mag
.magADC
[Y
] = ((int16_t)sbufReadU16(src
)) / 20; //note that mag failure is simulated by setting all readings to zero
3622 mag
.magADC
[Z
] = ((int16_t)sbufReadU16(src
)) / 20;
3624 sbufAdvance(src
, sizeof(uint16_t) * XYZ_AXIS_COUNT
);
3627 #if defined(USE_FAKE_BATT_SENSOR)
3628 if (SIMULATOR_HAS_OPTION(HITL_EXT_BATTERY_VOLTAGE
)) {
3629 fakeBattSensorSetVbat(sbufReadU8(src
) * 10);
3632 fakeBattSensorSetVbat((uint16_t)(SIMULATOR_FULL_BATTERY
* 10.0f
));
3633 #if defined(USE_FAKE_BATT_SENSOR)
3637 if (SIMULATOR_HAS_OPTION(HITL_AIRSPEED
)) {
3638 simulatorData
.airSpeed
= sbufReadU16(src
);
3640 if (SIMULATOR_HAS_OPTION(HITL_EXTENDED_FLAGS
)) {
3645 if (SIMULATOR_HAS_OPTION(HITL_EXTENDED_FLAGS
)) {
3646 simulatorData
.flags
|= ((uint16_t)sbufReadU8(src
)) << 8;
3649 DISABLE_STATE(GPS_FIX
);
3653 sbufWriteU16(dst
, (uint16_t)simulatorData
.input
[INPUT_STABILIZED_ROLL
]);
3654 sbufWriteU16(dst
, (uint16_t)simulatorData
.input
[INPUT_STABILIZED_PITCH
]);
3655 sbufWriteU16(dst
, (uint16_t)simulatorData
.input
[INPUT_STABILIZED_YAW
]);
3656 sbufWriteU16(dst
, (uint16_t)(ARMING_FLAG(ARMED
) ? simulatorData
.input
[INPUT_STABILIZED_THROTTLE
] : -500));
3658 simulatorData
.debugIndex
++;
3659 if (simulatorData
.debugIndex
== 8) {
3660 simulatorData
.debugIndex
= 0;
3663 tmp_u8
= simulatorData
.debugIndex
|
3664 ((mixerConfig()->platformType
== PLATFORM_AIRPLANE
) ? 128 : 0) |
3665 (ARMING_FLAG(ARMED
) ? 64 : 0) |
3666 (!feature(FEATURE_OSD
) ? 32: 0) |
3667 (!isOSDTypeSupportedBySimulator() ? 16 : 0);
3669 sbufWriteU8(dst
, tmp_u8
);
3670 sbufWriteU32(dst
, debug
[simulatorData
.debugIndex
]);
3672 sbufWriteU16(dst
, attitude
.values
.roll
);
3673 sbufWriteU16(dst
, attitude
.values
.pitch
);
3674 sbufWriteU16(dst
, attitude
.values
.yaw
);
3676 mspWriteSimulatorOSD(dst
);
3678 *ret
= MSP_RESULT_ACK
;
3682 case MSP2_INAV_TIMER_OUTPUT_MODE
:
3683 if (dataSize
== 0) {
3684 for (int i
= 0; i
< HARDWARE_TIMER_DEFINITION_COUNT
; ++i
) {
3685 sbufWriteU8(dst
, i
);
3686 sbufWriteU8(dst
, timerOverrides(i
)->outputMode
);
3688 *ret
= MSP_RESULT_ACK
;
3689 } else if(dataSize
== 1) {
3690 uint8_t timer
= sbufReadU8(src
);
3691 if(timer
< HARDWARE_TIMER_DEFINITION_COUNT
) {
3692 sbufWriteU8(dst
, timer
);
3693 sbufWriteU8(dst
, timerOverrides(timer
)->outputMode
);
3694 *ret
= MSP_RESULT_ACK
;
3696 *ret
= MSP_RESULT_ERROR
;
3699 *ret
= MSP_RESULT_ERROR
;
3702 case MSP2_INAV_SET_TIMER_OUTPUT_MODE
:
3704 uint8_t timer
= sbufReadU8(src
);
3705 uint8_t outputMode
= sbufReadU8(src
);
3706 if(timer
< HARDWARE_TIMER_DEFINITION_COUNT
) {
3707 timerOverridesMutable(timer
)->outputMode
= outputMode
;
3708 *ret
= MSP_RESULT_ACK
;
3710 *ret
= MSP_RESULT_ERROR
;
3713 *ret
= MSP_RESULT_ERROR
;
3725 static mspResult_e
mspProcessSensorCommand(uint16_t cmdMSP
, sbuf_t
*src
)
3730 #if defined(USE_RANGEFINDER_MSP)
3731 case MSP2_SENSOR_RANGEFINDER
:
3732 mspRangefinderReceiveNewData(sbufPtr(src
));
3736 #if defined(USE_OPFLOW_MSP)
3737 case MSP2_SENSOR_OPTIC_FLOW
:
3738 mspOpflowReceiveNewData(sbufPtr(src
));
3742 #if defined(USE_GPS_PROTO_MSP)
3743 case MSP2_SENSOR_GPS
:
3744 mspGPSReceiveNewData(sbufPtr(src
));
3748 #if defined(USE_MAG_MSP)
3749 case MSP2_SENSOR_COMPASS
:
3750 mspMagReceiveNewData(sbufPtr(src
));
3754 #if defined(USE_BARO_MSP)
3755 case MSP2_SENSOR_BAROMETER
:
3756 mspBaroReceiveNewData(sbufPtr(src
));
3760 #if defined(USE_PITOT_MSP)
3761 case MSP2_SENSOR_AIRSPEED
:
3762 mspPitotmeterReceiveNewData(sbufPtr(src
));
3767 return MSP_RESULT_NO_REPLY
;
3771 * Returns MSP_RESULT_ACK, MSP_RESULT_ERROR or MSP_RESULT_NO_REPLY
3773 mspResult_e
mspFcProcessCommand(mspPacket_t
*cmd
, mspPacket_t
*reply
, mspPostProcessFnPtr
*mspPostProcessFn
)
3775 mspResult_e ret
= MSP_RESULT_ACK
;
3776 sbuf_t
*dst
= &reply
->buf
;
3777 sbuf_t
*src
= &cmd
->buf
;
3778 const uint16_t cmdMSP
= cmd
->cmd
;
3779 // initialize reply by default
3780 reply
->cmd
= cmd
->cmd
;
3782 if (MSP2_IS_SENSOR_MESSAGE(cmdMSP
)) {
3783 ret
= mspProcessSensorCommand(cmdMSP
, src
);
3784 } else if (mspFcProcessOutCommand(cmdMSP
, dst
, mspPostProcessFn
)) {
3785 ret
= MSP_RESULT_ACK
;
3786 } else if (cmdMSP
== MSP_SET_PASSTHROUGH
) {
3787 mspFcSetPassthroughCommand(dst
, src
, mspPostProcessFn
);
3788 ret
= MSP_RESULT_ACK
;
3790 if (!mspFCProcessInOutCommand(cmdMSP
, dst
, src
, &ret
)) {
3791 ret
= mspFcProcessInCommand(cmdMSP
, src
);
3795 // Process DONT_REPLY flag
3796 if (cmd
->flags
& MSP_FLAG_DONT_REPLY
) {
3797 ret
= MSP_RESULT_NO_REPLY
;
3800 reply
->result
= ret
;
3805 * Return a pointer to the process command function
3807 void mspFcInit(void)