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_eeprom.h"
37 #include "config/config_profile.h"
38 #include "config/feature.h"
39 #include "config/parameter_group.h"
40 #include "config/parameter_group_ids.h"
42 #include "drivers/accgyro.h"
43 #include "drivers/bus_i2c.h"
44 #include "drivers/compass.h"
45 #include "drivers/flash.h"
46 #include "drivers/io.h"
47 #include "drivers/max7456.h"
48 #include "drivers/pwm_output.h"
49 #include "drivers/sdcard.h"
50 #include "drivers/serial.h"
51 #include "drivers/serial_escserial.h"
52 #include "drivers/system.h"
53 #include "drivers/vcd.h"
54 #include "drivers/vtx_common.h"
55 #include "drivers/vtx_soft_spi_rtc6705.h"
57 #include "fc/config.h"
58 #include "fc/controlrate_profile.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"
89 #include "msp/msp_protocol.h"
90 #include "msp/msp_serial.h"
95 #include "scheduler/scheduler.h"
97 #include "sensors/acceleration.h"
98 #include "sensors/barometer.h"
99 #include "sensors/battery.h"
100 #include "sensors/boardalignment.h"
101 #include "sensors/compass.h"
102 #include "sensors/gyro.h"
103 #include "sensors/sensors.h"
104 #include "sensors/sonar.h"
106 #include "telemetry/telemetry.h"
108 #ifdef USE_HARDWARE_REVISION_DETECTION
109 #include "hardware_revision.h"
112 extern uint16_t cycleTime
; // FIXME dependency on mw.c
114 static const char * const flightControllerIdentifier
= BETAFLIGHT_IDENTIFIER
; // 4 UPPER CASE alpha numeric characters that identify the flight controller.
115 static const char * const boardIdentifier
= TARGET_BOARD_IDENTIFIER
;
117 typedef struct box_e
{
118 const uint8_t boxId
; // see boxId_e
119 const char *boxName
; // GUI-readable box name
120 const uint8_t permanentId
; //
124 static const box_t boxes
[CHECKBOX_ITEM_COUNT
+ 1] = {
125 { BOXARM
, "ARM;", 0 },
126 { BOXANGLE
, "ANGLE;", 1 },
127 { BOXHORIZON
, "HORIZON;", 2 },
128 { BOXBARO
, "BARO;", 3 },
129 //{ BOXVARIO, "VARIO;", 4 },
130 { BOXMAG
, "MAG;", 5 },
131 { BOXHEADFREE
, "HEADFREE;", 6 },
132 { BOXHEADADJ
, "HEADADJ;", 7 },
133 { BOXCAMSTAB
, "CAMSTAB;", 8 },
134 { BOXCAMTRIG
, "CAMTRIG;", 9 },
135 { BOXGPSHOME
, "GPS HOME;", 10 },
136 { BOXGPSHOLD
, "GPS HOLD;", 11 },
137 { BOXPASSTHRU
, "PASSTHRU;", 12 },
138 { BOXBEEPERON
, "BEEPER;", 13 },
139 { BOXLEDMAX
, "LEDMAX;", 14 },
140 { BOXLEDLOW
, "LEDLOW;", 15 },
141 { BOXLLIGHTS
, "LLIGHTS;", 16 },
142 { BOXCALIB
, "CALIB;", 17 },
143 { BOXGOV
, "GOVERNOR;", 18 },
144 { BOXOSD
, "OSD SW;", 19 },
145 { BOXTELEMETRY
, "TELEMETRY;", 20 },
146 { BOXGTUNE
, "GTUNE;", 21 },
147 { BOXSONAR
, "SONAR;", 22 },
148 { BOXSERVO1
, "SERVO1;", 23 },
149 { BOXSERVO2
, "SERVO2;", 24 },
150 { BOXSERVO3
, "SERVO3;", 25 },
151 { BOXBLACKBOX
, "BLACKBOX;", 26 },
152 { BOXFAILSAFE
, "FAILSAFE;", 27 },
153 { BOXAIRMODE
, "AIR MODE;", 28 },
154 { BOX3DDISABLESWITCH
, "DISABLE 3D SWITCH;", 29},
155 { BOXFPVANGLEMIX
, "FPV ANGLE MIX;", 30},
156 { BOXBLACKBOXERASE
, "BLACKBOX ERASE (>30s);", 31 },
157 { CHECKBOX_ITEM_COUNT
, NULL
, 0xFF }
160 // this is calculated at startup based on enabled features.
161 static uint8_t activeBoxIds
[CHECKBOX_ITEM_COUNT
];
162 // this is the number of filled indexes in above array
163 static uint8_t activeBoxIdCount
= 0;
165 static const char pidnames
[] =
178 MSP_SDCARD_STATE_NOT_PRESENT
= 0,
179 MSP_SDCARD_STATE_FATAL
= 1,
180 MSP_SDCARD_STATE_CARD_INIT
= 2,
181 MSP_SDCARD_STATE_FS_INIT
= 3,
182 MSP_SDCARD_STATE_READY
= 4
186 MSP_SDCARD_FLAG_SUPPORTTED
= 1
189 #define RATEPROFILE_MASK (1 << 7)
191 #ifdef USE_SERIAL_4WAY_BLHELI_INTERFACE
192 #define ESC_4WAY 0xff
195 uint8_t escPortIndex
= 0;
198 static void mspEscPassthroughFn(serialPort_t
*serialPort
)
200 escEnablePassthrough(serialPort
, escPortIndex
, escMode
);
204 static void mspFc4waySerialCommand(sbuf_t
*dst
, sbuf_t
*src
, mspPostProcessFnPtr
*mspPostProcessFn
)
206 const unsigned int dataSize
= sbufBytesRemaining(src
);
212 escMode
= sbufReadU8(src
);
213 escPortIndex
= sbufReadU8(src
);
218 // get channel number
219 // switch all motor lines HI
220 // reply with the count of ESC found
221 sbufWriteU8(dst
, esc4wayInit());
223 if (mspPostProcessFn
) {
224 *mspPostProcessFn
= esc4wayProcess
;
229 case PROTOCOL_SIMONK
:
230 case PROTOCOL_BLHELI
:
232 case PROTOCOL_KISSALL
:
233 case PROTOCOL_CASTLE
:
234 if (escPortIndex
< getMotorCount() || (escMode
== PROTOCOL_KISS
&& escPortIndex
== ALL_ESCS
)) {
237 if (mspPostProcessFn
) {
238 *mspPostProcessFn
= mspEscPassthroughFn
;
250 static void mspRebootFn(serialPort_t
*serialPort
)
257 // control should never return here.
261 static const box_t
*findBoxByActiveBoxId(uint8_t activeBoxId
)
263 for (uint8_t boxIndex
= 0; boxIndex
< sizeof(boxes
) / sizeof(box_t
); boxIndex
++) {
264 const box_t
*candidate
= &boxes
[boxIndex
];
265 if (candidate
->boxId
== activeBoxId
) {
272 static const box_t
*findBoxByPermenantId(uint8_t permenantId
)
274 for (uint8_t boxIndex
= 0; boxIndex
< sizeof(boxes
) / sizeof(box_t
); boxIndex
++) {
275 const box_t
*candidate
= &boxes
[boxIndex
];
276 if (candidate
->permanentId
== permenantId
) {
283 static void serializeBoxNamesReply(sbuf_t
*dst
)
285 int flag
= 1, count
= 0;
288 // in first run of the loop, we grab total size of junk to be sent
289 // then come back and actually send it
290 for (int i
= 0; i
< activeBoxIdCount
; i
++) {
291 const int activeBoxId
= activeBoxIds
[i
];
292 const box_t
*box
= findBoxByActiveBoxId(activeBoxId
);
297 const int len
= strlen(box
->boxName
);
301 for (int j
= 0; j
< len
; j
++) {
302 sbufWriteU8(dst
, box
->boxName
[j
]);
313 void initActiveBoxIds(void)
315 // calculate used boxes based on features and fill availableBoxes[] array
316 memset(activeBoxIds
, 0xFF, sizeof(activeBoxIds
));
318 activeBoxIdCount
= 0;
319 activeBoxIds
[activeBoxIdCount
++] = BOXARM
;
321 if (!feature(FEATURE_AIRMODE
)) {
322 activeBoxIds
[activeBoxIdCount
++] = BOXAIRMODE
;
325 if (sensors(SENSOR_ACC
)) {
326 activeBoxIds
[activeBoxIdCount
++] = BOXANGLE
;
327 activeBoxIds
[activeBoxIdCount
++] = BOXHORIZON
;
328 activeBoxIds
[activeBoxIdCount
++] = BOXHEADFREE
;
332 if (sensors(SENSOR_BARO
)) {
333 activeBoxIds
[activeBoxIdCount
++] = BOXBARO
;
338 if (sensors(SENSOR_MAG
)) {
339 activeBoxIds
[activeBoxIdCount
++] = BOXMAG
;
340 activeBoxIds
[activeBoxIdCount
++] = BOXHEADADJ
;
345 if (feature(FEATURE_GPS
)) {
346 activeBoxIds
[activeBoxIdCount
++] = BOXGPSHOME
;
347 activeBoxIds
[activeBoxIdCount
++] = BOXGPSHOLD
;
352 if (feature(FEATURE_SONAR
)) {
353 activeBoxIds
[activeBoxIdCount
++] = BOXSONAR
;
357 if (feature(FEATURE_FAILSAFE
)) {
358 activeBoxIds
[activeBoxIdCount
++] = BOXFAILSAFE
;
361 if (mixerConfig()->mixerMode
== MIXER_FLYING_WING
|| mixerConfig()->mixerMode
== MIXER_AIRPLANE
) {
362 activeBoxIds
[activeBoxIdCount
++] = BOXPASSTHRU
;
365 activeBoxIds
[activeBoxIdCount
++] = BOXBEEPERON
;
368 if (feature(FEATURE_LED_STRIP
)) {
369 activeBoxIds
[activeBoxIdCount
++] = BOXLEDLOW
;
374 if (feature(FEATURE_BLACKBOX
)) {
375 activeBoxIds
[activeBoxIdCount
++] = BOXBLACKBOX
;
377 activeBoxIds
[activeBoxIdCount
++] = BOXBLACKBOXERASE
;
382 activeBoxIds
[activeBoxIdCount
++] = BOXFPVANGLEMIX
;
384 if (feature(FEATURE_3D
)) {
385 activeBoxIds
[activeBoxIdCount
++] = BOX3DDISABLESWITCH
;
388 if (feature(FEATURE_SERVO_TILT
)) {
389 activeBoxIds
[activeBoxIdCount
++] = BOXCAMSTAB
;
392 if (feature(FEATURE_INFLIGHT_ACC_CAL
)) {
393 activeBoxIds
[activeBoxIdCount
++] = BOXCALIB
;
396 activeBoxIds
[activeBoxIdCount
++] = BOXOSD
;
399 if (feature(FEATURE_TELEMETRY
) && telemetryConfig()->telemetry_switch
) {
400 activeBoxIds
[activeBoxIdCount
++] = BOXTELEMETRY
;
405 if (mixerConfig()->mixerMode
== MIXER_CUSTOM_AIRPLANE
) {
406 activeBoxIds
[activeBoxIdCount
++] = BOXSERVO1
;
407 activeBoxIds
[activeBoxIdCount
++] = BOXSERVO2
;
408 activeBoxIds
[activeBoxIdCount
++] = BOXSERVO3
;
413 #define IS_ENABLED(mask) (mask == 0 ? 0 : 1)
415 static uint32_t packFlightModeFlags(void)
417 // Serialize the flags in the order we delivered them, ignoring BOXNAMES and BOXINDEXES
418 // Requires new Multiwii protocol version to fix
419 // It would be preferable to setting the enabled bits based on BOXINDEX.
420 const uint32_t tmp
= IS_ENABLED(FLIGHT_MODE(ANGLE_MODE
)) << BOXANGLE
|
421 IS_ENABLED(FLIGHT_MODE(HORIZON_MODE
)) << BOXHORIZON
|
422 IS_ENABLED(FLIGHT_MODE(BARO_MODE
)) << BOXBARO
|
423 IS_ENABLED(FLIGHT_MODE(MAG_MODE
)) << BOXMAG
|
424 IS_ENABLED(FLIGHT_MODE(HEADFREE_MODE
)) << BOXHEADFREE
|
425 IS_ENABLED(IS_RC_MODE_ACTIVE(BOXHEADADJ
)) << BOXHEADADJ
|
426 IS_ENABLED(IS_RC_MODE_ACTIVE(BOXCAMSTAB
)) << BOXCAMSTAB
|
427 IS_ENABLED(IS_RC_MODE_ACTIVE(BOXCAMTRIG
)) << BOXCAMTRIG
|
428 IS_ENABLED(FLIGHT_MODE(GPS_HOME_MODE
)) << BOXGPSHOME
|
429 IS_ENABLED(FLIGHT_MODE(GPS_HOLD_MODE
)) << BOXGPSHOLD
|
430 IS_ENABLED(FLIGHT_MODE(PASSTHRU_MODE
)) << BOXPASSTHRU
|
431 IS_ENABLED(IS_RC_MODE_ACTIVE(BOXBEEPERON
)) << BOXBEEPERON
|
432 IS_ENABLED(IS_RC_MODE_ACTIVE(BOXLEDMAX
)) << BOXLEDMAX
|
433 IS_ENABLED(IS_RC_MODE_ACTIVE(BOXLEDLOW
)) << BOXLEDLOW
|
434 IS_ENABLED(IS_RC_MODE_ACTIVE(BOXLLIGHTS
)) << BOXLLIGHTS
|
435 IS_ENABLED(IS_RC_MODE_ACTIVE(BOXCALIB
)) << BOXCALIB
|
436 IS_ENABLED(IS_RC_MODE_ACTIVE(BOXGOV
)) << BOXGOV
|
437 IS_ENABLED(IS_RC_MODE_ACTIVE(BOXOSD
)) << BOXOSD
|
438 IS_ENABLED(IS_RC_MODE_ACTIVE(BOXTELEMETRY
)) << BOXTELEMETRY
|
439 IS_ENABLED(IS_RC_MODE_ACTIVE(BOXGTUNE
)) << BOXGTUNE
|
440 IS_ENABLED(FLIGHT_MODE(SONAR_MODE
)) << BOXSONAR
|
441 IS_ENABLED(ARMING_FLAG(ARMED
)) << BOXARM
|
442 IS_ENABLED(IS_RC_MODE_ACTIVE(BOXBLACKBOX
)) << BOXBLACKBOX
|
443 IS_ENABLED(IS_RC_MODE_ACTIVE(BOXBLACKBOXERASE
)) << BOXBLACKBOXERASE
|
444 IS_ENABLED(FLIGHT_MODE(FAILSAFE_MODE
)) << BOXFAILSAFE
|
445 IS_ENABLED(IS_RC_MODE_ACTIVE(BOXAIRMODE
)) << BOXAIRMODE
|
446 IS_ENABLED(IS_RC_MODE_ACTIVE(BOXFPVANGLEMIX
)) << BOXFPVANGLEMIX
;
449 for (int i
= 0; i
< activeBoxIdCount
; i
++) {
450 const uint32_t flag
= (tmp
& (1 << activeBoxIds
[i
]));
458 static void serializeSDCardSummaryReply(sbuf_t
*dst
)
461 uint8_t flags
= MSP_SDCARD_FLAG_SUPPORTTED
;
464 sbufWriteU8(dst
, flags
);
466 // Merge the card and filesystem states together
467 if (!sdcard_isInserted()) {
468 state
= MSP_SDCARD_STATE_NOT_PRESENT
;
469 } else if (!sdcard_isFunctional()) {
470 state
= MSP_SDCARD_STATE_FATAL
;
472 switch (afatfs_getFilesystemState()) {
473 case AFATFS_FILESYSTEM_STATE_READY
:
474 state
= MSP_SDCARD_STATE_READY
;
477 case AFATFS_FILESYSTEM_STATE_INITIALIZATION
:
478 if (sdcard_isInitialized()) {
479 state
= MSP_SDCARD_STATE_FS_INIT
;
481 state
= MSP_SDCARD_STATE_CARD_INIT
;
485 case AFATFS_FILESYSTEM_STATE_FATAL
:
486 case AFATFS_FILESYSTEM_STATE_UNKNOWN
:
488 state
= MSP_SDCARD_STATE_FATAL
;
494 sbufWriteU8(dst
, state
);
495 sbufWriteU8(dst
, afatfs_getLastError());
496 // Write free space and total space in kilobytes
497 sbufWriteU32(dst
, afatfs_getContiguousFreeSpace() / 1024);
498 sbufWriteU32(dst
, sdcard_getMetadata()->numBlocks
/ 2); // Block size is half a kilobyte
503 sbufWriteU32(dst
, 0);
504 sbufWriteU32(dst
, 0);
508 static void serializeDataflashSummaryReply(sbuf_t
*dst
)
511 const flashGeometry_t
*geometry
= flashfsGetGeometry();
512 uint8_t flags
= (flashfsIsReady() ? 1 : 0) | 2 /* FlashFS is supported */;
514 sbufWriteU8(dst
, flags
);
515 sbufWriteU32(dst
, geometry
->sectors
);
516 sbufWriteU32(dst
, geometry
->totalSize
);
517 sbufWriteU32(dst
, flashfsGetOffset()); // Effectively the current number of bytes stored on the volume
519 sbufWriteU8(dst
, 0); // FlashFS is neither ready nor supported
520 sbufWriteU32(dst
, 0);
521 sbufWriteU32(dst
, 0);
522 sbufWriteU32(dst
, 0);
527 static void serializeDataflashReadReply(sbuf_t
*dst
, uint32_t address
, const uint16_t size
, bool useLegacyFormat
)
529 BUILD_BUG_ON(MSP_PORT_DATAFLASH_INFO_SIZE
< 16);
531 uint16_t readLen
= size
;
532 const int bytesRemainingInBuf
= sbufBytesRemaining(dst
) - MSP_PORT_DATAFLASH_INFO_SIZE
;
533 if (readLen
> bytesRemainingInBuf
) {
534 readLen
= bytesRemainingInBuf
;
536 // size will be lower than that requested if we reach end of volume
537 if (readLen
> flashfsGetSize() - address
) {
538 // truncate the request
539 readLen
= flashfsGetSize() - address
;
541 sbufWriteU32(dst
, address
);
542 if (!useLegacyFormat
) {
543 // new format supports variable read lengths
544 sbufWriteU16(dst
, readLen
);
545 sbufWriteU8(dst
, 0); // placeholder for compression format
548 // bytesRead will equal readLen
549 const int bytesRead
= flashfsReadAbs(address
, sbufPtr(dst
), readLen
);
550 sbufAdvance(dst
, bytesRead
);
552 if (useLegacyFormat
) {
553 // pad the buffer with zeros
554 for (int i
= bytesRead
; i
< size
; i
++) {
562 * Returns true if the command was processd, false otherwise.
563 * May set mspPostProcessFunc to a function to be called once the command has been processed
565 static bool mspFcProcessOutCommand(uint8_t cmdMSP
, sbuf_t
*dst
, mspPostProcessFnPtr
*mspPostProcessFn
)
568 case MSP_API_VERSION
:
569 sbufWriteU8(dst
, MSP_PROTOCOL_VERSION
);
570 sbufWriteU8(dst
, API_VERSION_MAJOR
);
571 sbufWriteU8(dst
, API_VERSION_MINOR
);
575 sbufWriteData(dst
, flightControllerIdentifier
, FLIGHT_CONTROLLER_IDENTIFIER_LENGTH
);
579 sbufWriteU8(dst
, FC_VERSION_MAJOR
);
580 sbufWriteU8(dst
, FC_VERSION_MINOR
);
581 sbufWriteU8(dst
, FC_VERSION_PATCH_LEVEL
);
585 sbufWriteData(dst
, boardIdentifier
, BOARD_IDENTIFIER_LENGTH
);
586 #ifdef USE_HARDWARE_REVISION_DETECTION
587 sbufWriteU16(dst
, hardwareRevision
);
589 sbufWriteU16(dst
, 0); // No other build targets currently have hardware revision detection.
594 sbufWriteData(dst
, buildDate
, BUILD_DATE_LENGTH
);
595 sbufWriteData(dst
, buildTime
, BUILD_TIME_LENGTH
);
596 sbufWriteData(dst
, shortGitRevision
, GIT_SHORT_REVISION_LENGTH
);
599 // DEPRECATED - Use MSP_API_VERSION
601 sbufWriteU8(dst
, MW_VERSION
);
602 sbufWriteU8(dst
, mixerConfig()->mixerMode
);
603 sbufWriteU8(dst
, MSP_PROTOCOL_VERSION
);
604 sbufWriteU32(dst
, CAP_DYNBALANCE
); // "capability"
608 sbufWriteU16(dst
, getTaskDeltaTime(TASK_GYROPID
));
610 sbufWriteU16(dst
, i2cGetErrorCounter());
612 sbufWriteU16(dst
, 0);
614 sbufWriteU16(dst
, sensors(SENSOR_ACC
) | sensors(SENSOR_BARO
) << 1 | sensors(SENSOR_MAG
) << 2 | sensors(SENSOR_GPS
) << 3 | sensors(SENSOR_SONAR
) << 4);
615 sbufWriteU32(dst
, packFlightModeFlags());
616 sbufWriteU8(dst
, getCurrentPidProfileIndex());
617 sbufWriteU16(dst
, constrain(averageSystemLoadPercent
, 0, 100));
618 sbufWriteU8(dst
, MAX_PROFILE_COUNT
);
619 sbufWriteU8(dst
, getCurrentControlRateProfileIndex());
624 const int nameLen
= strlen(systemConfig()->name
);
625 for (int i
= 0; i
< nameLen
; i
++) {
626 sbufWriteU8(dst
, systemConfig()->name
[i
]);
632 sbufWriteU16(dst
, getTaskDeltaTime(TASK_GYROPID
));
634 sbufWriteU16(dst
, i2cGetErrorCounter());
636 sbufWriteU16(dst
, 0);
638 sbufWriteU16(dst
, sensors(SENSOR_ACC
) | sensors(SENSOR_BARO
) << 1 | sensors(SENSOR_MAG
) << 2 | sensors(SENSOR_GPS
) << 3 | sensors(SENSOR_SONAR
) << 4);
639 sbufWriteU32(dst
, packFlightModeFlags());
640 sbufWriteU8(dst
, getCurrentPidProfileIndex());
641 sbufWriteU16(dst
, constrain(averageSystemLoadPercent
, 0, 100));
642 sbufWriteU16(dst
, 0); // gyro cycle time
647 // Hack scale due to choice of units for sensor data in multiwii
648 const uint8_t scale
= (acc
.dev
.acc_1G
> 512) ? 4 : 1;
649 for (int i
= 0; i
< 3; i
++) {
650 sbufWriteU16(dst
, acc
.accSmooth
[i
] / scale
);
652 for (int i
= 0; i
< 3; i
++) {
653 sbufWriteU16(dst
, gyroRateDps(i
));
655 for (int i
= 0; i
< 3; i
++) {
656 sbufWriteU16(dst
, mag
.magADC
[i
]);
663 sbufWriteData(dst
, &servo
, MAX_SUPPORTED_SERVOS
* 2);
665 case MSP_SERVO_CONFIGURATIONS
:
666 for (int i
= 0; i
< MAX_SUPPORTED_SERVOS
; i
++) {
667 sbufWriteU16(dst
, servoParams(i
)->min
);
668 sbufWriteU16(dst
, servoParams(i
)->max
);
669 sbufWriteU16(dst
, servoParams(i
)->middle
);
670 sbufWriteU8(dst
, servoParams(i
)->rate
);
671 sbufWriteU8(dst
, servoParams(i
)->angleAtMin
);
672 sbufWriteU8(dst
, servoParams(i
)->angleAtMax
);
673 sbufWriteU8(dst
, servoParams(i
)->forwardFromChannel
);
674 sbufWriteU32(dst
, servoParams(i
)->reversedSources
);
677 case MSP_SERVO_MIX_RULES
:
678 for (int i
= 0; i
< MAX_SERVO_RULES
; i
++) {
679 sbufWriteU8(dst
, customServoMixers(i
)->targetChannel
);
680 sbufWriteU8(dst
, customServoMixers(i
)->inputSource
);
681 sbufWriteU8(dst
, customServoMixers(i
)->rate
);
682 sbufWriteU8(dst
, customServoMixers(i
)->speed
);
683 sbufWriteU8(dst
, customServoMixers(i
)->min
);
684 sbufWriteU8(dst
, customServoMixers(i
)->max
);
685 sbufWriteU8(dst
, customServoMixers(i
)->box
);
691 for (unsigned i
= 0; i
< 8; i
++) {
692 if (i
>= MAX_SUPPORTED_MOTORS
|| !pwmGetMotors()[i
].enabled
) {
693 sbufWriteU16(dst
, 0);
697 sbufWriteU16(dst
, convertMotorToExternal(motor
[i
]));
702 for (int i
= 0; i
< rxRuntimeConfig
.channelCount
; i
++) {
703 sbufWriteU16(dst
, rcData
[i
]);
708 sbufWriteU16(dst
, attitude
.values
.roll
);
709 sbufWriteU16(dst
, attitude
.values
.pitch
);
710 sbufWriteU16(dst
, DECIDEGREES_TO_DEGREES(attitude
.values
.yaw
));
714 #if defined(BARO) || defined(SONAR)
715 sbufWriteU32(dst
, altitudeHoldGetEstimatedAltitude());
717 sbufWriteU32(dst
, 0);
719 sbufWriteU16(dst
, vario
);
722 case MSP_SONAR_ALTITUDE
:
724 sbufWriteU32(dst
, sonarGetLatestAltitude());
726 sbufWriteU32(dst
, 0);
731 sbufWriteU8(dst
, (uint8_t)constrain(getBatteryVoltage(), 0, 255));
732 sbufWriteU16(dst
, (uint16_t)constrain(getMAhDrawn(), 0, 0xFFFF)); // milliamp hours drawn from battery
733 sbufWriteU16(dst
, rssi
);
734 sbufWriteU16(dst
, (int16_t)constrain(getAmperage(), -0x8000, 0x7FFF)); // send current in 0.01 A steps, range is -320A to 320A
737 case MSP_ARMING_CONFIG
:
738 sbufWriteU8(dst
, armingConfig()->auto_disarm_delay
);
739 sbufWriteU8(dst
, armingConfig()->disarm_kill_switch
);
743 sbufWriteU16(dst
, (uint16_t)gyro
.targetLooptime
);
747 sbufWriteU8(dst
, currentControlRateProfile
->rcRate8
);
748 sbufWriteU8(dst
, currentControlRateProfile
->rcExpo8
);
749 for (int i
= 0 ; i
< 3; i
++) {
750 sbufWriteU8(dst
, currentControlRateProfile
->rates
[i
]); // R,P,Y see flight_dynamics_index_t
752 sbufWriteU8(dst
, currentControlRateProfile
->dynThrPID
);
753 sbufWriteU8(dst
, currentControlRateProfile
->thrMid8
);
754 sbufWriteU8(dst
, currentControlRateProfile
->thrExpo8
);
755 sbufWriteU16(dst
, currentControlRateProfile
->tpa_breakpoint
);
756 sbufWriteU8(dst
, currentControlRateProfile
->rcYawExpo8
);
757 sbufWriteU8(dst
, currentControlRateProfile
->rcYawRate8
);
761 for (int i
= 0; i
< PID_ITEM_COUNT
; i
++) {
762 sbufWriteU8(dst
, currentPidProfile
->P8
[i
]);
763 sbufWriteU8(dst
, currentPidProfile
->I8
[i
]);
764 sbufWriteU8(dst
, currentPidProfile
->D8
[i
]);
769 for (const char *c
= pidnames
; *c
; c
++) {
770 sbufWriteU8(dst
, *c
);
774 case MSP_PID_CONTROLLER
:
775 sbufWriteU8(dst
, PID_CONTROLLER_BETAFLIGHT
);
778 case MSP_MODE_RANGES
:
779 for (int i
= 0; i
< MAX_MODE_ACTIVATION_CONDITION_COUNT
; i
++) {
780 const modeActivationCondition_t
*mac
= modeActivationConditions(i
);
781 const box_t
*box
= &boxes
[mac
->modeId
];
782 sbufWriteU8(dst
, box
->permanentId
);
783 sbufWriteU8(dst
, mac
->auxChannelIndex
);
784 sbufWriteU8(dst
, mac
->range
.startStep
);
785 sbufWriteU8(dst
, mac
->range
.endStep
);
789 case MSP_ADJUSTMENT_RANGES
:
790 for (int i
= 0; i
< MAX_ADJUSTMENT_RANGE_COUNT
; i
++) {
791 const adjustmentRange_t
*adjRange
= adjustmentRanges(i
);
792 sbufWriteU8(dst
, adjRange
->adjustmentIndex
);
793 sbufWriteU8(dst
, adjRange
->auxChannelIndex
);
794 sbufWriteU8(dst
, adjRange
->range
.startStep
);
795 sbufWriteU8(dst
, adjRange
->range
.endStep
);
796 sbufWriteU8(dst
, adjRange
->adjustmentFunction
);
797 sbufWriteU8(dst
, adjRange
->auxSwitchChannelIndex
);
802 serializeBoxNamesReply(dst
);
806 for (int i
= 0; i
< activeBoxIdCount
; i
++) {
807 const box_t
*box
= findBoxByActiveBoxId(activeBoxIds
[i
]);
811 sbufWriteU8(dst
, box
->permanentId
);
816 sbufWriteU16(dst
, rxConfig()->midrc
);
818 sbufWriteU16(dst
, motorConfig()->minthrottle
);
819 sbufWriteU16(dst
, motorConfig()->maxthrottle
);
820 sbufWriteU16(dst
, motorConfig()->mincommand
);
822 sbufWriteU16(dst
, failsafeConfig()->failsafe_throttle
);
825 sbufWriteU8(dst
, gpsConfig()->provider
); // gps_type
826 sbufWriteU8(dst
, 0); // TODO gps_baudrate (an index, cleanflight uses a uint32_t
827 sbufWriteU8(dst
, gpsConfig()->sbasMode
); // gps_ubx_sbas
829 sbufWriteU8(dst
, 0); // gps_type
830 sbufWriteU8(dst
, 0); // TODO gps_baudrate (an index, cleanflight uses a uint32_t
831 sbufWriteU8(dst
, 0); // gps_ubx_sbas
833 sbufWriteU8(dst
, 0); // was multiwiiCurrentMeterOutput
834 sbufWriteU8(dst
, rxConfig()->rssi_channel
);
837 sbufWriteU16(dst
, compassConfig()->mag_declination
/ 10);
839 sbufWriteU8(dst
, 0); // was vbatscale
840 sbufWriteU8(dst
, batteryConfig()->vbatmincellvoltage
);
841 sbufWriteU8(dst
, batteryConfig()->vbatmaxcellvoltage
);
842 sbufWriteU8(dst
, batteryConfig()->vbatwarningcellvoltage
);
846 // FIXME This is hardcoded and should not be.
847 for (int i
= 0; i
< 8; i
++) {
848 sbufWriteU8(dst
, i
+ 1);
854 sbufWriteU8(dst
, STATE(GPS_FIX
));
855 sbufWriteU8(dst
, GPS_numSat
);
856 sbufWriteU32(dst
, GPS_coord
[LAT
]);
857 sbufWriteU32(dst
, GPS_coord
[LON
]);
858 sbufWriteU16(dst
, GPS_altitude
);
859 sbufWriteU16(dst
, GPS_speed
);
860 sbufWriteU16(dst
, GPS_ground_course
);
864 sbufWriteU16(dst
, GPS_distanceToHome
);
865 sbufWriteU16(dst
, GPS_directionToHome
);
866 sbufWriteU8(dst
, GPS_update
& 1);
870 sbufWriteU8(dst
, GPS_numCh
);
871 for (int i
= 0; i
< GPS_numCh
; i
++) {
872 sbufWriteU8(dst
, GPS_svinfo_chn
[i
]);
873 sbufWriteU8(dst
, GPS_svinfo_svid
[i
]);
874 sbufWriteU8(dst
, GPS_svinfo_quality
[i
]);
875 sbufWriteU8(dst
, GPS_svinfo_cno
[i
]);
881 // output some useful QA statistics
882 // debug[x] = ((hse_value / 1000000) * 1000) + (SystemCoreClock / 1000000); // XX0YY [crystal clock : core clock]
884 for (int i
= 0; i
< DEBUG16_VALUE_COUNT
; i
++) {
885 sbufWriteU16(dst
, debug
[i
]); // 4 variables are here for general monitoring purpose
889 // Additional commands that are not compatible with MultiWii
891 sbufWriteU16(dst
, accelerometerConfig()->accelerometerTrims
.values
.pitch
);
892 sbufWriteU16(dst
, accelerometerConfig()->accelerometerTrims
.values
.roll
);
896 sbufWriteU32(dst
, U_ID_0
);
897 sbufWriteU32(dst
, U_ID_1
);
898 sbufWriteU32(dst
, U_ID_2
);
902 sbufWriteU32(dst
, featureMask());
905 case MSP_BOARD_ALIGNMENT
:
906 sbufWriteU16(dst
, boardAlignment()->rollDegrees
);
907 sbufWriteU16(dst
, boardAlignment()->pitchDegrees
);
908 sbufWriteU16(dst
, boardAlignment()->yawDegrees
);
911 case MSP_BATTERY_STATE
: {
912 // battery characteristics
913 sbufWriteU8(dst
, (uint8_t)constrain(getBatteryCellCount(), 0, 255)); // 0 indicates battery not detected.
914 sbufWriteU16(dst
, batteryConfig()->batteryCapacity
); // in mAh
917 sbufWriteU8(dst
, (uint8_t)constrain(getBatteryVoltage(), 0, 255)); // in 0.1V steps
918 sbufWriteU16(dst
, (uint16_t)constrain(getMAhDrawn(), 0, 0xFFFF)); // milliamp hours drawn from battery
919 sbufWriteU16(dst
, (int16_t)constrain(getAmperage(), -0x8000, 0x7FFF)); // send current in 0.01 A steps, range is -320A to 320A
922 sbufWriteU8(dst
, (uint8_t)getBatteryState());
925 case MSP_VOLTAGE_METERS
:
926 // write out id and voltage meter values, once for each meter we support
927 for (int i
= 0; i
< supportedVoltageMeterCount
; i
++) {
929 voltageMeter_t meter
;
930 uint8_t id
= (uint8_t)voltageMeterIds
[i
];
931 voltageMeterRead(id
, &meter
);
933 sbufWriteU8(dst
, id
);
934 sbufWriteU8(dst
, (uint8_t)constrain(meter
.filtered
, 0, 255));
938 case MSP_CURRENT_METERS
:
939 // write out id and current meter values, once for each meter we support
940 for (int i
= 0; i
< supportedCurrentMeterCount
; i
++) {
942 currentMeter_t meter
;
943 uint8_t id
= (uint8_t)currentMeterIds
[i
];
944 currentMeterRead(id
, &meter
);
946 sbufWriteU8(dst
, id
);
947 sbufWriteU16(dst
, (uint16_t)constrain(meter
.mAhDrawn
, 0, 0xFFFF)); // milliamp hours drawn from battery
948 sbufWriteU16(dst
, (uint16_t)constrain(meter
.amperage
* 10, 0, 0xFFFF)); // send amperage in 0.001 A steps (mA). Negative range is truncated to zero
952 case MSP_VOLTAGE_METER_CONFIG
:
953 // by using a sensor type and a sub-frame length it's possible to configure any type of voltage meter,
954 // e.g. an i2c/spi/can sensor or any sensor not built directly into the FC such as ESC/RX/SPort/SBus that has
955 // different configuration requirements.
956 BUILD_BUG_ON(VOLTAGE_SENSOR_ADC_VBAT
!= 0); // VOLTAGE_SENSOR_ADC_VBAT should be the first index,
957 sbufWriteU8(dst
, MAX_VOLTAGE_SENSOR_ADC
); // voltage meters in payload
958 for (int i
= VOLTAGE_SENSOR_ADC_VBAT
; i
< MAX_VOLTAGE_SENSOR_ADC
; i
++) {
959 sbufWriteU8(dst
, voltageMeterADCtoIDMap
[i
]); // id of the sensor
960 sbufWriteU8(dst
, VOLTAGE_SENSOR_TYPE_ADC_RESISTOR_DIVIDER
); // indicate the type of sensor that the next part of the payload is for
962 const uint8_t adcSensorSubframeLength
= 1 + 1 + 1; // length of vbatscale, vbatresdivval, vbatresdivmultipler, in bytes
963 sbufWriteU8(dst
, adcSensorSubframeLength
); // ADC sensor sub-frame length
964 sbufWriteU8(dst
, voltageSensorADCConfig(i
)->vbatscale
);
965 sbufWriteU8(dst
, voltageSensorADCConfig(i
)->vbatresdivval
);
966 sbufWriteU8(dst
, voltageSensorADCConfig(i
)->vbatresdivmultiplier
);
968 // if we had any other voltage sensors, this is where we would output any needed configuration
971 case MSP_CURRENT_METER_CONFIG
: {
972 // the ADC and VIRTUAL sensors have the same configuration requirements, however this API reflects
973 // that this situation may change and allows us to support configuration of any current sensor with
974 // specialist configuration requirements.
976 sbufWriteU8(dst
, 2); // current meters in payload (adc + virtual)
978 const uint8_t adcSensorSubframeLength
= 1 + 1 + 2 + 2; // length of id, type, scale, offset, in bytes
979 sbufWriteU8(dst
, adcSensorSubframeLength
);
980 sbufWriteU8(dst
, CURRENT_METER_ID_BATTERY_1
); // the id of the sensor
981 sbufWriteU8(dst
, CURRENT_SENSOR_ADC
); // indicate the type of sensor that the next part of the payload is for
982 sbufWriteU16(dst
, currentSensorADCConfig()->scale
);
983 sbufWriteU16(dst
, currentSensorADCConfig()->offset
);
985 const int8_t virtualSensorSubframeLength
= 1 + 1 + 2 + 2; // length of id, type, scale, offset, in bytes
986 sbufWriteU8(dst
, virtualSensorSubframeLength
);
987 sbufWriteU8(dst
, CURRENT_METER_ID_VIRTUAL_1
); // the id of the sensor
988 sbufWriteU8(dst
, CURRENT_SENSOR_VIRTUAL
); // indicate the type of sensor that the next part of the payload is for
989 sbufWriteU16(dst
, currentMeterVirtualConfig()->scale
);
990 sbufWriteU16(dst
, currentMeterVirtualConfig()->offset
);
992 // if we had any other current sensors, this is where we would output any needed configuration
995 case MSP_BATTERY_CONFIG
:
996 sbufWriteU8(dst
, batteryConfig()->vbatmincellvoltage
);
997 sbufWriteU8(dst
, batteryConfig()->vbatmaxcellvoltage
);
998 sbufWriteU8(dst
, batteryConfig()->vbatwarningcellvoltage
);
999 sbufWriteU16(dst
, batteryConfig()->batteryCapacity
);
1000 sbufWriteU8(dst
, batteryConfig()->voltageMeterSource
);
1001 sbufWriteU8(dst
, batteryConfig()->currentMeterSource
);
1005 sbufWriteU8(dst
, mixerConfig()->mixerMode
);
1009 sbufWriteU8(dst
, rxConfig()->serialrx_provider
);
1010 sbufWriteU16(dst
, rxConfig()->maxcheck
);
1011 sbufWriteU16(dst
, rxConfig()->midrc
);
1012 sbufWriteU16(dst
, rxConfig()->mincheck
);
1013 sbufWriteU8(dst
, rxConfig()->spektrum_sat_bind
);
1014 sbufWriteU16(dst
, rxConfig()->rx_min_usec
);
1015 sbufWriteU16(dst
, rxConfig()->rx_max_usec
);
1016 sbufWriteU8(dst
, rxConfig()->rcInterpolation
);
1017 sbufWriteU8(dst
, rxConfig()->rcInterpolationInterval
);
1018 sbufWriteU16(dst
, rxConfig()->airModeActivateThreshold
);
1019 sbufWriteU8(dst
, rxConfig()->rx_spi_protocol
);
1020 sbufWriteU32(dst
, rxConfig()->rx_spi_id
);
1021 sbufWriteU8(dst
, rxConfig()->rx_spi_rf_channel_count
);
1022 sbufWriteU8(dst
, rxConfig()->fpvCamAngleDegrees
);
1025 case MSP_FAILSAFE_CONFIG
:
1026 sbufWriteU8(dst
, failsafeConfig()->failsafe_delay
);
1027 sbufWriteU8(dst
, failsafeConfig()->failsafe_off_delay
);
1028 sbufWriteU16(dst
, failsafeConfig()->failsafe_throttle
);
1029 sbufWriteU8(dst
, failsafeConfig()->failsafe_kill_switch
);
1030 sbufWriteU16(dst
, failsafeConfig()->failsafe_throttle_low_delay
);
1031 sbufWriteU8(dst
, failsafeConfig()->failsafe_procedure
);
1034 case MSP_RXFAIL_CONFIG
:
1035 for (int i
= 0; i
< rxRuntimeConfig
.channelCount
; i
++) {
1036 sbufWriteU8(dst
, rxFailsafeChannelConfigs(i
)->mode
);
1037 sbufWriteU16(dst
, RXFAIL_STEP_TO_CHANNEL_VALUE(rxFailsafeChannelConfigs(i
)->step
));
1041 case MSP_RSSI_CONFIG
:
1042 sbufWriteU8(dst
, rxConfig()->rssi_channel
);
1046 sbufWriteData(dst
, rxConfig()->rcmap
, MAX_MAPPABLE_RX_INPUTS
);
1050 sbufWriteU8(dst
, mixerConfig()->mixerMode
);
1052 sbufWriteU32(dst
, featureMask());
1054 sbufWriteU8(dst
, rxConfig()->serialrx_provider
);
1056 sbufWriteU16(dst
, boardAlignment()->rollDegrees
);
1057 sbufWriteU16(dst
, boardAlignment()->pitchDegrees
);
1058 sbufWriteU16(dst
, boardAlignment()->yawDegrees
);
1060 sbufWriteU16(dst
, 0); // was currentMeterScale, see MSP_CURRENT_METER_CONFIG
1061 sbufWriteU16(dst
, 0); //was currentMeterOffset, see MSP_CURRENT_METER_CONFIG
1064 case MSP_CF_SERIAL_CONFIG
:
1065 for (int i
= 0; i
< SERIAL_PORT_COUNT
; i
++) {
1066 if (!serialIsPortAvailable(serialConfig()->portConfigs
[i
].identifier
)) {
1069 sbufWriteU8(dst
, serialConfig()->portConfigs
[i
].identifier
);
1070 sbufWriteU16(dst
, serialConfig()->portConfigs
[i
].functionMask
);
1071 sbufWriteU8(dst
, serialConfig()->portConfigs
[i
].msp_baudrateIndex
);
1072 sbufWriteU8(dst
, serialConfig()->portConfigs
[i
].gps_baudrateIndex
);
1073 sbufWriteU8(dst
, serialConfig()->portConfigs
[i
].telemetry_baudrateIndex
);
1074 sbufWriteU8(dst
, serialConfig()->portConfigs
[i
].blackbox_baudrateIndex
);
1079 case MSP_LED_COLORS
:
1080 for (int i
= 0; i
< LED_CONFIGURABLE_COLOR_COUNT
; i
++) {
1081 const hsvColor_t
*color
= &ledStripConfig()->colors
[i
];
1082 sbufWriteU16(dst
, color
->h
);
1083 sbufWriteU8(dst
, color
->s
);
1084 sbufWriteU8(dst
, color
->v
);
1088 case MSP_LED_STRIP_CONFIG
:
1089 for (int i
= 0; i
< LED_MAX_STRIP_LENGTH
; i
++) {
1090 const ledConfig_t
*ledConfig
= &ledStripConfig()->ledConfigs
[i
];
1091 sbufWriteU32(dst
, *ledConfig
);
1095 case MSP_LED_STRIP_MODECOLOR
:
1096 for (int i
= 0; i
< LED_MODE_COUNT
; i
++) {
1097 for (int j
= 0; j
< LED_DIRECTION_COUNT
; j
++) {
1098 sbufWriteU8(dst
, i
);
1099 sbufWriteU8(dst
, j
);
1100 sbufWriteU8(dst
, ledStripConfig()->modeColors
[i
].color
[j
]);
1104 for (int j
= 0; j
< LED_SPECIAL_COLOR_COUNT
; j
++) {
1105 sbufWriteU8(dst
, LED_MODE_COUNT
);
1106 sbufWriteU8(dst
, j
);
1107 sbufWriteU8(dst
, ledStripConfig()->specialColors
.color
[j
]);
1110 sbufWriteU8(dst
, LED_AUX_CHANNEL
);
1111 sbufWriteU8(dst
, 0);
1112 sbufWriteU8(dst
, ledStripConfig()->ledstrip_aux_channel
);
1116 case MSP_DATAFLASH_SUMMARY
:
1117 serializeDataflashSummaryReply(dst
);
1120 case MSP_BLACKBOX_CONFIG
:
1122 sbufWriteU8(dst
, 1); //Blackbox supported
1123 sbufWriteU8(dst
, blackboxConfig()->device
);
1124 sbufWriteU8(dst
, blackboxConfig()->rate_num
);
1125 sbufWriteU8(dst
, blackboxConfig()->rate_denom
);
1127 sbufWriteU8(dst
, 0); // Blackbox not supported
1128 sbufWriteU8(dst
, 0);
1129 sbufWriteU8(dst
, 0);
1130 sbufWriteU8(dst
, 0);
1134 case MSP_SDCARD_SUMMARY
:
1135 serializeSDCardSummaryReply(dst
);
1138 case MSP_TRANSPONDER_CONFIG
:
1140 sbufWriteU8(dst
, 1); //Transponder supported
1141 for (unsigned int i
= 0; i
< sizeof(transponderConfig()->data
); i
++) {
1142 sbufWriteU8(dst
, transponderConfig()->data
[i
]);
1145 sbufWriteU8(dst
, 0); // Transponder not supported
1149 case MSP_OSD_CONFIG
:
1151 sbufWriteU8(dst
, 1); // OSD supported
1152 // send video system (AUTO/PAL/NTSC)
1154 sbufWriteU8(dst
, vcdProfile()->video_system
);
1156 sbufWriteU8(dst
, 0);
1158 sbufWriteU8(dst
, osdConfig()->units
);
1159 sbufWriteU8(dst
, osdConfig()->rssi_alarm
);
1160 sbufWriteU16(dst
, osdConfig()->cap_alarm
);
1161 sbufWriteU16(dst
, osdConfig()->time_alarm
);
1162 sbufWriteU16(dst
, osdConfig()->alt_alarm
);
1163 for (int i
= 0; i
< OSD_ITEM_COUNT
; i
++) {
1164 sbufWriteU16(dst
, osdConfig()->item_pos
[i
]);
1167 sbufWriteU8(dst
, 0); // OSD not supported
1171 case MSP_BF_BUILD_INFO
:
1172 sbufWriteData(dst
, buildDate
, 11); // MMM DD YYYY as ascii, MMM = Jan/Feb... etc
1173 sbufWriteU32(dst
, 0); // future exp
1174 sbufWriteU32(dst
, 0); // future exp
1178 sbufWriteU16(dst
, flight3DConfig()->deadband3d_low
);
1179 sbufWriteU16(dst
, flight3DConfig()->deadband3d_high
);
1180 sbufWriteU16(dst
, flight3DConfig()->neutral3d
);
1183 case MSP_RC_DEADBAND
:
1184 sbufWriteU8(dst
, rcControlsConfig()->deadband
);
1185 sbufWriteU8(dst
, rcControlsConfig()->yaw_deadband
);
1186 sbufWriteU8(dst
, rcControlsConfig()->alt_hold_deadband
);
1187 sbufWriteU16(dst
, flight3DConfig()->deadband3d_throttle
);
1190 case MSP_SENSOR_ALIGNMENT
:
1191 sbufWriteU8(dst
, gyroConfig()->gyro_align
);
1192 sbufWriteU8(dst
, accelerometerConfig()->acc_align
);
1193 sbufWriteU8(dst
, compassConfig()->mag_align
);
1196 case MSP_ADVANCED_CONFIG
:
1197 if (gyroConfig()->gyro_lpf
) {
1198 sbufWriteU8(dst
, 8); // If gyro_lpf != OFF then looptime is set to 1000
1199 sbufWriteU8(dst
, 1);
1201 sbufWriteU8(dst
, gyroConfig()->gyro_sync_denom
);
1202 sbufWriteU8(dst
, pidConfig()->pid_process_denom
);
1204 sbufWriteU8(dst
, motorConfig()->dev
.useUnsyncedPwm
);
1205 sbufWriteU8(dst
, motorConfig()->dev
.motorPwmProtocol
);
1206 sbufWriteU16(dst
, motorConfig()->dev
.motorPwmRate
);
1207 sbufWriteU16(dst
, (uint16_t)lrintf(motorConfig()->digitalIdleOffsetPercent
* 100));
1208 sbufWriteU8(dst
, gyroConfig()->gyro_use_32khz
);
1209 //!!TODO gyro_isr_update to be added pending decision
1210 //sbufWriteU8(dst, gyroConfig()->gyro_isr_update);
1211 sbufWriteU8(dst
, motorConfig()->dev
.motorPwmInversion
);
1214 case MSP_FILTER_CONFIG
:
1215 sbufWriteU8(dst
, gyroConfig()->gyro_soft_lpf_hz
);
1216 sbufWriteU16(dst
, currentPidProfile
->dterm_lpf_hz
);
1217 sbufWriteU16(dst
, currentPidProfile
->yaw_lpf_hz
);
1218 sbufWriteU16(dst
, gyroConfig()->gyro_soft_notch_hz_1
);
1219 sbufWriteU16(dst
, gyroConfig()->gyro_soft_notch_cutoff_1
);
1220 sbufWriteU16(dst
, currentPidProfile
->dterm_notch_hz
);
1221 sbufWriteU16(dst
, currentPidProfile
->dterm_notch_cutoff
);
1222 sbufWriteU16(dst
, gyroConfig()->gyro_soft_notch_hz_2
);
1223 sbufWriteU16(dst
, gyroConfig()->gyro_soft_notch_cutoff_2
);
1226 case MSP_PID_ADVANCED
:
1227 sbufWriteU16(dst
, 0);
1228 sbufWriteU16(dst
, 0);
1229 sbufWriteU16(dst
, 0); // was pidProfile.yaw_p_limit
1230 sbufWriteU8(dst
, 0); // reserved
1231 sbufWriteU8(dst
, currentPidProfile
->vbatPidCompensation
);
1232 sbufWriteU8(dst
, currentPidProfile
->setpointRelaxRatio
);
1233 sbufWriteU8(dst
, currentPidProfile
->dtermSetpointWeight
);
1234 sbufWriteU8(dst
, 0); // reserved
1235 sbufWriteU8(dst
, 0); // reserved
1236 sbufWriteU8(dst
, 0); // reserved
1237 sbufWriteU16(dst
, (uint16_t)lrintf(currentPidProfile
->rateAccelLimit
* 10));
1238 sbufWriteU16(dst
, (uint16_t)lrintf(currentPidProfile
->yawRateAccelLimit
* 10));
1239 sbufWriteU8(dst
, currentPidProfile
->levelAngleLimit
);
1240 sbufWriteU8(dst
, currentPidProfile
->levelSensitivity
);
1243 case MSP_SENSOR_CONFIG
:
1244 sbufWriteU8(dst
, accelerometerConfig()->acc_hardware
);
1245 sbufWriteU8(dst
, barometerConfig()->baro_hardware
);
1246 sbufWriteU8(dst
, compassConfig()->mag_hardware
);
1250 if (mspPostProcessFn
) {
1251 *mspPostProcessFn
= mspRebootFn
;
1255 #if defined(VTX_COMMON)
1256 case MSP_VTX_CONFIG
:
1258 uint8_t deviceType
= vtxCommonGetDeviceType();
1259 if (deviceType
!= VTXDEV_UNKNOWN
) {
1261 uint8_t band
=0, channel
=0;
1262 vtxCommonGetBandChan(&band
,&channel
);
1264 uint8_t powerIdx
=0; // debug
1265 vtxCommonGetPowerIndex(&powerIdx
);
1268 vtxCommonGetPitmode(&pitmode
);
1270 sbufWriteU8(dst
, deviceType
);
1271 sbufWriteU8(dst
, band
);
1272 sbufWriteU8(dst
, channel
);
1273 sbufWriteU8(dst
, powerIdx
);
1274 sbufWriteU8(dst
, pitmode
);
1275 // future extensions here...
1278 sbufWriteU8(dst
, VTXDEV_UNKNOWN
); // no VTX detected
1291 static void mspFcWpCommand(sbuf_t
*dst
, sbuf_t
*src
)
1294 int32_t lat
= 0, lon
= 0;
1295 wp_no
= sbufReadU8(src
); // get the wp number
1297 lat
= GPS_home
[LAT
];
1298 lon
= GPS_home
[LON
];
1299 } else if (wp_no
== 16) {
1300 lat
= GPS_hold
[LAT
];
1301 lon
= GPS_hold
[LON
];
1303 sbufWriteU8(dst
, wp_no
);
1304 sbufWriteU32(dst
, lat
);
1305 sbufWriteU32(dst
, lon
);
1306 sbufWriteU32(dst
, AltHold
); // altitude (cm) will come here -- temporary implementation to test feature with apps
1307 sbufWriteU16(dst
, 0); // heading will come here (deg)
1308 sbufWriteU16(dst
, 0); // time to stay (ms) will come here
1309 sbufWriteU8(dst
, 0); // nav flag will come here
1314 static void mspFcDataFlashReadCommand(sbuf_t
*dst
, sbuf_t
*src
)
1316 const unsigned int dataSize
= sbufBytesRemaining(src
);
1317 const uint32_t readAddress
= sbufReadU32(src
);
1318 uint16_t readLength
;
1319 bool useLegacyFormat
;
1320 if (dataSize
>= sizeof(uint32_t) + sizeof(uint16_t)) {
1321 readLength
= sbufReadU16(src
);
1322 useLegacyFormat
= false;
1325 useLegacyFormat
= true;
1328 serializeDataflashReadReply(dst
, readAddress
, readLength
, useLegacyFormat
);
1332 static mspResult_e
mspFcProcessInCommand(uint8_t cmdMSP
, sbuf_t
*src
)
1336 const unsigned int dataSize
= sbufBytesRemaining(src
);
1339 int32_t lat
= 0, lon
= 0, alt
= 0;
1342 case MSP_SELECT_SETTING
:
1343 value
= sbufReadU8(src
);
1344 if ((value
& RATEPROFILE_MASK
) == 0) {
1345 if (!ARMING_FLAG(ARMED
)) {
1346 if (value
>= MAX_PROFILE_COUNT
) {
1349 changePidProfile(value
);
1352 value
= value
& ~RATEPROFILE_MASK
;
1354 if (value
>= CONTROL_RATE_PROFILE_COUNT
) {
1357 changeControlRateProfile(value
);
1362 magHold
= sbufReadU16(src
);
1365 case MSP_SET_RAW_RC
:
1368 uint8_t channelCount
= dataSize
/ sizeof(uint16_t);
1369 if (channelCount
> MAX_SUPPORTED_RC_CHANNEL_COUNT
) {
1370 return MSP_RESULT_ERROR
;
1372 uint16_t frame
[MAX_SUPPORTED_RC_CHANNEL_COUNT
];
1373 for (int i
= 0; i
< channelCount
; i
++) {
1374 frame
[i
] = sbufReadU16(src
);
1376 rxMspFrameReceive(frame
, channelCount
);
1381 case MSP_SET_ACC_TRIM
:
1382 accelerometerConfigMutable()->accelerometerTrims
.values
.pitch
= sbufReadU16(src
);
1383 accelerometerConfigMutable()->accelerometerTrims
.values
.roll
= sbufReadU16(src
);
1385 case MSP_SET_ARMING_CONFIG
:
1386 armingConfigMutable()->auto_disarm_delay
= sbufReadU8(src
);
1387 armingConfigMutable()->disarm_kill_switch
= sbufReadU8(src
);
1390 case MSP_SET_LOOP_TIME
:
1394 case MSP_SET_PID_CONTROLLER
:
1398 for (int i
= 0; i
< PID_ITEM_COUNT
; i
++) {
1399 currentPidProfile
->P8
[i
] = sbufReadU8(src
);
1400 currentPidProfile
->I8
[i
] = sbufReadU8(src
);
1401 currentPidProfile
->D8
[i
] = sbufReadU8(src
);
1403 pidInitConfig(currentPidProfile
);
1406 case MSP_SET_MODE_RANGE
:
1407 i
= sbufReadU8(src
);
1408 if (i
< MAX_MODE_ACTIVATION_CONDITION_COUNT
) {
1409 modeActivationCondition_t
*mac
= modeActivationConditionsMutable(i
);
1410 i
= sbufReadU8(src
);
1411 const box_t
*box
= findBoxByPermenantId(i
);
1413 mac
->modeId
= box
->boxId
;
1414 mac
->auxChannelIndex
= sbufReadU8(src
);
1415 mac
->range
.startStep
= sbufReadU8(src
);
1416 mac
->range
.endStep
= sbufReadU8(src
);
1418 useRcControlsConfig(modeActivationConditions(0), currentPidProfile
);
1420 return MSP_RESULT_ERROR
;
1423 return MSP_RESULT_ERROR
;
1427 case MSP_SET_ADJUSTMENT_RANGE
:
1428 i
= sbufReadU8(src
);
1429 if (i
< MAX_ADJUSTMENT_RANGE_COUNT
) {
1430 adjustmentRange_t
*adjRange
= adjustmentRangesMutable(i
);
1431 i
= sbufReadU8(src
);
1432 if (i
< MAX_SIMULTANEOUS_ADJUSTMENT_COUNT
) {
1433 adjRange
->adjustmentIndex
= i
;
1434 adjRange
->auxChannelIndex
= sbufReadU8(src
);
1435 adjRange
->range
.startStep
= sbufReadU8(src
);
1436 adjRange
->range
.endStep
= sbufReadU8(src
);
1437 adjRange
->adjustmentFunction
= sbufReadU8(src
);
1438 adjRange
->auxSwitchChannelIndex
= sbufReadU8(src
);
1440 return MSP_RESULT_ERROR
;
1443 return MSP_RESULT_ERROR
;
1447 case MSP_SET_RC_TUNING
:
1448 if (dataSize
>= 10) {
1449 currentControlRateProfile
->rcRate8
= sbufReadU8(src
);
1450 currentControlRateProfile
->rcExpo8
= sbufReadU8(src
);
1451 for (int i
= 0; i
< 3; i
++) {
1452 value
= sbufReadU8(src
);
1453 currentControlRateProfile
->rates
[i
] = MIN(value
, i
== FD_YAW
? CONTROL_RATE_CONFIG_YAW_RATE_MAX
: CONTROL_RATE_CONFIG_ROLL_PITCH_RATE_MAX
);
1455 value
= sbufReadU8(src
);
1456 currentControlRateProfile
->dynThrPID
= MIN(value
, CONTROL_RATE_CONFIG_TPA_MAX
);
1457 currentControlRateProfile
->thrMid8
= sbufReadU8(src
);
1458 currentControlRateProfile
->thrExpo8
= sbufReadU8(src
);
1459 currentControlRateProfile
->tpa_breakpoint
= sbufReadU16(src
);
1460 if (dataSize
>= 11) {
1461 currentControlRateProfile
->rcYawExpo8
= sbufReadU8(src
);
1463 if (dataSize
>= 12) {
1464 currentControlRateProfile
->rcYawRate8
= sbufReadU8(src
);
1466 generateThrottleCurve();
1468 return MSP_RESULT_ERROR
;
1473 rxConfigMutable()->midrc
= sbufReadU16(src
);
1474 motorConfigMutable()->minthrottle
= sbufReadU16(src
);
1475 motorConfigMutable()->maxthrottle
= sbufReadU16(src
);
1476 motorConfigMutable()->mincommand
= sbufReadU16(src
);
1478 failsafeConfigMutable()->failsafe_throttle
= sbufReadU16(src
);
1481 gpsConfigMutable()->provider
= sbufReadU8(src
); // gps_type
1482 sbufReadU8(src
); // gps_baudrate
1483 gpsConfigMutable()->sbasMode
= sbufReadU8(src
); // gps_ubx_sbas
1485 sbufReadU8(src
); // gps_type
1486 sbufReadU8(src
); // gps_baudrate
1487 sbufReadU8(src
); // gps_ubx_sbas
1489 sbufReadU8(src
); // legacy - was multiwiiCurrentMeterOutput
1490 rxConfigMutable()->rssi_channel
= sbufReadU8(src
);
1493 compassConfigMutable()->mag_declination
= sbufReadU16(src
) * 10;
1495 sbufReadU8(src
); // legacy - was vbatscale
1496 batteryConfigMutable()->vbatmincellvoltage
= sbufReadU8(src
); // vbatlevel_warn1 in MWC2.3 GUI
1497 batteryConfigMutable()->vbatmaxcellvoltage
= sbufReadU8(src
); // vbatlevel_warn2 in MWC2.3 GUI
1498 batteryConfigMutable()->vbatwarningcellvoltage
= sbufReadU8(src
); // vbatlevel when buzzer starts to alert
1502 for (int i
= 0; i
< 8; i
++) { // FIXME should this use MAX_MOTORS or MAX_SUPPORTED_MOTORS instead of 8
1503 motor_disarmed
[i
] = convertExternalToMotor(sbufReadU16(src
));
1507 case MSP_SET_SERVO_CONFIGURATION
:
1509 if (dataSize
!= 1 + sizeof(servoParam_t
)) {
1510 return MSP_RESULT_ERROR
;
1512 i
= sbufReadU8(src
);
1513 if (i
>= MAX_SUPPORTED_SERVOS
) {
1514 return MSP_RESULT_ERROR
;
1516 servoParamsMutable(i
)->min
= sbufReadU16(src
);
1517 servoParamsMutable(i
)->max
= sbufReadU16(src
);
1518 servoParamsMutable(i
)->middle
= sbufReadU16(src
);
1519 servoParamsMutable(i
)->rate
= sbufReadU8(src
);
1520 servoParamsMutable(i
)->angleAtMin
= sbufReadU8(src
);
1521 servoParamsMutable(i
)->angleAtMax
= sbufReadU8(src
);
1522 servoParamsMutable(i
)->forwardFromChannel
= sbufReadU8(src
);
1523 servoParamsMutable(i
)->reversedSources
= sbufReadU32(src
);
1528 case MSP_SET_SERVO_MIX_RULE
:
1530 i
= sbufReadU8(src
);
1531 if (i
>= MAX_SERVO_RULES
) {
1532 return MSP_RESULT_ERROR
;
1534 customServoMixersMutable(i
)->targetChannel
= sbufReadU8(src
);
1535 customServoMixersMutable(i
)->inputSource
= sbufReadU8(src
);
1536 customServoMixersMutable(i
)->rate
= sbufReadU8(src
);
1537 customServoMixersMutable(i
)->speed
= sbufReadU8(src
);
1538 customServoMixersMutable(i
)->min
= sbufReadU8(src
);
1539 customServoMixersMutable(i
)->max
= sbufReadU8(src
);
1540 customServoMixersMutable(i
)->box
= sbufReadU8(src
);
1541 loadCustomServoMixer();
1547 flight3DConfigMutable()->deadband3d_low
= sbufReadU16(src
);
1548 flight3DConfigMutable()->deadband3d_high
= sbufReadU16(src
);
1549 flight3DConfigMutable()->neutral3d
= sbufReadU16(src
);
1552 case MSP_SET_RC_DEADBAND
:
1553 rcControlsConfigMutable()->deadband
= sbufReadU8(src
);
1554 rcControlsConfigMutable()->yaw_deadband
= sbufReadU8(src
);
1555 rcControlsConfigMutable()->alt_hold_deadband
= sbufReadU8(src
);
1556 flight3DConfigMutable()->deadband3d_throttle
= sbufReadU16(src
);
1559 case MSP_SET_RESET_CURR_PID
:
1560 resetPidProfile(currentPidProfile
);
1562 case MSP_SET_SENSOR_ALIGNMENT
:
1563 gyroConfigMutable()->gyro_align
= sbufReadU8(src
);
1564 accelerometerConfigMutable()->acc_align
= sbufReadU8(src
);
1565 compassConfigMutable()->mag_align
= sbufReadU8(src
);
1568 case MSP_SET_ADVANCED_CONFIG
:
1569 gyroConfigMutable()->gyro_sync_denom
= sbufReadU8(src
);
1570 pidConfigMutable()->pid_process_denom
= sbufReadU8(src
);
1571 motorConfigMutable()->dev
.useUnsyncedPwm
= sbufReadU8(src
);
1573 motorConfigMutable()->dev
.motorPwmProtocol
= constrain(sbufReadU8(src
), 0, PWM_TYPE_MAX
- 1);
1575 motorConfigMutable()->dev
.motorPwmProtocol
= constrain(sbufReadU8(src
), 0, PWM_TYPE_BRUSHED
);
1577 motorConfigMutable()->dev
.motorPwmRate
= sbufReadU16(src
);
1578 if (sbufBytesRemaining(src
) >= 2) {
1579 motorConfigMutable()->digitalIdleOffsetPercent
= sbufReadU16(src
) / 100.0f
;
1581 if (sbufBytesRemaining(src
)) {
1582 gyroConfigMutable()->gyro_use_32khz
= sbufReadU8(src
);
1584 //!!TODO gyro_isr_update to be added pending decision
1585 /*if (sbufBytesRemaining(src)) {
1586 gyroConfigMutable()->gyro_isr_update = sbufReadU8(src);
1588 validateAndFixGyroConfig();
1590 if (sbufBytesRemaining(src
)) {
1591 motorConfigMutable()->dev
.motorPwmInversion
= sbufReadU8(src
);
1595 case MSP_SET_FILTER_CONFIG
:
1596 gyroConfigMutable()->gyro_soft_lpf_hz
= sbufReadU8(src
);
1597 currentPidProfile
->dterm_lpf_hz
= sbufReadU16(src
);
1598 currentPidProfile
->yaw_lpf_hz
= sbufReadU16(src
);
1600 gyroConfigMutable()->gyro_soft_notch_hz_1
= sbufReadU16(src
);
1601 gyroConfigMutable()->gyro_soft_notch_cutoff_1
= sbufReadU16(src
);
1602 currentPidProfile
->dterm_notch_hz
= sbufReadU16(src
);
1603 currentPidProfile
->dterm_notch_cutoff
= sbufReadU16(src
);
1605 if (dataSize
> 13) {
1606 gyroConfigMutable()->gyro_soft_notch_hz_2
= sbufReadU16(src
);
1607 gyroConfigMutable()->gyro_soft_notch_cutoff_2
= sbufReadU16(src
);
1609 // reinitialize the gyro filters with the new values
1610 validateAndFixGyroConfig();
1612 // reinitialize the PID filters with the new values
1613 pidInitFilters(currentPidProfile
);
1616 case MSP_SET_PID_ADVANCED
:
1619 sbufReadU16(src
); // was pidProfile.yaw_p_limit
1620 sbufReadU8(src
); // reserved
1621 currentPidProfile
->vbatPidCompensation
= sbufReadU8(src
);
1622 currentPidProfile
->setpointRelaxRatio
= sbufReadU8(src
);
1623 currentPidProfile
->dtermSetpointWeight
= sbufReadU8(src
);
1624 sbufReadU8(src
); // reserved
1625 sbufReadU8(src
); // reserved
1626 sbufReadU8(src
); // reserved
1627 currentPidProfile
->rateAccelLimit
= sbufReadU16(src
) / 10.0f
;
1628 currentPidProfile
->yawRateAccelLimit
= sbufReadU16(src
) / 10.0f
;
1629 if (dataSize
> 17) {
1630 currentPidProfile
->levelAngleLimit
= sbufReadU8(src
);
1631 currentPidProfile
->levelSensitivity
= sbufReadU8(src
);
1633 pidInitConfig(currentPidProfile
);
1636 case MSP_SET_SENSOR_CONFIG
:
1637 accelerometerConfigMutable()->acc_hardware
= sbufReadU8(src
);
1638 barometerConfigMutable()->baro_hardware
= sbufReadU8(src
);
1639 compassConfigMutable()->mag_hardware
= sbufReadU8(src
);
1642 case MSP_RESET_CONF
:
1643 if (!ARMING_FLAG(ARMED
)) {
1649 case MSP_ACC_CALIBRATION
:
1650 if (!ARMING_FLAG(ARMED
))
1651 accSetCalibrationCycles(CALIBRATING_ACC_CYCLES
);
1654 case MSP_MAG_CALIBRATION
:
1655 if (!ARMING_FLAG(ARMED
))
1656 ENABLE_STATE(CALIBRATE_MAG
);
1659 case MSP_EEPROM_WRITE
:
1660 if (ARMING_FLAG(ARMED
)) {
1661 return MSP_RESULT_ERROR
;
1668 case MSP_SET_BLACKBOX_CONFIG
:
1669 // Don't allow config to be updated while Blackbox is logging
1670 if (blackboxMayEditConfig()) {
1671 blackboxConfigMutable()->device
= sbufReadU8(src
);
1672 blackboxConfigMutable()->rate_num
= sbufReadU8(src
);
1673 blackboxConfigMutable()->rate_denom
= sbufReadU8(src
);
1679 case MSP_SET_TRANSPONDER_CONFIG
:
1680 if (dataSize
!= sizeof(transponderConfig()->data
)) {
1681 return MSP_RESULT_ERROR
;
1683 for (unsigned int i
= 0; i
< sizeof(transponderConfig()->data
); i
++) {
1684 transponderConfigMutable()->data
[i
] = sbufReadU8(src
);
1686 transponderUpdateData();
1691 case MSP_SET_OSD_CONFIG
:
1693 const uint8_t addr
= sbufReadU8(src
);
1694 // set all the other settings
1695 if ((int8_t)addr
== -1) {
1697 vcdProfileMutable()->video_system
= sbufReadU8(src
);
1699 sbufReadU8(src
); // Skip video system
1701 osdConfigMutable()->units
= sbufReadU8(src
);
1702 osdConfigMutable()->rssi_alarm
= sbufReadU8(src
);
1703 osdConfigMutable()->cap_alarm
= sbufReadU16(src
);
1704 osdConfigMutable()->time_alarm
= sbufReadU16(src
);
1705 osdConfigMutable()->alt_alarm
= sbufReadU16(src
);
1707 // set a position setting
1708 const uint16_t pos
= sbufReadU16(src
);
1709 if (addr
< OSD_ITEM_COUNT
) {
1710 osdConfigMutable()->item_pos
[addr
] = pos
;
1715 case MSP_OSD_CHAR_WRITE
:
1718 uint8_t font_data
[64];
1719 const uint8_t addr
= sbufReadU8(src
);
1720 for (int i
= 0; i
< 54; i
++) {
1721 font_data
[i
] = sbufReadU8(src
);
1723 // !!TODO - replace this with a device independent implementation
1724 max7456WriteNvm(addr
, font_data
);
1727 // just discard the data
1729 for (int i
= 0; i
< 54; i
++) {
1736 #if defined(USE_RTC6705) || defined(VTX_COMMON)
1737 case MSP_SET_VTX_CONFIG
:
1739 uint16_t tmp
= sbufReadU16(src
);
1740 #if defined(USE_RTC6705)
1742 vtxConfigMutable()->vtx_channel
= tmp
;
1743 if (current_vtx_channel
!= vtxConfig()->vtx_channel
) {
1744 current_vtx_channel
= vtxConfig()->vtx_channel
;
1745 rtc6705_soft_spi_set_channel(vtx_freq
[current_vtx_channel
]);
1748 if (vtxCommonGetDeviceType() != VTXDEV_UNKNOWN
) {
1750 uint8_t band
= (tmp
/ 8) + 1;
1751 uint8_t channel
= (tmp
% 8) + 1;
1753 uint8_t current_band
=0, current_channel
=0;
1754 vtxCommonGetBandChan(¤t_band
,¤t_channel
);
1755 if ((current_band
!= band
) || (current_channel
!= channel
))
1756 vtxCommonSetBandChan(band
,channel
);
1758 if (sbufBytesRemaining(src
) < 2)
1761 uint8_t power
= sbufReadU8(src
);
1762 uint8_t current_power
= 0;
1763 vtxCommonGetPowerIndex(¤t_power
);
1764 if (current_power
!= power
)
1765 vtxCommonSetPowerByIndex(power
);
1767 uint8_t pitmode
= sbufReadU8(src
);
1768 uint8_t current_pitmode
= 0;
1769 vtxCommonGetPitmode(¤t_pitmode
);
1770 if (current_pitmode
!= pitmode
)
1771 vtxCommonSetPitmode(pitmode
);
1779 case MSP_DATAFLASH_ERASE
:
1780 flashfsEraseCompletely();
1785 case MSP_SET_RAW_GPS
:
1786 if (sbufReadU8(src
)) {
1787 ENABLE_STATE(GPS_FIX
);
1789 DISABLE_STATE(GPS_FIX
);
1791 GPS_numSat
= sbufReadU8(src
);
1792 GPS_coord
[LAT
] = sbufReadU32(src
);
1793 GPS_coord
[LON
] = sbufReadU32(src
);
1794 GPS_altitude
= sbufReadU16(src
);
1795 GPS_speed
= sbufReadU16(src
);
1796 GPS_update
|= 2; // New data signalisation to GPS functions // FIXME Magic Numbers
1799 wp_no
= sbufReadU8(src
); //get the wp number
1800 lat
= sbufReadU32(src
);
1801 lon
= sbufReadU32(src
);
1802 alt
= sbufReadU32(src
); // to set altitude (cm)
1803 sbufReadU16(src
); // future: to set heading (deg)
1804 sbufReadU16(src
); // future: to set time to stay (ms)
1805 sbufReadU8(src
); // future: to set nav flag
1807 GPS_home
[LAT
] = lat
;
1808 GPS_home
[LON
] = lon
;
1809 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
1810 ENABLE_STATE(GPS_FIX_HOME
);
1812 AltHold
= alt
; // temporary implementation to test feature with apps
1813 } 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
1814 GPS_hold
[LAT
] = lat
;
1815 GPS_hold
[LON
] = lon
;
1817 AltHold
= alt
; // temporary implementation to test feature with apps
1818 nav_mode
= NAV_MODE_WP
;
1819 GPS_set_next_wp(&GPS_hold
[LAT
], &GPS_hold
[LON
]);
1823 case MSP_SET_FEATURE
:
1825 featureSet(sbufReadU32(src
)); // features bitmap
1828 case MSP_SET_BOARD_ALIGNMENT
:
1829 boardAlignmentMutable()->rollDegrees
= sbufReadU16(src
);
1830 boardAlignmentMutable()->pitchDegrees
= sbufReadU16(src
);
1831 boardAlignmentMutable()->yawDegrees
= sbufReadU16(src
);
1834 case MSP_SET_VOLTAGE_METER_CONFIG
: {
1835 int id
= sbufReadU8(src
);
1838 // find and configure an ADC voltage sensor
1840 int voltageSensorADCIndex
;
1841 for (voltageSensorADCIndex
= 0; voltageSensorADCIndex
< MAX_VOLTAGE_SENSOR_ADC
; voltageSensorADCIndex
++) {
1842 if (id
== voltageMeterADCtoIDMap
[voltageSensorADCIndex
]) {
1847 if (voltageSensorADCIndex
< MAX_VOLTAGE_SENSOR_ADC
) {
1848 voltageSensorADCConfigMutable(voltageSensorADCIndex
)->vbatscale
= sbufReadU8(src
);
1849 voltageSensorADCConfigMutable(voltageSensorADCIndex
)->vbatresdivval
= sbufReadU8(src
);
1850 voltageSensorADCConfigMutable(voltageSensorADCIndex
)->vbatresdivmultiplier
= sbufReadU8(src
);
1852 // if we had any other types of voltage sensor to configure, this is where we'd do it.
1858 case MSP_SET_CURRENT_METER_CONFIG
: {
1859 int id
= sbufReadU8(src
);
1862 case CURRENT_METER_ID_BATTERY_1
:
1863 currentSensorADCConfigMutable()->scale
= sbufReadU16(src
);
1864 currentSensorADCConfigMutable()->offset
= sbufReadU16(src
);
1866 case CURRENT_METER_ID_VIRTUAL_1
:
1867 currentMeterVirtualConfigMutable()->scale
= sbufReadU16(src
);
1868 currentMeterVirtualConfigMutable()->offset
= sbufReadU16(src
);
1878 case MSP_SET_BATTERY_CONFIG
:
1879 batteryConfigMutable()->vbatmincellvoltage
= sbufReadU8(src
); // vbatlevel_warn1 in MWC2.3 GUI
1880 batteryConfigMutable()->vbatmaxcellvoltage
= sbufReadU8(src
); // vbatlevel_warn2 in MWC2.3 GUI
1881 batteryConfigMutable()->vbatwarningcellvoltage
= sbufReadU8(src
); // vbatlevel when buzzer starts to alert
1882 batteryConfigMutable()->batteryCapacity
= sbufReadU16(src
);
1883 batteryConfigMutable()->voltageMeterSource
= sbufReadU8(src
);
1884 batteryConfigMutable()->currentMeterSource
= sbufReadU8(src
);
1887 #ifndef USE_QUAD_MIXER_ONLY
1889 mixerConfigMutable()->mixerMode
= sbufReadU8(src
);
1893 case MSP_SET_RX_CONFIG
:
1894 rxConfigMutable()->serialrx_provider
= sbufReadU8(src
);
1895 rxConfigMutable()->maxcheck
= sbufReadU16(src
);
1896 rxConfigMutable()->midrc
= sbufReadU16(src
);
1897 rxConfigMutable()->mincheck
= sbufReadU16(src
);
1898 rxConfigMutable()->spektrum_sat_bind
= sbufReadU8(src
);
1900 rxConfigMutable()->rx_min_usec
= sbufReadU16(src
);
1901 rxConfigMutable()->rx_max_usec
= sbufReadU16(src
);
1903 if (dataSize
> 12) {
1904 rxConfigMutable()->rcInterpolation
= sbufReadU8(src
);
1905 rxConfigMutable()->rcInterpolationInterval
= sbufReadU8(src
);
1906 rxConfigMutable()->airModeActivateThreshold
= sbufReadU16(src
);
1908 if (dataSize
> 16) {
1909 rxConfigMutable()->rx_spi_protocol
= sbufReadU8(src
);
1910 rxConfigMutable()->rx_spi_id
= sbufReadU32(src
);
1911 rxConfigMutable()->rx_spi_rf_channel_count
= sbufReadU8(src
);
1913 if (dataSize
> 22) {
1914 rxConfigMutable()->fpvCamAngleDegrees
= sbufReadU8(src
);
1918 case MSP_SET_FAILSAFE_CONFIG
:
1919 failsafeConfigMutable()->failsafe_delay
= sbufReadU8(src
);
1920 failsafeConfigMutable()->failsafe_off_delay
= sbufReadU8(src
);
1921 failsafeConfigMutable()->failsafe_throttle
= sbufReadU16(src
);
1922 failsafeConfigMutable()->failsafe_kill_switch
= sbufReadU8(src
);
1923 failsafeConfigMutable()->failsafe_throttle_low_delay
= sbufReadU16(src
);
1924 failsafeConfigMutable()->failsafe_procedure
= sbufReadU8(src
);
1927 case MSP_SET_RXFAIL_CONFIG
:
1928 i
= sbufReadU8(src
);
1929 if (i
< MAX_SUPPORTED_RC_CHANNEL_COUNT
) {
1930 rxFailsafeChannelConfigsMutable(i
)->mode
= sbufReadU8(src
);
1931 rxFailsafeChannelConfigsMutable(i
)->step
= CHANNEL_VALUE_TO_RXFAIL_STEP(sbufReadU16(src
));
1933 return MSP_RESULT_ERROR
;
1937 case MSP_SET_RSSI_CONFIG
:
1938 rxConfigMutable()->rssi_channel
= sbufReadU8(src
);
1941 case MSP_SET_RX_MAP
:
1942 for (int i
= 0; i
< MAX_MAPPABLE_RX_INPUTS
; i
++) {
1943 rxConfigMutable()->rcmap
[i
] = sbufReadU8(src
);
1947 case MSP_SET_BF_CONFIG
:
1948 #ifdef USE_QUAD_MIXER_ONLY
1949 sbufReadU8(src
); // mixerMode ignored
1951 mixerConfigMutable()->mixerMode
= sbufReadU8(src
); // mixerMode
1955 featureSet(sbufReadU32(src
)); // features bitmap
1957 rxConfigMutable()->serialrx_provider
= sbufReadU8(src
); // serialrx_type
1959 boardAlignmentMutable()->rollDegrees
= sbufReadU16(src
); // board_align_roll
1960 boardAlignmentMutable()->pitchDegrees
= sbufReadU16(src
); // board_align_pitch
1961 boardAlignmentMutable()->yawDegrees
= sbufReadU16(src
); // board_align_yaw
1963 sbufReadU16(src
); // was currentMeterScale, see MSP_SET_CURRENT_METER_CONFIG
1964 sbufReadU16(src
); // was currentMeterOffset see MSP_SET_CURRENT_METER_CONFIG
1967 case MSP_SET_CF_SERIAL_CONFIG
:
1969 uint8_t portConfigSize
= sizeof(uint8_t) + sizeof(uint16_t) + (sizeof(uint8_t) * 4);
1971 if (dataSize
% portConfigSize
!= 0) {
1972 return MSP_RESULT_ERROR
;
1975 uint8_t remainingPortsInPacket
= dataSize
/ portConfigSize
;
1977 while (remainingPortsInPacket
--) {
1978 uint8_t identifier
= sbufReadU8(src
);
1980 serialPortConfig_t
*portConfig
= serialFindPortConfiguration(identifier
);
1982 return MSP_RESULT_ERROR
;
1985 portConfig
->identifier
= identifier
;
1986 portConfig
->functionMask
= sbufReadU16(src
);
1987 portConfig
->msp_baudrateIndex
= sbufReadU8(src
);
1988 portConfig
->gps_baudrateIndex
= sbufReadU8(src
);
1989 portConfig
->telemetry_baudrateIndex
= sbufReadU8(src
);
1990 portConfig
->blackbox_baudrateIndex
= sbufReadU8(src
);
1996 case MSP_SET_LED_COLORS
:
1997 for (int i
= 0; i
< LED_CONFIGURABLE_COLOR_COUNT
; i
++) {
1998 hsvColor_t
*color
= &ledStripConfigMutable()->colors
[i
];
1999 color
->h
= sbufReadU16(src
);
2000 color
->s
= sbufReadU8(src
);
2001 color
->v
= sbufReadU8(src
);
2005 case MSP_SET_LED_STRIP_CONFIG
:
2007 i
= sbufReadU8(src
);
2008 if (i
>= LED_MAX_STRIP_LENGTH
|| dataSize
!= (1 + 4)) {
2009 return MSP_RESULT_ERROR
;
2011 ledConfig_t
*ledConfig
= &ledStripConfigMutable()->ledConfigs
[i
];
2012 *ledConfig
= sbufReadU32(src
);
2013 reevaluateLedConfig();
2017 case MSP_SET_LED_STRIP_MODECOLOR
:
2019 ledModeIndex_e modeIdx
= sbufReadU8(src
);
2020 int funIdx
= sbufReadU8(src
);
2021 int color
= sbufReadU8(src
);
2023 if (!setModeColor(modeIdx
, funIdx
, color
))
2024 return MSP_RESULT_ERROR
;
2030 memset(systemConfigMutable()->name
, 0, ARRAYLEN(systemConfig()->name
));
2031 for (unsigned int i
= 0; i
< MIN(MAX_NAME_LENGTH
, dataSize
); i
++) {
2032 systemConfigMutable()->name
[i
] = sbufReadU8(src
);
2037 // we do not know how to handle the (valid) message, indicate error MSP $M!
2038 return MSP_RESULT_ERROR
;
2040 return MSP_RESULT_ACK
;
2044 * Returns MSP_RESULT_ACK, MSP_RESULT_ERROR or MSP_RESULT_NO_REPLY
2046 mspResult_e
mspFcProcessCommand(mspPacket_t
*cmd
, mspPacket_t
*reply
, mspPostProcessFnPtr
*mspPostProcessFn
)
2048 int ret
= MSP_RESULT_ACK
;
2049 sbuf_t
*dst
= &reply
->buf
;
2050 sbuf_t
*src
= &cmd
->buf
;
2051 const uint8_t cmdMSP
= cmd
->cmd
;
2052 // initialize reply by default
2053 reply
->cmd
= cmd
->cmd
;
2055 if (mspFcProcessOutCommand(cmdMSP
, dst
, mspPostProcessFn
)) {
2056 ret
= MSP_RESULT_ACK
;
2057 #ifdef USE_SERIAL_4WAY_BLHELI_INTERFACE
2058 } else if (cmdMSP
== MSP_SET_4WAY_IF
) {
2059 mspFc4waySerialCommand(dst
, src
, mspPostProcessFn
);
2060 ret
= MSP_RESULT_ACK
;
2063 } else if (cmdMSP
== MSP_WP
) {
2064 mspFcWpCommand(dst
, src
);
2065 ret
= MSP_RESULT_ACK
;
2068 } else if (cmdMSP
== MSP_DATAFLASH_READ
) {
2069 mspFcDataFlashReadCommand(dst
, src
);
2070 ret
= MSP_RESULT_ACK
;
2073 ret
= mspFcProcessInCommand(cmdMSP
, src
);
2075 reply
->result
= ret
;
2080 * Return a pointer to the process command function
2082 void mspFcInit(void)