2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
25 #include "blackbox/blackbox.h"
27 #include "build/build_config.h"
28 #include "build/debug.h"
29 #include "build/version.h"
31 #include "common/axis.h"
32 #include "common/color.h"
33 #include "common/maths.h"
34 #include "common/streambuf.h"
36 #include "config/config_master.h"
37 #include "config/config_eeprom.h"
38 #include "config/config_profile.h"
39 #include "config/feature.h"
40 #include "config/parameter_group.h"
41 #include "config/parameter_group_ids.h"
43 #include "drivers/accgyro.h"
44 #include "drivers/bus_i2c.h"
45 #include "drivers/compass.h"
46 #include "drivers/flash.h"
47 #include "drivers/io.h"
48 #include "drivers/max7456.h"
49 #include "drivers/pwm_output.h"
50 #include "drivers/sdcard.h"
51 #include "drivers/serial.h"
52 #include "drivers/serial_escserial.h"
53 #include "drivers/system.h"
54 #include "drivers/vcd.h"
55 #include "drivers/vtx_common.h"
56 #include "drivers/vtx_soft_spi_rtc6705.h"
58 #include "fc/config.h"
59 #include "fc/fc_core.h"
60 #include "fc/fc_msp.h"
62 #include "fc/rc_adjustments.h"
63 #include "fc/rc_controls.h"
64 #include "fc/runtime_config.h"
66 #include "flight/altitudehold.h"
67 #include "flight/failsafe.h"
68 #include "flight/imu.h"
69 #include "flight/mixer.h"
70 #include "flight/navigation.h"
71 #include "flight/pid.h"
72 #include "flight/servos.h"
74 #include "io/asyncfatfs/asyncfatfs.h"
75 #include "io/beeper.h"
76 #include "io/flashfs.h"
77 #include "io/gimbal.h"
79 #include "io/ledstrip.h"
80 #include "io/motors.h"
82 #include "io/serial.h"
83 #include "io/serial_4way.h"
84 #include "io/servos.h"
85 #include "io/transponder_ir.h"
88 #include "msp/msp_protocol.h"
89 #include "msp/msp_serial.h"
94 #include "scheduler/scheduler.h"
96 #include "sensors/acceleration.h"
97 #include "sensors/barometer.h"
98 #include "sensors/battery.h"
99 #include "sensors/boardalignment.h"
100 #include "sensors/compass.h"
101 #include "sensors/gyro.h"
102 #include "sensors/sensors.h"
103 #include "sensors/sonar.h"
105 #include "telemetry/telemetry.h"
107 #ifdef USE_HARDWARE_REVISION_DETECTION
108 #include "hardware_revision.h"
111 extern uint16_t cycleTime
; // FIXME dependency on mw.c
113 static const char * const flightControllerIdentifier
= CLEANFLIGHT_IDENTIFIER
; // 4 UPPER CASE alpha numeric characters that identify the flight controller.
114 static const char * const boardIdentifier
= TARGET_BOARD_IDENTIFIER
;
116 typedef struct box_e
{
117 const uint8_t boxId
; // see boxId_e
118 const char *boxName
; // GUI-readable box name
119 const uint8_t permanentId
; //
123 static const box_t boxes
[CHECKBOX_ITEM_COUNT
+ 1] = {
124 { BOXARM
, "ARM;", 0 },
125 { BOXANGLE
, "ANGLE;", 1 },
126 { BOXHORIZON
, "HORIZON;", 2 },
127 { BOXBARO
, "BARO;", 3 },
128 //{ BOXVARIO, "VARIO;", 4 },
129 { BOXMAG
, "MAG;", 5 },
130 { BOXHEADFREE
, "HEADFREE;", 6 },
131 { BOXHEADADJ
, "HEADADJ;", 7 },
132 { BOXCAMSTAB
, "CAMSTAB;", 8 },
133 { BOXCAMTRIG
, "CAMTRIG;", 9 },
134 { BOXGPSHOME
, "GPS HOME;", 10 },
135 { BOXGPSHOLD
, "GPS HOLD;", 11 },
136 { BOXPASSTHRU
, "PASSTHRU;", 12 },
137 { BOXBEEPERON
, "BEEPER;", 13 },
138 { BOXLEDMAX
, "LEDMAX;", 14 },
139 { BOXLEDLOW
, "LEDLOW;", 15 },
140 { BOXLLIGHTS
, "LLIGHTS;", 16 },
141 { BOXCALIB
, "CALIB;", 17 },
142 { BOXGOV
, "GOVERNOR;", 18 },
143 { BOXOSD
, "OSD SW;", 19 },
144 { BOXTELEMETRY
, "TELEMETRY;", 20 },
145 { BOXGTUNE
, "GTUNE;", 21 },
146 { BOXSONAR
, "SONAR;", 22 },
147 { BOXSERVO1
, "SERVO1;", 23 },
148 { BOXSERVO2
, "SERVO2;", 24 },
149 { BOXSERVO3
, "SERVO3;", 25 },
150 { BOXBLACKBOX
, "BLACKBOX;", 26 },
151 { BOXFAILSAFE
, "FAILSAFE;", 27 },
152 { BOXAIRMODE
, "AIR MODE;", 28 },
153 { BOX3DDISABLESWITCH
, "DISABLE 3D SWITCH;", 29},
154 { BOXFPVANGLEMIX
, "FPV ANGLE MIX;", 30},
155 { BOXBLACKBOXERASE
, "BLACKBOX ERASE (>30s);", 31 },
156 { CHECKBOX_ITEM_COUNT
, NULL
, 0xFF }
159 // this is calculated at startup based on enabled features.
160 static uint8_t activeBoxIds
[CHECKBOX_ITEM_COUNT
];
161 // this is the number of filled indexes in above array
162 static uint8_t activeBoxIdCount
= 0;
164 static const char pidnames
[] =
177 MSP_SDCARD_STATE_NOT_PRESENT
= 0,
178 MSP_SDCARD_STATE_FATAL
= 1,
179 MSP_SDCARD_STATE_CARD_INIT
= 2,
180 MSP_SDCARD_STATE_FS_INIT
= 3,
181 MSP_SDCARD_STATE_READY
= 4
185 MSP_SDCARD_FLAG_SUPPORTTED
= 1
188 #define RATEPROFILE_MASK (1 << 7)
190 #ifdef USE_SERIAL_4WAY_BLHELI_INTERFACE
191 #define ESC_4WAY 0xff
194 uint8_t escPortIndex
= 0;
197 static void mspEscPassthroughFn(serialPort_t
*serialPort
)
199 escEnablePassthrough(serialPort
, escPortIndex
, escMode
);
203 static void mspFc4waySerialCommand(sbuf_t
*dst
, sbuf_t
*src
, mspPostProcessFnPtr
*mspPostProcessFn
)
205 const unsigned int dataSize
= sbufBytesRemaining(src
);
211 escMode
= sbufReadU8(src
);
212 escPortIndex
= sbufReadU8(src
);
217 // get channel number
218 // switch all motor lines HI
219 // reply with the count of ESC found
220 sbufWriteU8(dst
, esc4wayInit());
222 if (mspPostProcessFn
) {
223 *mspPostProcessFn
= esc4wayProcess
;
228 case PROTOCOL_SIMONK
:
229 case PROTOCOL_BLHELI
:
231 case PROTOCOL_KISSALL
:
232 case PROTOCOL_CASTLE
:
233 if (escPortIndex
< USABLE_TIMER_CHANNEL_COUNT
|| (escMode
== PROTOCOL_KISS
&& escPortIndex
== 255)) {
236 if (mspPostProcessFn
) {
237 *mspPostProcessFn
= mspEscPassthroughFn
;
249 static void mspRebootFn(serialPort_t
*serialPort
)
256 // control should never return here.
260 static const box_t
*findBoxByActiveBoxId(uint8_t activeBoxId
)
262 for (uint8_t boxIndex
= 0; boxIndex
< sizeof(boxes
) / sizeof(box_t
); boxIndex
++) {
263 const box_t
*candidate
= &boxes
[boxIndex
];
264 if (candidate
->boxId
== activeBoxId
) {
271 static const box_t
*findBoxByPermenantId(uint8_t permenantId
)
273 for (uint8_t boxIndex
= 0; boxIndex
< sizeof(boxes
) / sizeof(box_t
); boxIndex
++) {
274 const box_t
*candidate
= &boxes
[boxIndex
];
275 if (candidate
->permanentId
== permenantId
) {
282 static void serializeBoxNamesReply(sbuf_t
*dst
)
284 int flag
= 1, count
= 0;
287 // in first run of the loop, we grab total size of junk to be sent
288 // then come back and actually send it
289 for (int i
= 0; i
< activeBoxIdCount
; i
++) {
290 const int activeBoxId
= activeBoxIds
[i
];
291 const box_t
*box
= findBoxByActiveBoxId(activeBoxId
);
296 const int len
= strlen(box
->boxName
);
300 for (int j
= 0; j
< len
; j
++) {
301 sbufWriteU8(dst
, box
->boxName
[j
]);
312 void initActiveBoxIds(void)
314 // calculate used boxes based on features and fill availableBoxes[] array
315 memset(activeBoxIds
, 0xFF, sizeof(activeBoxIds
));
317 activeBoxIdCount
= 0;
318 activeBoxIds
[activeBoxIdCount
++] = BOXARM
;
320 if (!feature(FEATURE_AIRMODE
)) {
321 activeBoxIds
[activeBoxIdCount
++] = BOXAIRMODE
;
324 if (sensors(SENSOR_ACC
)) {
325 activeBoxIds
[activeBoxIdCount
++] = BOXANGLE
;
326 activeBoxIds
[activeBoxIdCount
++] = BOXHORIZON
;
327 activeBoxIds
[activeBoxIdCount
++] = BOXHEADFREE
;
331 if (sensors(SENSOR_BARO
)) {
332 activeBoxIds
[activeBoxIdCount
++] = BOXBARO
;
337 if (sensors(SENSOR_MAG
)) {
338 activeBoxIds
[activeBoxIdCount
++] = BOXMAG
;
339 activeBoxIds
[activeBoxIdCount
++] = BOXHEADADJ
;
344 if (feature(FEATURE_GPS
)) {
345 activeBoxIds
[activeBoxIdCount
++] = BOXGPSHOME
;
346 activeBoxIds
[activeBoxIdCount
++] = BOXGPSHOLD
;
351 if (feature(FEATURE_SONAR
)) {
352 activeBoxIds
[activeBoxIdCount
++] = BOXSONAR
;
356 if (feature(FEATURE_FAILSAFE
)) {
357 activeBoxIds
[activeBoxIdCount
++] = BOXFAILSAFE
;
360 if (mixerConfig()->mixerMode
== MIXER_FLYING_WING
|| mixerConfig()->mixerMode
== MIXER_AIRPLANE
) {
361 activeBoxIds
[activeBoxIdCount
++] = BOXPASSTHRU
;
364 activeBoxIds
[activeBoxIdCount
++] = BOXBEEPERON
;
367 if (feature(FEATURE_LED_STRIP
)) {
368 activeBoxIds
[activeBoxIdCount
++] = BOXLEDLOW
;
373 if (feature(FEATURE_BLACKBOX
)) {
374 activeBoxIds
[activeBoxIdCount
++] = BOXBLACKBOX
;
376 activeBoxIds
[activeBoxIdCount
++] = BOXBLACKBOXERASE
;
381 activeBoxIds
[activeBoxIdCount
++] = BOXFPVANGLEMIX
;
383 if (feature(FEATURE_3D
)) {
384 activeBoxIds
[activeBoxIdCount
++] = BOX3DDISABLESWITCH
;
387 if (feature(FEATURE_SERVO_TILT
)) {
388 activeBoxIds
[activeBoxIdCount
++] = BOXCAMSTAB
;
391 if (feature(FEATURE_INFLIGHT_ACC_CAL
)) {
392 activeBoxIds
[activeBoxIdCount
++] = BOXCALIB
;
395 activeBoxIds
[activeBoxIdCount
++] = BOXOSD
;
398 if (feature(FEATURE_TELEMETRY
) && telemetryConfig()->telemetry_switch
) {
399 activeBoxIds
[activeBoxIdCount
++] = BOXTELEMETRY
;
404 if (mixerConfig()->mixerMode
== MIXER_CUSTOM_AIRPLANE
) {
405 activeBoxIds
[activeBoxIdCount
++] = BOXSERVO1
;
406 activeBoxIds
[activeBoxIdCount
++] = BOXSERVO2
;
407 activeBoxIds
[activeBoxIdCount
++] = BOXSERVO3
;
412 #define IS_ENABLED(mask) (mask == 0 ? 0 : 1)
414 static uint32_t packFlightModeFlags(void)
416 // Serialize the flags in the order we delivered them, ignoring BOXNAMES and BOXINDEXES
417 // Requires new Multiwii protocol version to fix
418 // It would be preferable to setting the enabled bits based on BOXINDEX.
419 const uint32_t tmp
= IS_ENABLED(FLIGHT_MODE(ANGLE_MODE
)) << BOXANGLE
|
420 IS_ENABLED(FLIGHT_MODE(HORIZON_MODE
)) << BOXHORIZON
|
421 IS_ENABLED(FLIGHT_MODE(BARO_MODE
)) << BOXBARO
|
422 IS_ENABLED(FLIGHT_MODE(MAG_MODE
)) << BOXMAG
|
423 IS_ENABLED(FLIGHT_MODE(HEADFREE_MODE
)) << BOXHEADFREE
|
424 IS_ENABLED(IS_RC_MODE_ACTIVE(BOXHEADADJ
)) << BOXHEADADJ
|
425 IS_ENABLED(IS_RC_MODE_ACTIVE(BOXCAMSTAB
)) << BOXCAMSTAB
|
426 IS_ENABLED(IS_RC_MODE_ACTIVE(BOXCAMTRIG
)) << BOXCAMTRIG
|
427 IS_ENABLED(FLIGHT_MODE(GPS_HOME_MODE
)) << BOXGPSHOME
|
428 IS_ENABLED(FLIGHT_MODE(GPS_HOLD_MODE
)) << BOXGPSHOLD
|
429 IS_ENABLED(FLIGHT_MODE(PASSTHRU_MODE
)) << BOXPASSTHRU
|
430 IS_ENABLED(IS_RC_MODE_ACTIVE(BOXBEEPERON
)) << BOXBEEPERON
|
431 IS_ENABLED(IS_RC_MODE_ACTIVE(BOXLEDMAX
)) << BOXLEDMAX
|
432 IS_ENABLED(IS_RC_MODE_ACTIVE(BOXLEDLOW
)) << BOXLEDLOW
|
433 IS_ENABLED(IS_RC_MODE_ACTIVE(BOXLLIGHTS
)) << BOXLLIGHTS
|
434 IS_ENABLED(IS_RC_MODE_ACTIVE(BOXCALIB
)) << BOXCALIB
|
435 IS_ENABLED(IS_RC_MODE_ACTIVE(BOXGOV
)) << BOXGOV
|
436 IS_ENABLED(IS_RC_MODE_ACTIVE(BOXOSD
)) << BOXOSD
|
437 IS_ENABLED(IS_RC_MODE_ACTIVE(BOXTELEMETRY
)) << BOXTELEMETRY
|
438 IS_ENABLED(IS_RC_MODE_ACTIVE(BOXGTUNE
)) << BOXGTUNE
|
439 IS_ENABLED(FLIGHT_MODE(SONAR_MODE
)) << BOXSONAR
|
440 IS_ENABLED(ARMING_FLAG(ARMED
)) << BOXARM
|
441 IS_ENABLED(IS_RC_MODE_ACTIVE(BOXBLACKBOX
)) << BOXBLACKBOX
|
442 IS_ENABLED(IS_RC_MODE_ACTIVE(BOXBLACKBOXERASE
)) << BOXBLACKBOXERASE
|
443 IS_ENABLED(FLIGHT_MODE(FAILSAFE_MODE
)) << BOXFAILSAFE
|
444 IS_ENABLED(IS_RC_MODE_ACTIVE(BOXAIRMODE
)) << BOXAIRMODE
|
445 IS_ENABLED(IS_RC_MODE_ACTIVE(BOXFPVANGLEMIX
)) << BOXFPVANGLEMIX
;
448 for (int i
= 0; i
< activeBoxIdCount
; i
++) {
449 const uint32_t flag
= (tmp
& (1 << activeBoxIds
[i
]));
457 static void serializeSDCardSummaryReply(sbuf_t
*dst
)
460 uint8_t flags
= MSP_SDCARD_FLAG_SUPPORTTED
;
463 sbufWriteU8(dst
, flags
);
465 // Merge the card and filesystem states together
466 if (!sdcard_isInserted()) {
467 state
= MSP_SDCARD_STATE_NOT_PRESENT
;
468 } else if (!sdcard_isFunctional()) {
469 state
= MSP_SDCARD_STATE_FATAL
;
471 switch (afatfs_getFilesystemState()) {
472 case AFATFS_FILESYSTEM_STATE_READY
:
473 state
= MSP_SDCARD_STATE_READY
;
476 case AFATFS_FILESYSTEM_STATE_INITIALIZATION
:
477 if (sdcard_isInitialized()) {
478 state
= MSP_SDCARD_STATE_FS_INIT
;
480 state
= MSP_SDCARD_STATE_CARD_INIT
;
484 case AFATFS_FILESYSTEM_STATE_FATAL
:
485 case AFATFS_FILESYSTEM_STATE_UNKNOWN
:
487 state
= MSP_SDCARD_STATE_FATAL
;
493 sbufWriteU8(dst
, state
);
494 sbufWriteU8(dst
, afatfs_getLastError());
495 // Write free space and total space in kilobytes
496 sbufWriteU32(dst
, afatfs_getContiguousFreeSpace() / 1024);
497 sbufWriteU32(dst
, sdcard_getMetadata()->numBlocks
/ 2); // Block size is half a kilobyte
502 sbufWriteU32(dst
, 0);
503 sbufWriteU32(dst
, 0);
507 static void serializeDataflashSummaryReply(sbuf_t
*dst
)
510 const flashGeometry_t
*geometry
= flashfsGetGeometry();
511 uint8_t flags
= (flashfsIsReady() ? 1 : 0) | 2 /* FlashFS is supported */;
513 sbufWriteU8(dst
, flags
);
514 sbufWriteU32(dst
, geometry
->sectors
);
515 sbufWriteU32(dst
, geometry
->totalSize
);
516 sbufWriteU32(dst
, flashfsGetOffset()); // Effectively the current number of bytes stored on the volume
518 sbufWriteU8(dst
, 0); // FlashFS is neither ready nor supported
519 sbufWriteU32(dst
, 0);
520 sbufWriteU32(dst
, 0);
521 sbufWriteU32(dst
, 0);
526 static void serializeDataflashReadReply(sbuf_t
*dst
, uint32_t address
, const uint16_t size
, bool useLegacyFormat
)
528 BUILD_BUG_ON(MSP_PORT_DATAFLASH_INFO_SIZE
< 16);
530 uint16_t readLen
= size
;
531 const int bytesRemainingInBuf
= sbufBytesRemaining(dst
) - MSP_PORT_DATAFLASH_INFO_SIZE
;
532 if (readLen
> bytesRemainingInBuf
) {
533 readLen
= bytesRemainingInBuf
;
535 // size will be lower than that requested if we reach end of volume
536 if (readLen
> flashfsGetSize() - address
) {
537 // truncate the request
538 readLen
= flashfsGetSize() - address
;
540 sbufWriteU32(dst
, address
);
541 if (!useLegacyFormat
) {
542 // new format supports variable read lengths
543 sbufWriteU16(dst
, readLen
);
544 sbufWriteU8(dst
, 0); // placeholder for compression format
547 // bytesRead will equal readLen
548 const int bytesRead
= flashfsReadAbs(address
, sbufPtr(dst
), readLen
);
549 sbufAdvance(dst
, bytesRead
);
551 if (useLegacyFormat
) {
552 // pad the buffer with zeros
553 for (int i
= bytesRead
; i
< size
; i
++) {
561 * Returns true if the command was processd, false otherwise.
562 * May set mspPostProcessFunc to a function to be called once the command has been processed
564 static bool mspFcProcessOutCommand(uint8_t cmdMSP
, sbuf_t
*dst
, mspPostProcessFnPtr
*mspPostProcessFn
)
567 case MSP_API_VERSION
:
568 sbufWriteU8(dst
, MSP_PROTOCOL_VERSION
);
569 sbufWriteU8(dst
, API_VERSION_MAJOR
);
570 sbufWriteU8(dst
, API_VERSION_MINOR
);
574 sbufWriteData(dst
, flightControllerIdentifier
, FLIGHT_CONTROLLER_IDENTIFIER_LENGTH
);
578 sbufWriteU8(dst
, FC_VERSION_MAJOR
);
579 sbufWriteU8(dst
, FC_VERSION_MINOR
);
580 sbufWriteU8(dst
, FC_VERSION_PATCH_LEVEL
);
584 sbufWriteData(dst
, boardIdentifier
, BOARD_IDENTIFIER_LENGTH
);
585 #ifdef USE_HARDWARE_REVISION_DETECTION
586 sbufWriteU16(dst
, hardwareRevision
);
588 sbufWriteU16(dst
, 0); // No other build targets currently have hardware revision detection.
593 sbufWriteData(dst
, buildDate
, BUILD_DATE_LENGTH
);
594 sbufWriteData(dst
, buildTime
, BUILD_TIME_LENGTH
);
595 sbufWriteData(dst
, shortGitRevision
, GIT_SHORT_REVISION_LENGTH
);
598 // DEPRECATED - Use MSP_API_VERSION
600 sbufWriteU8(dst
, MW_VERSION
);
601 sbufWriteU8(dst
, mixerConfig()->mixerMode
);
602 sbufWriteU8(dst
, MSP_PROTOCOL_VERSION
);
603 sbufWriteU32(dst
, CAP_DYNBALANCE
); // "capability"
607 sbufWriteU16(dst
, getTaskDeltaTime(TASK_GYROPID
));
609 sbufWriteU16(dst
, i2cGetErrorCounter());
611 sbufWriteU16(dst
, 0);
613 sbufWriteU16(dst
, sensors(SENSOR_ACC
) | sensors(SENSOR_BARO
) << 1 | sensors(SENSOR_MAG
) << 2 | sensors(SENSOR_GPS
) << 3 | sensors(SENSOR_SONAR
) << 4);
614 sbufWriteU32(dst
, packFlightModeFlags());
615 sbufWriteU8(dst
, getCurrentProfile());
616 sbufWriteU16(dst
, constrain(averageSystemLoadPercent
, 0, 100));
617 sbufWriteU8(dst
, MAX_PROFILE_COUNT
);
618 sbufWriteU8(dst
, getCurrentControlRateProfile());
623 const int nameLen
= strlen(systemConfig()->name
);
624 for (int i
= 0; i
< nameLen
; i
++) {
625 sbufWriteU8(dst
, systemConfig()->name
[i
]);
631 sbufWriteU16(dst
, getTaskDeltaTime(TASK_GYROPID
));
633 sbufWriteU16(dst
, i2cGetErrorCounter());
635 sbufWriteU16(dst
, 0);
637 sbufWriteU16(dst
, sensors(SENSOR_ACC
) | sensors(SENSOR_BARO
) << 1 | sensors(SENSOR_MAG
) << 2 | sensors(SENSOR_GPS
) << 3 | sensors(SENSOR_SONAR
) << 4);
638 sbufWriteU32(dst
, packFlightModeFlags());
639 sbufWriteU8(dst
, systemConfig()->current_profile_index
);
640 sbufWriteU16(dst
, constrain(averageSystemLoadPercent
, 0, 100));
641 sbufWriteU16(dst
, 0); // gyro cycle time
646 // Hack scale due to choice of units for sensor data in multiwii
647 const uint8_t scale
= (acc
.dev
.acc_1G
> 512) ? 4 : 1;
648 for (int i
= 0; i
< 3; i
++) {
649 sbufWriteU16(dst
, acc
.accSmooth
[i
] / scale
);
651 for (int i
= 0; i
< 3; i
++) {
652 sbufWriteU16(dst
, gyroRateDps(i
));
654 for (int i
= 0; i
< 3; i
++) {
655 sbufWriteU16(dst
, mag
.magADC
[i
]);
662 sbufWriteData(dst
, &servo
, MAX_SUPPORTED_SERVOS
* 2);
664 case MSP_SERVO_CONFIGURATIONS
:
665 for (int i
= 0; i
< MAX_SUPPORTED_SERVOS
; i
++) {
666 sbufWriteU16(dst
, servoParams(i
)->min
);
667 sbufWriteU16(dst
, servoParams(i
)->max
);
668 sbufWriteU16(dst
, servoParams(i
)->middle
);
669 sbufWriteU8(dst
, servoParams(i
)->rate
);
670 sbufWriteU8(dst
, servoParams(i
)->angleAtMin
);
671 sbufWriteU8(dst
, servoParams(i
)->angleAtMax
);
672 sbufWriteU8(dst
, servoParams(i
)->forwardFromChannel
);
673 sbufWriteU32(dst
, servoParams(i
)->reversedSources
);
676 case MSP_SERVO_MIX_RULES
:
677 for (int i
= 0; i
< MAX_SERVO_RULES
; i
++) {
678 sbufWriteU8(dst
, customServoMixers(i
)->targetChannel
);
679 sbufWriteU8(dst
, customServoMixers(i
)->inputSource
);
680 sbufWriteU8(dst
, customServoMixers(i
)->rate
);
681 sbufWriteU8(dst
, customServoMixers(i
)->speed
);
682 sbufWriteU8(dst
, customServoMixers(i
)->min
);
683 sbufWriteU8(dst
, customServoMixers(i
)->max
);
684 sbufWriteU8(dst
, customServoMixers(i
)->box
);
690 for (unsigned i
= 0; i
< 8; i
++) {
691 if (i
>= MAX_SUPPORTED_MOTORS
|| !pwmGetMotors()[i
].enabled
) {
692 sbufWriteU16(dst
, 0);
696 sbufWriteU16(dst
, convertMotorToExternal(motor
[i
]));
701 for (int i
= 0; i
< rxRuntimeConfig
.channelCount
; i
++) {
702 sbufWriteU16(dst
, rcData
[i
]);
707 sbufWriteU16(dst
, attitude
.values
.roll
);
708 sbufWriteU16(dst
, attitude
.values
.pitch
);
709 sbufWriteU16(dst
, DECIDEGREES_TO_DEGREES(attitude
.values
.yaw
));
713 #if defined(BARO) || defined(SONAR)
714 sbufWriteU32(dst
, altitudeHoldGetEstimatedAltitude());
716 sbufWriteU32(dst
, 0);
718 sbufWriteU16(dst
, vario
);
721 case MSP_SONAR_ALTITUDE
:
723 sbufWriteU32(dst
, sonarGetLatestAltitude());
725 sbufWriteU32(dst
, 0);
730 sbufWriteU8(dst
, (uint8_t)constrain(getVbat(), 0, 255));
731 sbufWriteU16(dst
, (uint16_t)constrain(mAhDrawn
, 0, 0xFFFF)); // milliamp hours drawn from battery
732 sbufWriteU16(dst
, rssi
);
733 if(batteryConfig()->multiwiiCurrentMeterOutput
) {
734 sbufWriteU16(dst
, (uint16_t)constrain(amperage
* 10, 0, 0xFFFF)); // send amperage in 0.001 A steps. Negative range is truncated to zero
736 sbufWriteU16(dst
, (int16_t)constrain(amperage
, -0x8000, 0x7FFF)); // send amperage in 0.01 A steps, range is -320A to 320A
739 case MSP_ARMING_CONFIG
:
740 sbufWriteU8(dst
, armingConfig()->auto_disarm_delay
);
741 sbufWriteU8(dst
, armingConfig()->disarm_kill_switch
);
745 sbufWriteU16(dst
, (uint16_t)gyro
.targetLooptime
);
749 sbufWriteU8(dst
, currentControlRateProfile
->rcRate8
);
750 sbufWriteU8(dst
, currentControlRateProfile
->rcExpo8
);
751 for (int i
= 0 ; i
< 3; i
++) {
752 sbufWriteU8(dst
, currentControlRateProfile
->rates
[i
]); // R,P,Y see flight_dynamics_index_t
754 sbufWriteU8(dst
, currentControlRateProfile
->dynThrPID
);
755 sbufWriteU8(dst
, currentControlRateProfile
->thrMid8
);
756 sbufWriteU8(dst
, currentControlRateProfile
->thrExpo8
);
757 sbufWriteU16(dst
, currentControlRateProfile
->tpa_breakpoint
);
758 sbufWriteU8(dst
, currentControlRateProfile
->rcYawExpo8
);
759 sbufWriteU8(dst
, currentControlRateProfile
->rcYawRate8
);
763 for (int i
= 0; i
< PID_ITEM_COUNT
; i
++) {
764 sbufWriteU8(dst
, currentProfile
->pidProfile
.P8
[i
]);
765 sbufWriteU8(dst
, currentProfile
->pidProfile
.I8
[i
]);
766 sbufWriteU8(dst
, currentProfile
->pidProfile
.D8
[i
]);
771 for (const char *c
= pidnames
; *c
; c
++) {
772 sbufWriteU8(dst
, *c
);
776 case MSP_PID_CONTROLLER
:
777 sbufWriteU8(dst
, PID_CONTROLLER_BETAFLIGHT
);
780 case MSP_MODE_RANGES
:
781 for (int i
= 0; i
< MAX_MODE_ACTIVATION_CONDITION_COUNT
; i
++) {
782 const modeActivationCondition_t
*mac
= modeActivationConditions(i
);
783 const box_t
*box
= &boxes
[mac
->modeId
];
784 sbufWriteU8(dst
, box
->permanentId
);
785 sbufWriteU8(dst
, mac
->auxChannelIndex
);
786 sbufWriteU8(dst
, mac
->range
.startStep
);
787 sbufWriteU8(dst
, mac
->range
.endStep
);
791 case MSP_ADJUSTMENT_RANGES
:
792 for (int i
= 0; i
< MAX_ADJUSTMENT_RANGE_COUNT
; i
++) {
793 const adjustmentRange_t
*adjRange
= adjustmentRanges(i
);
794 sbufWriteU8(dst
, adjRange
->adjustmentIndex
);
795 sbufWriteU8(dst
, adjRange
->auxChannelIndex
);
796 sbufWriteU8(dst
, adjRange
->range
.startStep
);
797 sbufWriteU8(dst
, adjRange
->range
.endStep
);
798 sbufWriteU8(dst
, adjRange
->adjustmentFunction
);
799 sbufWriteU8(dst
, adjRange
->auxSwitchChannelIndex
);
804 serializeBoxNamesReply(dst
);
808 for (int i
= 0; i
< activeBoxIdCount
; i
++) {
809 const box_t
*box
= findBoxByActiveBoxId(activeBoxIds
[i
]);
813 sbufWriteU8(dst
, box
->permanentId
);
818 sbufWriteU16(dst
, rxConfig()->midrc
);
820 sbufWriteU16(dst
, motorConfig()->minthrottle
);
821 sbufWriteU16(dst
, motorConfig()->maxthrottle
);
822 sbufWriteU16(dst
, motorConfig()->mincommand
);
824 sbufWriteU16(dst
, failsafeConfig()->failsafe_throttle
);
827 sbufWriteU8(dst
, gpsConfig()->provider
); // gps_type
828 sbufWriteU8(dst
, 0); // TODO gps_baudrate (an index, cleanflight uses a uint32_t
829 sbufWriteU8(dst
, gpsConfig()->sbasMode
); // gps_ubx_sbas
831 sbufWriteU8(dst
, 0); // gps_type
832 sbufWriteU8(dst
, 0); // TODO gps_baudrate (an index, cleanflight uses a uint32_t
833 sbufWriteU8(dst
, 0); // gps_ubx_sbas
835 sbufWriteU8(dst
, batteryConfig()->multiwiiCurrentMeterOutput
);
836 sbufWriteU8(dst
, rxConfig()->rssi_channel
);
839 sbufWriteU16(dst
, compassConfig()->mag_declination
/ 10);
841 sbufWriteU8(dst
, batteryConfig()->vbatscale
);
842 sbufWriteU8(dst
, batteryConfig()->vbatmincellvoltage
);
843 sbufWriteU8(dst
, batteryConfig()->vbatmaxcellvoltage
);
844 sbufWriteU8(dst
, batteryConfig()->vbatwarningcellvoltage
);
848 // FIXME This is hardcoded and should not be.
849 for (int i
= 0; i
< 8; i
++) {
850 sbufWriteU8(dst
, i
+ 1);
856 sbufWriteU8(dst
, STATE(GPS_FIX
));
857 sbufWriteU8(dst
, GPS_numSat
);
858 sbufWriteU32(dst
, GPS_coord
[LAT
]);
859 sbufWriteU32(dst
, GPS_coord
[LON
]);
860 sbufWriteU16(dst
, GPS_altitude
);
861 sbufWriteU16(dst
, GPS_speed
);
862 sbufWriteU16(dst
, GPS_ground_course
);
866 sbufWriteU16(dst
, GPS_distanceToHome
);
867 sbufWriteU16(dst
, GPS_directionToHome
);
868 sbufWriteU8(dst
, GPS_update
& 1);
872 sbufWriteU8(dst
, GPS_numCh
);
873 for (int i
= 0; i
< GPS_numCh
; i
++) {
874 sbufWriteU8(dst
, GPS_svinfo_chn
[i
]);
875 sbufWriteU8(dst
, GPS_svinfo_svid
[i
]);
876 sbufWriteU8(dst
, GPS_svinfo_quality
[i
]);
877 sbufWriteU8(dst
, GPS_svinfo_cno
[i
]);
883 // output some useful QA statistics
884 // debug[x] = ((hse_value / 1000000) * 1000) + (SystemCoreClock / 1000000); // XX0YY [crystal clock : core clock]
886 for (int i
= 0; i
< DEBUG16_VALUE_COUNT
; i
++) {
887 sbufWriteU16(dst
, debug
[i
]); // 4 variables are here for general monitoring purpose
891 // Additional commands that are not compatible with MultiWii
893 sbufWriteU16(dst
, accelerometerConfig()->accelerometerTrims
.values
.pitch
);
894 sbufWriteU16(dst
, accelerometerConfig()->accelerometerTrims
.values
.roll
);
898 sbufWriteU32(dst
, U_ID_0
);
899 sbufWriteU32(dst
, U_ID_1
);
900 sbufWriteU32(dst
, U_ID_2
);
904 sbufWriteU32(dst
, featureMask());
907 case MSP_BOARD_ALIGNMENT
:
908 sbufWriteU16(dst
, boardAlignment()->rollDegrees
);
909 sbufWriteU16(dst
, boardAlignment()->pitchDegrees
);
910 sbufWriteU16(dst
, boardAlignment()->yawDegrees
);
913 case MSP_VOLTAGE_METER_CONFIG
:
914 sbufWriteU8(dst
, batteryConfig()->vbatscale
);
915 sbufWriteU8(dst
, batteryConfig()->vbatmincellvoltage
);
916 sbufWriteU8(dst
, batteryConfig()->vbatmaxcellvoltage
);
917 sbufWriteU8(dst
, batteryConfig()->vbatwarningcellvoltage
);
918 sbufWriteU8(dst
, batteryConfig()->batteryMeterType
);
921 case MSP_CURRENT_METER_CONFIG
:
922 sbufWriteU16(dst
, batteryConfig()->currentMeterScale
);
923 sbufWriteU16(dst
, batteryConfig()->currentMeterOffset
);
924 sbufWriteU8(dst
, batteryConfig()->currentMeterType
);
925 sbufWriteU16(dst
, batteryConfig()->batteryCapacity
);
929 sbufWriteU8(dst
, mixerConfig()->mixerMode
);
933 sbufWriteU8(dst
, rxConfig()->serialrx_provider
);
934 sbufWriteU16(dst
, rxConfig()->maxcheck
);
935 sbufWriteU16(dst
, rxConfig()->midrc
);
936 sbufWriteU16(dst
, rxConfig()->mincheck
);
937 sbufWriteU8(dst
, rxConfig()->spektrum_sat_bind
);
938 sbufWriteU16(dst
, rxConfig()->rx_min_usec
);
939 sbufWriteU16(dst
, rxConfig()->rx_max_usec
);
940 sbufWriteU8(dst
, rxConfig()->rcInterpolation
);
941 sbufWriteU8(dst
, rxConfig()->rcInterpolationInterval
);
942 sbufWriteU16(dst
, rxConfig()->airModeActivateThreshold
);
943 sbufWriteU8(dst
, rxConfig()->rx_spi_protocol
);
944 sbufWriteU32(dst
, rxConfig()->rx_spi_id
);
945 sbufWriteU8(dst
, rxConfig()->rx_spi_rf_channel_count
);
946 sbufWriteU8(dst
, rxConfig()->fpvCamAngleDegrees
);
949 case MSP_FAILSAFE_CONFIG
:
950 sbufWriteU8(dst
, failsafeConfig()->failsafe_delay
);
951 sbufWriteU8(dst
, failsafeConfig()->failsafe_off_delay
);
952 sbufWriteU16(dst
, failsafeConfig()->failsafe_throttle
);
953 sbufWriteU8(dst
, failsafeConfig()->failsafe_kill_switch
);
954 sbufWriteU16(dst
, failsafeConfig()->failsafe_throttle_low_delay
);
955 sbufWriteU8(dst
, failsafeConfig()->failsafe_procedure
);
958 case MSP_RXFAIL_CONFIG
:
959 for (int i
= 0; i
< rxRuntimeConfig
.channelCount
; i
++) {
960 sbufWriteU8(dst
, rxFailsafeChannelConfigs(i
)->mode
);
961 sbufWriteU16(dst
, RXFAIL_STEP_TO_CHANNEL_VALUE(rxFailsafeChannelConfigs(i
)->step
));
965 case MSP_RSSI_CONFIG
:
966 sbufWriteU8(dst
, rxConfig()->rssi_channel
);
970 sbufWriteData(dst
, rxConfig()->rcmap
, MAX_MAPPABLE_RX_INPUTS
);
974 sbufWriteU8(dst
, mixerConfig()->mixerMode
);
976 sbufWriteU32(dst
, featureMask());
978 sbufWriteU8(dst
, rxConfig()->serialrx_provider
);
980 sbufWriteU16(dst
, boardAlignment()->rollDegrees
);
981 sbufWriteU16(dst
, boardAlignment()->pitchDegrees
);
982 sbufWriteU16(dst
, boardAlignment()->yawDegrees
);
984 sbufWriteU16(dst
, batteryConfig()->currentMeterScale
);
985 sbufWriteU16(dst
, batteryConfig()->currentMeterOffset
);
988 case MSP_CF_SERIAL_CONFIG
:
989 for (int i
= 0; i
< SERIAL_PORT_COUNT
; i
++) {
990 if (!serialIsPortAvailable(serialConfig()->portConfigs
[i
].identifier
)) {
993 sbufWriteU8(dst
, serialConfig()->portConfigs
[i
].identifier
);
994 sbufWriteU16(dst
, serialConfig()->portConfigs
[i
].functionMask
);
995 sbufWriteU8(dst
, serialConfig()->portConfigs
[i
].msp_baudrateIndex
);
996 sbufWriteU8(dst
, serialConfig()->portConfigs
[i
].gps_baudrateIndex
);
997 sbufWriteU8(dst
, serialConfig()->portConfigs
[i
].telemetry_baudrateIndex
);
998 sbufWriteU8(dst
, serialConfig()->portConfigs
[i
].blackbox_baudrateIndex
);
1003 case MSP_LED_COLORS
:
1004 for (int i
= 0; i
< LED_CONFIGURABLE_COLOR_COUNT
; i
++) {
1005 const hsvColor_t
*color
= &ledStripConfig()->colors
[i
];
1006 sbufWriteU16(dst
, color
->h
);
1007 sbufWriteU8(dst
, color
->s
);
1008 sbufWriteU8(dst
, color
->v
);
1012 case MSP_LED_STRIP_CONFIG
:
1013 for (int i
= 0; i
< LED_MAX_STRIP_LENGTH
; i
++) {
1014 const ledConfig_t
*ledConfig
= &ledStripConfig()->ledConfigs
[i
];
1015 sbufWriteU32(dst
, *ledConfig
);
1019 case MSP_LED_STRIP_MODECOLOR
:
1020 for (int i
= 0; i
< LED_MODE_COUNT
; i
++) {
1021 for (int j
= 0; j
< LED_DIRECTION_COUNT
; j
++) {
1022 sbufWriteU8(dst
, i
);
1023 sbufWriteU8(dst
, j
);
1024 sbufWriteU8(dst
, ledStripConfig()->modeColors
[i
].color
[j
]);
1028 for (int j
= 0; j
< LED_SPECIAL_COLOR_COUNT
; j
++) {
1029 sbufWriteU8(dst
, LED_MODE_COUNT
);
1030 sbufWriteU8(dst
, j
);
1031 sbufWriteU8(dst
, ledStripConfig()->specialColors
.color
[j
]);
1034 sbufWriteU8(dst
, LED_AUX_CHANNEL
);
1035 sbufWriteU8(dst
, 0);
1036 sbufWriteU8(dst
, ledStripConfig()->ledstrip_aux_channel
);
1040 case MSP_DATAFLASH_SUMMARY
:
1041 serializeDataflashSummaryReply(dst
);
1044 case MSP_BLACKBOX_CONFIG
:
1046 sbufWriteU8(dst
, 1); //Blackbox supported
1047 sbufWriteU8(dst
, blackboxConfig()->device
);
1048 sbufWriteU8(dst
, blackboxConfig()->rate_num
);
1049 sbufWriteU8(dst
, blackboxConfig()->rate_denom
);
1051 sbufWriteU8(dst
, 0); // Blackbox not supported
1052 sbufWriteU8(dst
, 0);
1053 sbufWriteU8(dst
, 0);
1054 sbufWriteU8(dst
, 0);
1058 case MSP_SDCARD_SUMMARY
:
1059 serializeSDCardSummaryReply(dst
);
1062 case MSP_TRANSPONDER_CONFIG
:
1064 sbufWriteU8(dst
, 1); //Transponder supported
1065 for (unsigned int i
= 0; i
< sizeof(transponderConfig()->data
); i
++) {
1066 sbufWriteU8(dst
, transponderConfig()->data
[i
]);
1069 sbufWriteU8(dst
, 0); // Transponder not supported
1073 case MSP_OSD_CONFIG
:
1075 sbufWriteU8(dst
, 1); // OSD supported
1076 // send video system (AUTO/PAL/NTSC)
1078 sbufWriteU8(dst
, vcdProfile()->video_system
);
1080 sbufWriteU8(dst
, 0);
1082 sbufWriteU8(dst
, osdConfig()->units
);
1083 sbufWriteU8(dst
, osdConfig()->rssi_alarm
);
1084 sbufWriteU16(dst
, osdConfig()->cap_alarm
);
1085 sbufWriteU16(dst
, osdConfig()->time_alarm
);
1086 sbufWriteU16(dst
, osdConfig()->alt_alarm
);
1087 for (int i
= 0; i
< OSD_ITEM_COUNT
; i
++) {
1088 sbufWriteU16(dst
, osdConfig()->item_pos
[i
]);
1091 sbufWriteU8(dst
, 0); // OSD not supported
1095 case MSP_BF_BUILD_INFO
:
1096 sbufWriteData(dst
, buildDate
, 11); // MMM DD YYYY as ascii, MMM = Jan/Feb... etc
1097 sbufWriteU32(dst
, 0); // future exp
1098 sbufWriteU32(dst
, 0); // future exp
1102 sbufWriteU16(dst
, flight3DConfig()->deadband3d_low
);
1103 sbufWriteU16(dst
, flight3DConfig()->deadband3d_high
);
1104 sbufWriteU16(dst
, flight3DConfig()->neutral3d
);
1107 case MSP_RC_DEADBAND
:
1108 sbufWriteU8(dst
, rcControlsConfig()->deadband
);
1109 sbufWriteU8(dst
, rcControlsConfig()->yaw_deadband
);
1110 sbufWriteU8(dst
, rcControlsConfig()->alt_hold_deadband
);
1111 sbufWriteU16(dst
, flight3DConfig()->deadband3d_throttle
);
1114 case MSP_SENSOR_ALIGNMENT
:
1115 sbufWriteU8(dst
, gyroConfig()->gyro_align
);
1116 sbufWriteU8(dst
, accelerometerConfig()->acc_align
);
1117 sbufWriteU8(dst
, compassConfig()->mag_align
);
1120 case MSP_ADVANCED_CONFIG
:
1121 if (gyroConfig()->gyro_lpf
) {
1122 sbufWriteU8(dst
, 8); // If gyro_lpf != OFF then looptime is set to 1000
1123 sbufWriteU8(dst
, 1);
1125 sbufWriteU8(dst
, gyroConfig()->gyro_sync_denom
);
1126 sbufWriteU8(dst
, pidConfig()->pid_process_denom
);
1128 sbufWriteU8(dst
, motorConfig()->dev
.useUnsyncedPwm
);
1129 sbufWriteU8(dst
, motorConfig()->dev
.motorPwmProtocol
);
1130 sbufWriteU16(dst
, motorConfig()->dev
.motorPwmRate
);
1131 sbufWriteU16(dst
, (uint16_t)lrintf(motorConfig()->digitalIdleOffsetPercent
* 100));
1132 sbufWriteU8(dst
, gyroConfig()->gyro_use_32khz
);
1133 //!!TODO gyro_isr_update to be added pending decision
1134 //sbufWriteU8(dst, gyroConfig()->gyro_isr_update);
1135 sbufWriteU8(dst
, motorConfig()->dev
.motorPwmInversion
);
1138 case MSP_FILTER_CONFIG
:
1139 sbufWriteU8(dst
, gyroConfig()->gyro_soft_lpf_hz
);
1140 sbufWriteU16(dst
, currentProfile
->pidProfile
.dterm_lpf_hz
);
1141 sbufWriteU16(dst
, currentProfile
->pidProfile
.yaw_lpf_hz
);
1142 sbufWriteU16(dst
, gyroConfig()->gyro_soft_notch_hz_1
);
1143 sbufWriteU16(dst
, gyroConfig()->gyro_soft_notch_cutoff_1
);
1144 sbufWriteU16(dst
, currentProfile
->pidProfile
.dterm_notch_hz
);
1145 sbufWriteU16(dst
, currentProfile
->pidProfile
.dterm_notch_cutoff
);
1146 sbufWriteU16(dst
, gyroConfig()->gyro_soft_notch_hz_2
);
1147 sbufWriteU16(dst
, gyroConfig()->gyro_soft_notch_cutoff_2
);
1150 case MSP_PID_ADVANCED
:
1151 sbufWriteU16(dst
, 0);
1152 sbufWriteU16(dst
, 0);
1153 sbufWriteU16(dst
, currentProfile
->pidProfile
.yaw_p_limit
);
1154 sbufWriteU8(dst
, 0); // reserved
1155 sbufWriteU8(dst
, currentProfile
->pidProfile
.vbatPidCompensation
);
1156 sbufWriteU8(dst
, currentProfile
->pidProfile
.setpointRelaxRatio
);
1157 sbufWriteU8(dst
, currentProfile
->pidProfile
.dtermSetpointWeight
);
1158 sbufWriteU8(dst
, 0); // reserved
1159 sbufWriteU8(dst
, 0); // reserved
1160 sbufWriteU8(dst
, 0); // reserved
1161 sbufWriteU16(dst
, (uint16_t)lrintf(currentProfile
->pidProfile
.rateAccelLimit
* 10));
1162 sbufWriteU16(dst
, (uint16_t)lrintf(currentProfile
->pidProfile
.yawRateAccelLimit
* 10));
1163 sbufWriteU8(dst
, currentProfile
->pidProfile
.levelAngleLimit
);
1164 sbufWriteU8(dst
, currentProfile
->pidProfile
.levelSensitivity
);
1167 case MSP_SENSOR_CONFIG
:
1168 sbufWriteU8(dst
, accelerometerConfig()->acc_hardware
);
1169 sbufWriteU8(dst
, barometerConfig()->baro_hardware
);
1170 sbufWriteU8(dst
, compassConfig()->mag_hardware
);
1174 if (mspPostProcessFn
) {
1175 *mspPostProcessFn
= mspRebootFn
;
1179 #if defined(VTX_COMMON)
1180 case MSP_VTX_CONFIG
:
1182 uint8_t deviceType
= vtxCommonGetDeviceType();
1183 if (deviceType
!= VTXDEV_UNKNOWN
) {
1185 uint8_t band
=0, channel
=0;
1186 vtxCommonGetBandChan(&band
,&channel
);
1188 uint8_t powerIdx
=0; // debug
1189 vtxCommonGetPowerIndex(&powerIdx
);
1192 vtxCommonGetPitmode(&pitmode
);
1194 sbufWriteU8(dst
, deviceType
);
1195 sbufWriteU8(dst
, band
);
1196 sbufWriteU8(dst
, channel
);
1197 sbufWriteU8(dst
, powerIdx
);
1198 sbufWriteU8(dst
, pitmode
);
1199 // future extensions here...
1202 sbufWriteU8(dst
, VTXDEV_UNKNOWN
); // no VTX detected
1215 static void mspFcWpCommand(sbuf_t
*dst
, sbuf_t
*src
)
1218 int32_t lat
= 0, lon
= 0;
1219 wp_no
= sbufReadU8(src
); // get the wp number
1221 lat
= GPS_home
[LAT
];
1222 lon
= GPS_home
[LON
];
1223 } else if (wp_no
== 16) {
1224 lat
= GPS_hold
[LAT
];
1225 lon
= GPS_hold
[LON
];
1227 sbufWriteU8(dst
, wp_no
);
1228 sbufWriteU32(dst
, lat
);
1229 sbufWriteU32(dst
, lon
);
1230 sbufWriteU32(dst
, AltHold
); // altitude (cm) will come here -- temporary implementation to test feature with apps
1231 sbufWriteU16(dst
, 0); // heading will come here (deg)
1232 sbufWriteU16(dst
, 0); // time to stay (ms) will come here
1233 sbufWriteU8(dst
, 0); // nav flag will come here
1238 static void mspFcDataFlashReadCommand(sbuf_t
*dst
, sbuf_t
*src
)
1240 const unsigned int dataSize
= sbufBytesRemaining(src
);
1241 const uint32_t readAddress
= sbufReadU32(src
);
1242 uint16_t readLength
;
1243 bool useLegacyFormat
;
1244 if (dataSize
>= sizeof(uint32_t) + sizeof(uint16_t)) {
1245 readLength
= sbufReadU16(src
);
1246 useLegacyFormat
= false;
1249 useLegacyFormat
= true;
1252 serializeDataflashReadReply(dst
, readAddress
, readLength
, useLegacyFormat
);
1256 static mspResult_e
mspFcProcessInCommand(uint8_t cmdMSP
, sbuf_t
*src
)
1260 const unsigned int dataSize
= sbufBytesRemaining(src
);
1263 int32_t lat
= 0, lon
= 0, alt
= 0;
1266 case MSP_SELECT_SETTING
:
1267 value
= sbufReadU8(src
);
1268 if ((value
& RATEPROFILE_MASK
) == 0) {
1269 if (!ARMING_FLAG(ARMED
)) {
1270 if (value
>= MAX_PROFILE_COUNT
) {
1273 changeProfile(value
);
1276 value
= value
& ~RATEPROFILE_MASK
;
1278 if (value
>= MAX_RATEPROFILES
) {
1281 changeControlRateProfile(value
);
1286 magHold
= sbufReadU16(src
);
1289 case MSP_SET_RAW_RC
:
1292 uint8_t channelCount
= dataSize
/ sizeof(uint16_t);
1293 if (channelCount
> MAX_SUPPORTED_RC_CHANNEL_COUNT
) {
1294 return MSP_RESULT_ERROR
;
1296 uint16_t frame
[MAX_SUPPORTED_RC_CHANNEL_COUNT
];
1297 for (int i
= 0; i
< channelCount
; i
++) {
1298 frame
[i
] = sbufReadU16(src
);
1300 rxMspFrameReceive(frame
, channelCount
);
1305 case MSP_SET_ACC_TRIM
:
1306 accelerometerConfigMutable()->accelerometerTrims
.values
.pitch
= sbufReadU16(src
);
1307 accelerometerConfigMutable()->accelerometerTrims
.values
.roll
= sbufReadU16(src
);
1309 case MSP_SET_ARMING_CONFIG
:
1310 armingConfigMutable()->auto_disarm_delay
= sbufReadU8(src
);
1311 armingConfigMutable()->disarm_kill_switch
= sbufReadU8(src
);
1314 case MSP_SET_LOOP_TIME
:
1318 case MSP_SET_PID_CONTROLLER
:
1322 for (int i
= 0; i
< PID_ITEM_COUNT
; i
++) {
1323 currentProfile
->pidProfile
.P8
[i
] = sbufReadU8(src
);
1324 currentProfile
->pidProfile
.I8
[i
] = sbufReadU8(src
);
1325 currentProfile
->pidProfile
.D8
[i
] = sbufReadU8(src
);
1327 pidInitConfig(¤tProfile
->pidProfile
);
1330 case MSP_SET_MODE_RANGE
:
1331 i
= sbufReadU8(src
);
1332 if (i
< MAX_MODE_ACTIVATION_CONDITION_COUNT
) {
1333 modeActivationCondition_t
*mac
= modeActivationConditionsMutable(i
);
1334 i
= sbufReadU8(src
);
1335 const box_t
*box
= findBoxByPermenantId(i
);
1337 mac
->modeId
= box
->boxId
;
1338 mac
->auxChannelIndex
= sbufReadU8(src
);
1339 mac
->range
.startStep
= sbufReadU8(src
);
1340 mac
->range
.endStep
= sbufReadU8(src
);
1342 useRcControlsConfig(modeActivationConditions(0), ¤tProfile
->pidProfile
);
1344 return MSP_RESULT_ERROR
;
1347 return MSP_RESULT_ERROR
;
1351 case MSP_SET_ADJUSTMENT_RANGE
:
1352 i
= sbufReadU8(src
);
1353 if (i
< MAX_ADJUSTMENT_RANGE_COUNT
) {
1354 adjustmentRange_t
*adjRange
= adjustmentRangesMutable(i
);
1355 i
= sbufReadU8(src
);
1356 if (i
< MAX_SIMULTANEOUS_ADJUSTMENT_COUNT
) {
1357 adjRange
->adjustmentIndex
= i
;
1358 adjRange
->auxChannelIndex
= sbufReadU8(src
);
1359 adjRange
->range
.startStep
= sbufReadU8(src
);
1360 adjRange
->range
.endStep
= sbufReadU8(src
);
1361 adjRange
->adjustmentFunction
= sbufReadU8(src
);
1362 adjRange
->auxSwitchChannelIndex
= sbufReadU8(src
);
1364 return MSP_RESULT_ERROR
;
1367 return MSP_RESULT_ERROR
;
1371 case MSP_SET_RC_TUNING
:
1372 if (dataSize
>= 10) {
1373 currentControlRateProfile
->rcRate8
= sbufReadU8(src
);
1374 currentControlRateProfile
->rcExpo8
= sbufReadU8(src
);
1375 for (int i
= 0; i
< 3; i
++) {
1376 value
= sbufReadU8(src
);
1377 currentControlRateProfile
->rates
[i
] = MIN(value
, i
== FD_YAW
? CONTROL_RATE_CONFIG_YAW_RATE_MAX
: CONTROL_RATE_CONFIG_ROLL_PITCH_RATE_MAX
);
1379 value
= sbufReadU8(src
);
1380 currentControlRateProfile
->dynThrPID
= MIN(value
, CONTROL_RATE_CONFIG_TPA_MAX
);
1381 currentControlRateProfile
->thrMid8
= sbufReadU8(src
);
1382 currentControlRateProfile
->thrExpo8
= sbufReadU8(src
);
1383 currentControlRateProfile
->tpa_breakpoint
= sbufReadU16(src
);
1384 if (dataSize
>= 11) {
1385 currentControlRateProfile
->rcYawExpo8
= sbufReadU8(src
);
1387 if (dataSize
>= 12) {
1388 currentControlRateProfile
->rcYawRate8
= sbufReadU8(src
);
1390 generateThrottleCurve();
1392 return MSP_RESULT_ERROR
;
1397 rxConfigMutable()->midrc
= sbufReadU16(src
);
1398 motorConfigMutable()->minthrottle
= sbufReadU16(src
);
1399 motorConfigMutable()->maxthrottle
= sbufReadU16(src
);
1400 motorConfigMutable()->mincommand
= sbufReadU16(src
);
1402 failsafeConfigMutable()->failsafe_throttle
= sbufReadU16(src
);
1405 gpsConfigMutable()->provider
= sbufReadU8(src
); // gps_type
1406 sbufReadU8(src
); // gps_baudrate
1407 gpsConfigMutable()->sbasMode
= sbufReadU8(src
); // gps_ubx_sbas
1409 sbufReadU8(src
); // gps_type
1410 sbufReadU8(src
); // gps_baudrate
1411 sbufReadU8(src
); // gps_ubx_sbas
1413 batteryConfigMutable()->multiwiiCurrentMeterOutput
= sbufReadU8(src
);
1414 rxConfigMutable()->rssi_channel
= sbufReadU8(src
);
1417 compassConfigMutable()->mag_declination
= sbufReadU16(src
) * 10;
1419 batteryConfigMutable()->vbatscale
= sbufReadU8(src
); // actual vbatscale as intended
1420 batteryConfigMutable()->vbatmincellvoltage
= sbufReadU8(src
); // vbatlevel_warn1 in MWC2.3 GUI
1421 batteryConfigMutable()->vbatmaxcellvoltage
= sbufReadU8(src
); // vbatlevel_warn2 in MWC2.3 GUI
1422 batteryConfigMutable()->vbatwarningcellvoltage
= sbufReadU8(src
); // vbatlevel when buzzer starts to alert
1426 for (int i
= 0; i
< 8; i
++) { // FIXME should this use MAX_MOTORS or MAX_SUPPORTED_MOTORS instead of 8
1427 motor_disarmed
[i
] = convertExternalToMotor(sbufReadU16(src
));
1431 case MSP_SET_SERVO_CONFIGURATION
:
1433 if (dataSize
!= 1 + sizeof(servoParam_t
)) {
1434 return MSP_RESULT_ERROR
;
1436 i
= sbufReadU8(src
);
1437 if (i
>= MAX_SUPPORTED_SERVOS
) {
1438 return MSP_RESULT_ERROR
;
1440 servoParamsMutable(i
)->min
= sbufReadU16(src
);
1441 servoParamsMutable(i
)->max
= sbufReadU16(src
);
1442 servoParamsMutable(i
)->middle
= sbufReadU16(src
);
1443 servoParamsMutable(i
)->rate
= sbufReadU8(src
);
1444 servoParamsMutable(i
)->angleAtMin
= sbufReadU8(src
);
1445 servoParamsMutable(i
)->angleAtMax
= sbufReadU8(src
);
1446 servoParamsMutable(i
)->forwardFromChannel
= sbufReadU8(src
);
1447 servoParamsMutable(i
)->reversedSources
= sbufReadU32(src
);
1452 case MSP_SET_SERVO_MIX_RULE
:
1454 i
= sbufReadU8(src
);
1455 if (i
>= MAX_SERVO_RULES
) {
1456 return MSP_RESULT_ERROR
;
1458 customServoMixersMutable(i
)->targetChannel
= sbufReadU8(src
);
1459 customServoMixersMutable(i
)->inputSource
= sbufReadU8(src
);
1460 customServoMixersMutable(i
)->rate
= sbufReadU8(src
);
1461 customServoMixersMutable(i
)->speed
= sbufReadU8(src
);
1462 customServoMixersMutable(i
)->min
= sbufReadU8(src
);
1463 customServoMixersMutable(i
)->max
= sbufReadU8(src
);
1464 customServoMixersMutable(i
)->box
= sbufReadU8(src
);
1465 loadCustomServoMixer();
1471 flight3DConfigMutable()->deadband3d_low
= sbufReadU16(src
);
1472 flight3DConfigMutable()->deadband3d_high
= sbufReadU16(src
);
1473 flight3DConfigMutable()->neutral3d
= sbufReadU16(src
);
1476 case MSP_SET_RC_DEADBAND
:
1477 rcControlsConfigMutable()->deadband
= sbufReadU8(src
);
1478 rcControlsConfigMutable()->yaw_deadband
= sbufReadU8(src
);
1479 rcControlsConfigMutable()->alt_hold_deadband
= sbufReadU8(src
);
1480 flight3DConfigMutable()->deadband3d_throttle
= sbufReadU16(src
);
1483 case MSP_SET_RESET_CURR_PID
:
1484 resetProfile(currentProfile
);
1486 case MSP_SET_SENSOR_ALIGNMENT
:
1487 gyroConfigMutable()->gyro_align
= sbufReadU8(src
);
1488 accelerometerConfigMutable()->acc_align
= sbufReadU8(src
);
1489 compassConfigMutable()->mag_align
= sbufReadU8(src
);
1492 case MSP_SET_ADVANCED_CONFIG
:
1493 gyroConfigMutable()->gyro_sync_denom
= sbufReadU8(src
);
1494 pidConfigMutable()->pid_process_denom
= sbufReadU8(src
);
1495 motorConfigMutable()->dev
.useUnsyncedPwm
= sbufReadU8(src
);
1497 motorConfigMutable()->dev
.motorPwmProtocol
= constrain(sbufReadU8(src
), 0, PWM_TYPE_MAX
- 1);
1499 motorConfigMutable()->dev
.motorPwmProtocol
= constrain(sbufReadU8(src
), 0, PWM_TYPE_BRUSHED
);
1501 motorConfigMutable()->dev
.motorPwmRate
= sbufReadU16(src
);
1502 if (sbufBytesRemaining(src
) >= 2) {
1503 motorConfigMutable()->digitalIdleOffsetPercent
= sbufReadU16(src
) / 100.0f
;
1505 if (sbufBytesRemaining(src
)) {
1506 gyroConfigMutable()->gyro_use_32khz
= sbufReadU8(src
);
1508 //!!TODO gyro_isr_update to be added pending decision
1509 /*if (sbufBytesRemaining(src)) {
1510 gyroConfigMutable()->gyro_isr_update = sbufReadU8(src);
1512 validateAndFixGyroConfig();
1514 if (sbufBytesRemaining(src
)) {
1515 motorConfigMutable()->dev
.motorPwmInversion
= sbufReadU8(src
);
1519 case MSP_SET_FILTER_CONFIG
:
1520 gyroConfigMutable()->gyro_soft_lpf_hz
= sbufReadU8(src
);
1521 currentProfile
->pidProfile
.dterm_lpf_hz
= sbufReadU16(src
);
1522 currentProfile
->pidProfile
.yaw_lpf_hz
= sbufReadU16(src
);
1524 gyroConfigMutable()->gyro_soft_notch_hz_1
= sbufReadU16(src
);
1525 gyroConfigMutable()->gyro_soft_notch_cutoff_1
= sbufReadU16(src
);
1526 currentProfile
->pidProfile
.dterm_notch_hz
= sbufReadU16(src
);
1527 currentProfile
->pidProfile
.dterm_notch_cutoff
= sbufReadU16(src
);
1529 if (dataSize
> 13) {
1530 gyroConfigMutable()->gyro_soft_notch_hz_2
= sbufReadU16(src
);
1531 gyroConfigMutable()->gyro_soft_notch_cutoff_2
= sbufReadU16(src
);
1533 // reinitialize the gyro filters with the new values
1534 validateAndFixGyroConfig();
1536 // reinitialize the PID filters with the new values
1537 pidInitFilters(¤tProfile
->pidProfile
);
1540 case MSP_SET_PID_ADVANCED
:
1543 currentProfile
->pidProfile
.yaw_p_limit
= sbufReadU16(src
);
1544 sbufReadU8(src
); // reserved
1545 currentProfile
->pidProfile
.vbatPidCompensation
= sbufReadU8(src
);
1546 currentProfile
->pidProfile
.setpointRelaxRatio
= sbufReadU8(src
);
1547 currentProfile
->pidProfile
.dtermSetpointWeight
= sbufReadU8(src
);
1548 sbufReadU8(src
); // reserved
1549 sbufReadU8(src
); // reserved
1550 sbufReadU8(src
); // reserved
1551 currentProfile
->pidProfile
.rateAccelLimit
= sbufReadU16(src
) / 10.0f
;
1552 currentProfile
->pidProfile
.yawRateAccelLimit
= sbufReadU16(src
) / 10.0f
;
1553 if (dataSize
> 17) {
1554 currentProfile
->pidProfile
.levelAngleLimit
= sbufReadU8(src
);
1555 currentProfile
->pidProfile
.levelSensitivity
= sbufReadU8(src
);
1557 pidInitConfig(¤tProfile
->pidProfile
);
1560 case MSP_SET_SENSOR_CONFIG
:
1561 accelerometerConfigMutable()->acc_hardware
= sbufReadU8(src
);
1562 barometerConfigMutable()->baro_hardware
= sbufReadU8(src
);
1563 compassConfigMutable()->mag_hardware
= sbufReadU8(src
);
1566 case MSP_RESET_CONF
:
1567 if (!ARMING_FLAG(ARMED
)) {
1573 case MSP_ACC_CALIBRATION
:
1574 if (!ARMING_FLAG(ARMED
))
1575 accSetCalibrationCycles(CALIBRATING_ACC_CYCLES
);
1578 case MSP_MAG_CALIBRATION
:
1579 if (!ARMING_FLAG(ARMED
))
1580 ENABLE_STATE(CALIBRATE_MAG
);
1583 case MSP_EEPROM_WRITE
:
1584 if (ARMING_FLAG(ARMED
)) {
1585 return MSP_RESULT_ERROR
;
1592 case MSP_SET_BLACKBOX_CONFIG
:
1593 // Don't allow config to be updated while Blackbox is logging
1594 if (blackboxMayEditConfig()) {
1595 blackboxConfigMutable()->device
= sbufReadU8(src
);
1596 blackboxConfigMutable()->rate_num
= sbufReadU8(src
);
1597 blackboxConfigMutable()->rate_denom
= sbufReadU8(src
);
1603 case MSP_SET_TRANSPONDER_CONFIG
:
1604 if (dataSize
!= sizeof(transponderConfig()->data
)) {
1605 return MSP_RESULT_ERROR
;
1607 for (unsigned int i
= 0; i
< sizeof(transponderConfig()->data
); i
++) {
1608 transponderConfigMutable()->data
[i
] = sbufReadU8(src
);
1610 transponderUpdateData();
1615 case MSP_SET_OSD_CONFIG
:
1617 const uint8_t addr
= sbufReadU8(src
);
1618 // set all the other settings
1619 if ((int8_t)addr
== -1) {
1621 vcdProfileMutable()->video_system
= sbufReadU8(src
);
1623 sbufReadU8(src
); // Skip video system
1625 osdConfigMutable()->units
= sbufReadU8(src
);
1626 osdConfigMutable()->rssi_alarm
= sbufReadU8(src
);
1627 osdConfigMutable()->cap_alarm
= sbufReadU16(src
);
1628 osdConfigMutable()->time_alarm
= sbufReadU16(src
);
1629 osdConfigMutable()->alt_alarm
= sbufReadU16(src
);
1631 // set a position setting
1632 const uint16_t pos
= sbufReadU16(src
);
1633 if (addr
< OSD_ITEM_COUNT
) {
1634 osdConfigMutable()->item_pos
[addr
] = pos
;
1639 case MSP_OSD_CHAR_WRITE
:
1642 uint8_t font_data
[64];
1643 const uint8_t addr
= sbufReadU8(src
);
1644 for (int i
= 0; i
< 54; i
++) {
1645 font_data
[i
] = sbufReadU8(src
);
1647 // !!TODO - replace this with a device independent implementation
1648 max7456WriteNvm(addr
, font_data
);
1651 // just discard the data
1653 for (int i
= 0; i
< 54; i
++) {
1660 #if defined(USE_RTC6705) || defined(VTX_COMMON)
1661 case MSP_SET_VTX_CONFIG
:
1663 uint16_t tmp
= sbufReadU16(src
);
1664 #if defined(USE_RTC6705)
1666 vtxConfig()->vtx_channel
= tmp
;
1667 if (current_vtx_channel
!= vtxConfig()->vtx_channel
) {
1668 current_vtx_channel
= vtxConfig()->vtx_channel
;
1669 rtc6705_soft_spi_set_channel(vtx_freq
[current_vtx_channel
]);
1672 if (vtxCommonGetDeviceType() != VTXDEV_UNKNOWN
) {
1674 uint8_t band
= (tmp
/ 8) + 1;
1675 uint8_t channel
= (tmp
% 8) + 1;
1677 uint8_t current_band
=0, current_channel
=0;
1678 vtxCommonGetBandChan(¤t_band
,¤t_channel
);
1679 if ((current_band
!= band
) || (current_channel
!= channel
))
1680 vtxCommonSetBandChan(band
,channel
);
1682 if (sbufBytesRemaining(src
) < 2)
1685 uint8_t power
= sbufReadU8(src
);
1686 uint8_t current_power
= 0;
1687 vtxCommonGetPowerIndex(¤t_power
);
1688 if (current_power
!= power
)
1689 vtxCommonSetPowerByIndex(power
);
1691 uint8_t pitmode
= sbufReadU8(src
);
1692 uint8_t current_pitmode
= 0;
1693 vtxCommonGetPitmode(¤t_pitmode
);
1694 if (current_pitmode
!= pitmode
)
1695 vtxCommonSetPitmode(pitmode
);
1703 case MSP_DATAFLASH_ERASE
:
1704 flashfsEraseCompletely();
1709 case MSP_SET_RAW_GPS
:
1710 if (sbufReadU8(src
)) {
1711 ENABLE_STATE(GPS_FIX
);
1713 DISABLE_STATE(GPS_FIX
);
1715 GPS_numSat
= sbufReadU8(src
);
1716 GPS_coord
[LAT
] = sbufReadU32(src
);
1717 GPS_coord
[LON
] = sbufReadU32(src
);
1718 GPS_altitude
= sbufReadU16(src
);
1719 GPS_speed
= sbufReadU16(src
);
1720 GPS_update
|= 2; // New data signalisation to GPS functions // FIXME Magic Numbers
1723 wp_no
= sbufReadU8(src
); //get the wp number
1724 lat
= sbufReadU32(src
);
1725 lon
= sbufReadU32(src
);
1726 alt
= sbufReadU32(src
); // to set altitude (cm)
1727 sbufReadU16(src
); // future: to set heading (deg)
1728 sbufReadU16(src
); // future: to set time to stay (ms)
1729 sbufReadU8(src
); // future: to set nav flag
1731 GPS_home
[LAT
] = lat
;
1732 GPS_home
[LON
] = lon
;
1733 DISABLE_FLIGHT_MODE(GPS_HOME_MODE
); // with this flag, GPS_set_next_wp will be called in the next loop -- OK with SERIAL GPS / OK with I2C GPS
1734 ENABLE_STATE(GPS_FIX_HOME
);
1736 AltHold
= alt
; // temporary implementation to test feature with apps
1737 } else if (wp_no
== 16) { // OK with SERIAL GPS -- NOK for I2C GPS / needs more code dev in order to inject GPS coord inside I2C GPS
1738 GPS_hold
[LAT
] = lat
;
1739 GPS_hold
[LON
] = lon
;
1741 AltHold
= alt
; // temporary implementation to test feature with apps
1742 nav_mode
= NAV_MODE_WP
;
1743 GPS_set_next_wp(&GPS_hold
[LAT
], &GPS_hold
[LON
]);
1747 case MSP_SET_FEATURE
:
1749 featureSet(sbufReadU32(src
)); // features bitmap
1752 case MSP_SET_BOARD_ALIGNMENT
:
1753 boardAlignmentMutable()->rollDegrees
= sbufReadU16(src
);
1754 boardAlignmentMutable()->pitchDegrees
= sbufReadU16(src
);
1755 boardAlignmentMutable()->yawDegrees
= sbufReadU16(src
);
1758 case MSP_SET_VOLTAGE_METER_CONFIG
:
1759 batteryConfigMutable()->vbatscale
= sbufReadU8(src
); // actual vbatscale as intended
1760 batteryConfigMutable()->vbatmincellvoltage
= sbufReadU8(src
); // vbatlevel_warn1 in MWC2.3 GUI
1761 batteryConfigMutable()->vbatmaxcellvoltage
= sbufReadU8(src
); // vbatlevel_warn2 in MWC2.3 GUI
1762 batteryConfigMutable()->vbatwarningcellvoltage
= sbufReadU8(src
); // vbatlevel when buzzer starts to alert
1764 batteryConfigMutable()->batteryMeterType
= sbufReadU8(src
);
1768 case MSP_SET_CURRENT_METER_CONFIG
:
1769 batteryConfigMutable()->currentMeterScale
= sbufReadU16(src
);
1770 batteryConfigMutable()->currentMeterOffset
= sbufReadU16(src
);
1771 batteryConfigMutable()->currentMeterType
= sbufReadU8(src
);
1772 batteryConfigMutable()->batteryCapacity
= sbufReadU16(src
);
1775 #ifndef USE_QUAD_MIXER_ONLY
1777 mixerConfigMutable()->mixerMode
= sbufReadU8(src
);
1781 case MSP_SET_RX_CONFIG
:
1782 rxConfigMutable()->serialrx_provider
= sbufReadU8(src
);
1783 rxConfigMutable()->maxcheck
= sbufReadU16(src
);
1784 rxConfigMutable()->midrc
= sbufReadU16(src
);
1785 rxConfigMutable()->mincheck
= sbufReadU16(src
);
1786 rxConfigMutable()->spektrum_sat_bind
= sbufReadU8(src
);
1788 rxConfigMutable()->rx_min_usec
= sbufReadU16(src
);
1789 rxConfigMutable()->rx_max_usec
= sbufReadU16(src
);
1791 if (dataSize
> 12) {
1792 rxConfigMutable()->rcInterpolation
= sbufReadU8(src
);
1793 rxConfigMutable()->rcInterpolationInterval
= sbufReadU8(src
);
1794 rxConfigMutable()->airModeActivateThreshold
= sbufReadU16(src
);
1796 if (dataSize
> 16) {
1797 rxConfigMutable()->rx_spi_protocol
= sbufReadU8(src
);
1798 rxConfigMutable()->rx_spi_id
= sbufReadU32(src
);
1799 rxConfigMutable()->rx_spi_rf_channel_count
= sbufReadU8(src
);
1801 if (dataSize
> 22) {
1802 rxConfigMutable()->fpvCamAngleDegrees
= sbufReadU8(src
);
1806 case MSP_SET_FAILSAFE_CONFIG
:
1807 failsafeConfigMutable()->failsafe_delay
= sbufReadU8(src
);
1808 failsafeConfigMutable()->failsafe_off_delay
= sbufReadU8(src
);
1809 failsafeConfigMutable()->failsafe_throttle
= sbufReadU16(src
);
1810 failsafeConfigMutable()->failsafe_kill_switch
= sbufReadU8(src
);
1811 failsafeConfigMutable()->failsafe_throttle_low_delay
= sbufReadU16(src
);
1812 failsafeConfigMutable()->failsafe_procedure
= sbufReadU8(src
);
1815 case MSP_SET_RXFAIL_CONFIG
:
1816 i
= sbufReadU8(src
);
1817 if (i
< MAX_SUPPORTED_RC_CHANNEL_COUNT
) {
1818 rxConfigMutable()->failsafe_channel_configurations
[i
].mode
= sbufReadU8(src
);
1819 rxConfigMutable()->failsafe_channel_configurations
[i
].step
= CHANNEL_VALUE_TO_RXFAIL_STEP(sbufReadU16(src
));
1821 return MSP_RESULT_ERROR
;
1825 case MSP_SET_RSSI_CONFIG
:
1826 rxConfigMutable()->rssi_channel
= sbufReadU8(src
);
1829 case MSP_SET_RX_MAP
:
1830 for (int i
= 0; i
< MAX_MAPPABLE_RX_INPUTS
; i
++) {
1831 rxConfigMutable()->rcmap
[i
] = sbufReadU8(src
);
1835 case MSP_SET_BF_CONFIG
:
1836 #ifdef USE_QUAD_MIXER_ONLY
1837 sbufReadU8(src
); // mixerMode ignored
1839 mixerConfigMutable()->mixerMode
= sbufReadU8(src
); // mixerMode
1843 featureSet(sbufReadU32(src
)); // features bitmap
1845 rxConfigMutable()->serialrx_provider
= sbufReadU8(src
); // serialrx_type
1847 boardAlignmentMutable()->rollDegrees
= sbufReadU16(src
); // board_align_roll
1848 boardAlignmentMutable()->pitchDegrees
= sbufReadU16(src
); // board_align_pitch
1849 boardAlignmentMutable()->yawDegrees
= sbufReadU16(src
); // board_align_yaw
1851 batteryConfigMutable()->currentMeterScale
= sbufReadU16(src
);
1852 batteryConfigMutable()->currentMeterOffset
= sbufReadU16(src
);
1855 case MSP_SET_CF_SERIAL_CONFIG
:
1857 uint8_t portConfigSize
= sizeof(uint8_t) + sizeof(uint16_t) + (sizeof(uint8_t) * 4);
1859 if (dataSize
% portConfigSize
!= 0) {
1860 return MSP_RESULT_ERROR
;
1863 uint8_t remainingPortsInPacket
= dataSize
/ portConfigSize
;
1865 while (remainingPortsInPacket
--) {
1866 uint8_t identifier
= sbufReadU8(src
);
1868 serialPortConfig_t
*portConfig
= serialFindPortConfiguration(identifier
);
1870 return MSP_RESULT_ERROR
;
1873 portConfig
->identifier
= identifier
;
1874 portConfig
->functionMask
= sbufReadU16(src
);
1875 portConfig
->msp_baudrateIndex
= sbufReadU8(src
);
1876 portConfig
->gps_baudrateIndex
= sbufReadU8(src
);
1877 portConfig
->telemetry_baudrateIndex
= sbufReadU8(src
);
1878 portConfig
->blackbox_baudrateIndex
= sbufReadU8(src
);
1884 case MSP_SET_LED_COLORS
:
1885 for (int i
= 0; i
< LED_CONFIGURABLE_COLOR_COUNT
; i
++) {
1886 hsvColor_t
*color
= &ledStripConfigMutable()->colors
[i
];
1887 color
->h
= sbufReadU16(src
);
1888 color
->s
= sbufReadU8(src
);
1889 color
->v
= sbufReadU8(src
);
1893 case MSP_SET_LED_STRIP_CONFIG
:
1895 i
= sbufReadU8(src
);
1896 if (i
>= LED_MAX_STRIP_LENGTH
|| dataSize
!= (1 + 4)) {
1897 return MSP_RESULT_ERROR
;
1899 ledConfig_t
*ledConfig
= &ledStripConfigMutable()->ledConfigs
[i
];
1900 *ledConfig
= sbufReadU32(src
);
1901 reevaluateLedConfig();
1905 case MSP_SET_LED_STRIP_MODECOLOR
:
1907 ledModeIndex_e modeIdx
= sbufReadU8(src
);
1908 int funIdx
= sbufReadU8(src
);
1909 int color
= sbufReadU8(src
);
1911 if (!setModeColor(modeIdx
, funIdx
, color
))
1912 return MSP_RESULT_ERROR
;
1918 memset(systemConfigMutable()->name
, 0, ARRAYLEN(systemConfig()->name
));
1919 for (unsigned int i
= 0; i
< MIN(MAX_NAME_LENGTH
, dataSize
); i
++) {
1920 systemConfigMutable()->name
[i
] = sbufReadU8(src
);
1925 // we do not know how to handle the (valid) message, indicate error MSP $M!
1926 return MSP_RESULT_ERROR
;
1928 return MSP_RESULT_ACK
;
1932 * Returns MSP_RESULT_ACK, MSP_RESULT_ERROR or MSP_RESULT_NO_REPLY
1934 mspResult_e
mspFcProcessCommand(mspPacket_t
*cmd
, mspPacket_t
*reply
, mspPostProcessFnPtr
*mspPostProcessFn
)
1936 int ret
= MSP_RESULT_ACK
;
1937 sbuf_t
*dst
= &reply
->buf
;
1938 sbuf_t
*src
= &cmd
->buf
;
1939 const uint8_t cmdMSP
= cmd
->cmd
;
1940 // initialize reply by default
1941 reply
->cmd
= cmd
->cmd
;
1943 if (mspFcProcessOutCommand(cmdMSP
, dst
, mspPostProcessFn
)) {
1944 ret
= MSP_RESULT_ACK
;
1945 #ifdef USE_SERIAL_4WAY_BLHELI_INTERFACE
1946 } else if (cmdMSP
== MSP_SET_4WAY_IF
) {
1947 mspFc4waySerialCommand(dst
, src
, mspPostProcessFn
);
1948 ret
= MSP_RESULT_ACK
;
1951 } else if (cmdMSP
== MSP_WP
) {
1952 mspFcWpCommand(dst
, src
);
1953 ret
= MSP_RESULT_ACK
;
1956 } else if (cmdMSP
== MSP_DATAFLASH_READ
) {
1957 mspFcDataFlashReadCommand(dst
, src
);
1958 ret
= MSP_RESULT_ACK
;
1961 ret
= mspFcProcessInCommand(cmdMSP
, src
);
1963 reply
->result
= ret
;
1968 * Return a pointer to the process command function
1970 void mspFcInit(void)