2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
24 #include "common/streambuf.h"
25 #include "common/utils.h"
27 #include "config/feature.h"
29 #include "fc/config.h"
30 #include "fc/fc_msp_box.h"
31 #include "fc/runtime_config.h"
32 #include "flight/mixer.h"
36 #include "drivers/pwm_output.h"
38 #include "sensors/diagnostics.h"
39 #include "sensors/sensors.h"
41 #include "navigation/navigation.h"
43 #include "telemetry/telemetry.h"
45 #define BOX_SUFFIX ';'
46 #define BOX_SUFFIX_LEN 1
48 static const box_t boxes
[CHECKBOX_ITEM_COUNT
+ 1] = {
50 { BOXANGLE
, "ANGLE", 1 },
51 { BOXHORIZON
, "HORIZON", 2 },
52 { BOXNAVALTHOLD
, "NAV ALTHOLD", 3 }, // old BARO
53 { BOXHEADINGHOLD
, "HEADING HOLD", 5 },
54 { BOXHEADFREE
, "HEADFREE", 6 },
55 { BOXHEADADJ
, "HEADADJ", 7 },
56 { BOXCAMSTAB
, "CAMSTAB", 8 },
57 { BOXNAVRTH
, "NAV RTH", 10 }, // old GPS HOME
58 { BOXNAVPOSHOLD
, "NAV POSHOLD", 11 }, // old GPS HOLD
59 { BOXMANUAL
, "MANUAL", 12 },
60 { BOXBEEPERON
, "BEEPER", 13 },
61 { BOXLEDLOW
, "LEDS OFF", 15 },
62 { BOXLIGHTS
, "LIGHTS", 16 },
63 { BOXOSD
, "OSD OFF", 19 },
64 { BOXTELEMETRY
, "TELEMETRY", 20 },
65 { BOXAUTOTUNE
, "AUTO TUNE", 21 },
66 { BOXBLACKBOX
, "BLACKBOX", 26 },
67 { BOXFAILSAFE
, "FAILSAFE", 27 },
68 { BOXNAVWP
, "NAV WP", 28 },
69 { BOXAIRMODE
, "AIR MODE", 29 },
70 { BOXHOMERESET
, "HOME RESET", 30 },
71 { BOXGCSNAV
, "GCS NAV", 31 },
72 { BOXFPVANGLEMIX
, "FPV ANGLE MIX", 32 },
73 { BOXSURFACE
, "SURFACE", 33 },
74 { BOXFLAPERON
, "FLAPERON", 34 },
75 { BOXTURNASSIST
, "TURN ASSIST", 35 },
76 { BOXNAVLAUNCH
, "NAV LAUNCH", 36 },
77 { BOXAUTOTRIM
, "SERVO AUTOTRIM", 37 },
78 { BOXKILLSWITCH
, "KILLSWITCH", 38 },
79 { BOXCAMERA1
, "CAMERA CONTROL 1", 39 },
80 { BOXCAMERA2
, "CAMERA CONTROL 2", 40 },
81 { BOXCAMERA3
, "CAMERA CONTROL 3", 41 },
82 { BOXOSDALT1
, "OSD ALT 1", 42 },
83 { BOXOSDALT2
, "OSD ALT 2", 43 },
84 { BOXOSDALT3
, "OSD ALT 3", 44 },
85 { BOXNAVCOURSEHOLD
, "NAV COURSE HOLD", 45 },
86 { BOXBRAKING
, "MC BRAKING", 46 },
87 { BOXUSER1
, "USER1", BOX_PERMANENT_ID_USER1
},
88 { BOXUSER2
, "USER2", BOX_PERMANENT_ID_USER2
},
89 { BOXLOITERDIRCHN
, "LOITER CHANGE", 49 },
90 { BOXMSPRCOVERRIDE
, "MSP RC OVERRIDE", 50 },
91 { BOXPREARM
, "PREARM", 51 },
92 { BOXTURTLE
, "TURTLE", 52 },
93 { BOXNAVCRUISE
, "NAV CRUISE", 53 },
94 { BOXAUTOLEVEL
, "AUTO LEVEL", 54 },
95 { CHECKBOX_ITEM_COUNT
, NULL
, 0xFF }
98 // this is calculated at startup based on enabled features.
99 static uint8_t activeBoxIds
[CHECKBOX_ITEM_COUNT
];
100 // this is the number of filled indexes in above array
101 uint8_t activeBoxIdCount
= 0;
103 const box_t
*findBoxByActiveBoxId(uint8_t activeBoxId
)
105 for (uint8_t boxIndex
= 0; boxIndex
< sizeof(boxes
) / sizeof(box_t
); boxIndex
++) {
106 const box_t
*candidate
= &boxes
[boxIndex
];
107 if (candidate
->boxId
== activeBoxId
) {
114 const box_t
*findBoxByPermanentId(uint8_t permenantId
)
116 for (uint8_t boxIndex
= 0; boxIndex
< sizeof(boxes
) / sizeof(box_t
); boxIndex
++) {
117 const box_t
*candidate
= &boxes
[boxIndex
];
118 if (candidate
->permanentId
== permenantId
) {
125 bool serializeBoxNamesReply(sbuf_t
*dst
)
127 // First run of the loop - calculate total length of the reply
128 int replyLengthTotal
= 0;
129 for (int i
= 0; i
< activeBoxIdCount
; i
++) {
130 const box_t
*box
= findBoxByActiveBoxId(activeBoxIds
[i
]);
132 replyLengthTotal
+= strlen(box
->boxName
) + BOX_SUFFIX_LEN
;
136 // Check if we have enough space to send a reply
137 if (sbufBytesRemaining(dst
) < replyLengthTotal
) {
141 for (int i
= 0; i
< activeBoxIdCount
; i
++) {
142 const int activeBoxId
= activeBoxIds
[i
];
143 const box_t
*box
= findBoxByActiveBoxId(activeBoxId
);
145 const int len
= strlen(box
->boxName
);
146 sbufWriteData(dst
, box
->boxName
, len
);
147 sbufWriteU8(dst
, BOX_SUFFIX
);
154 void serializeBoxReply(sbuf_t
*dst
)
156 for (int i
= 0; i
< activeBoxIdCount
; i
++) {
157 const box_t
*box
= findBoxByActiveBoxId(activeBoxIds
[i
]);
161 sbufWriteU8(dst
, box
->permanentId
);
165 void initActiveBoxIds(void)
167 // calculate used boxes based on features and fill availableBoxes[] array
168 memset(activeBoxIds
, 0xFF, sizeof(activeBoxIds
));
170 activeBoxIdCount
= 0;
171 activeBoxIds
[activeBoxIdCount
++] = BOXARM
;
172 activeBoxIds
[activeBoxIdCount
++] = BOXPREARM
;
174 if (sensors(SENSOR_ACC
) && STATE(ALTITUDE_CONTROL
)) {
175 activeBoxIds
[activeBoxIdCount
++] = BOXANGLE
;
176 activeBoxIds
[activeBoxIdCount
++] = BOXHORIZON
;
177 activeBoxIds
[activeBoxIdCount
++] = BOXTURNASSIST
;
180 if (!feature(FEATURE_AIRMODE
) && STATE(ALTITUDE_CONTROL
)) {
181 activeBoxIds
[activeBoxIdCount
++] = BOXAIRMODE
;
184 activeBoxIds
[activeBoxIdCount
++] = BOXHEADINGHOLD
;
186 if (sensors(SENSOR_ACC
) || sensors(SENSOR_MAG
)) {
187 activeBoxIds
[activeBoxIdCount
++] = BOXHEADFREE
;
188 activeBoxIds
[activeBoxIdCount
++] = BOXHEADADJ
;
191 if (STATE(ALTITUDE_CONTROL
)) {
192 activeBoxIds
[activeBoxIdCount
++] = BOXFPVANGLEMIX
;
195 //Camstab mode is enabled always
196 activeBoxIds
[activeBoxIdCount
++] = BOXCAMSTAB
;
199 if (STATE(ALTITUDE_CONTROL
) && (sensors(SENSOR_BARO
) || (feature(FEATURE_GPS
) && (STATE(AIRPLANE
) || positionEstimationConfig()->use_gps_no_baro
)))) {
200 activeBoxIds
[activeBoxIdCount
++] = BOXNAVALTHOLD
;
201 activeBoxIds
[activeBoxIdCount
++] = BOXSURFACE
;
204 const bool navReadyMultirotor
= STATE(MULTIROTOR
) && (getHwCompassStatus() != HW_SENSOR_NONE
) && sensors(SENSOR_ACC
) && feature(FEATURE_GPS
);
205 const bool navReadyOther
= !STATE(MULTIROTOR
) && sensors(SENSOR_ACC
) && feature(FEATURE_GPS
);
206 const bool navFlowDeadReckoning
= sensors(SENSOR_OPFLOW
) && sensors(SENSOR_ACC
) && positionEstimationConfig()->allow_dead_reckoning
;
207 if (navFlowDeadReckoning
|| navReadyMultirotor
|| navReadyOther
) {
208 if (!STATE(ROVER
) && !STATE(BOAT
)) {
209 activeBoxIds
[activeBoxIdCount
++] = BOXNAVPOSHOLD
;
211 if (STATE(AIRPLANE
)) {
212 activeBoxIds
[activeBoxIdCount
++] = BOXLOITERDIRCHN
;
216 if (navReadyMultirotor
|| navReadyOther
) {
217 activeBoxIds
[activeBoxIdCount
++] = BOXNAVRTH
;
218 activeBoxIds
[activeBoxIdCount
++] = BOXNAVWP
;
219 activeBoxIds
[activeBoxIdCount
++] = BOXHOMERESET
;
221 if (feature(FEATURE_GPS
)) {
222 activeBoxIds
[activeBoxIdCount
++] = BOXGCSNAV
;
223 if (STATE(AIRPLANE
)) {
224 activeBoxIds
[activeBoxIdCount
++] = BOXNAVCOURSEHOLD
;
225 activeBoxIds
[activeBoxIdCount
++] = BOXNAVCRUISE
;
230 #ifdef USE_MR_BRAKING_MODE
231 if (mixerConfig()->platformType
== PLATFORM_MULTIROTOR
) {
232 activeBoxIds
[activeBoxIdCount
++] = BOXBRAKING
;
238 if (STATE(AIRPLANE
) || STATE(ROVER
) || STATE(BOAT
)) {
239 activeBoxIds
[activeBoxIdCount
++] = BOXMANUAL
;
242 if (STATE(AIRPLANE
)) {
243 if (!feature(FEATURE_FW_LAUNCH
)) {
244 activeBoxIds
[activeBoxIdCount
++] = BOXNAVLAUNCH
;
247 if (!feature(FEATURE_FW_AUTOTRIM
)) {
248 activeBoxIds
[activeBoxIdCount
++] = BOXAUTOTRIM
;
251 #if defined(USE_AUTOTUNE_FIXED_WING)
252 activeBoxIds
[activeBoxIdCount
++] = BOXAUTOTUNE
;
254 if (sensors(SENSOR_BARO
)) {
255 activeBoxIds
[activeBoxIdCount
++] = BOXAUTOLEVEL
;
260 * FLAPERON mode active only in case of airplane and custom airplane. Activating on
261 * flying wing can cause bad thing
263 if (STATE(FLAPERON_AVAILABLE
)) {
264 activeBoxIds
[activeBoxIdCount
++] = BOXFLAPERON
;
267 activeBoxIds
[activeBoxIdCount
++] = BOXBEEPERON
;
270 activeBoxIds
[activeBoxIdCount
++] = BOXLIGHTS
;
274 if (feature(FEATURE_LED_STRIP
)) {
275 activeBoxIds
[activeBoxIdCount
++] = BOXLEDLOW
;
279 activeBoxIds
[activeBoxIdCount
++] = BOXOSD
;
282 if (feature(FEATURE_TELEMETRY
) && telemetryConfig()->telemetry_switch
)
283 activeBoxIds
[activeBoxIdCount
++] = BOXTELEMETRY
;
287 if (feature(FEATURE_BLACKBOX
)){
288 activeBoxIds
[activeBoxIdCount
++] = BOXBLACKBOX
;
292 activeBoxIds
[activeBoxIdCount
++] = BOXKILLSWITCH
;
293 activeBoxIds
[activeBoxIdCount
++] = BOXFAILSAFE
;
296 activeBoxIds
[activeBoxIdCount
++] = BOXCAMERA1
;
297 activeBoxIds
[activeBoxIdCount
++] = BOXCAMERA2
;
298 activeBoxIds
[activeBoxIdCount
++] = BOXCAMERA3
;
302 // USER modes are only used for PINIO at the moment
303 activeBoxIds
[activeBoxIdCount
++] = BOXUSER1
;
304 activeBoxIds
[activeBoxIdCount
++] = BOXUSER2
;
307 #if defined(USE_OSD) && defined(OSD_LAYOUT_COUNT)
308 #if OSD_LAYOUT_COUNT > 0
309 activeBoxIds
[activeBoxIdCount
++] = BOXOSDALT1
;
310 #if OSD_LAYOUT_COUNT > 1
311 activeBoxIds
[activeBoxIdCount
++] = BOXOSDALT2
;
312 #if OSD_LAYOUT_COUNT > 2
313 activeBoxIds
[activeBoxIdCount
++] = BOXOSDALT3
;
319 #if defined(USE_RX_MSP) && defined(USE_MSP_RC_OVERRIDE)
320 activeBoxIds
[activeBoxIdCount
++] = BOXMSPRCOVERRIDE
;
324 if(STATE(MULTIROTOR
) && isMotorProtocolDshot())
325 activeBoxIds
[activeBoxIdCount
++] = BOXTURTLE
;
329 #define IS_ENABLED(mask) ((mask) == 0 ? 0 : 1)
330 #define CHECK_ACTIVE_BOX(condition, index) do { if (IS_ENABLED(condition)) { activeBoxes[index] = 1; } } while(0)
332 void packBoxModeFlags(boxBitmask_t
* mspBoxModeFlags
)
334 uint8_t activeBoxes
[CHECKBOX_ITEM_COUNT
];
335 memset(activeBoxes
, 0, sizeof(activeBoxes
));
337 // Serialize the flags in the order we delivered them, ignoring BOXNAMES and BOXINDEXES
338 // Requires new Multiwii protocol version to fix
339 // It would be preferable to setting the enabled bits based on BOXINDEX.
340 CHECK_ACTIVE_BOX(IS_ENABLED(FLIGHT_MODE(ANGLE_MODE
)), BOXANGLE
);
341 CHECK_ACTIVE_BOX(IS_ENABLED(FLIGHT_MODE(HORIZON_MODE
)), BOXHORIZON
);
342 CHECK_ACTIVE_BOX(IS_ENABLED(FLIGHT_MODE(HEADING_MODE
)), BOXHEADINGHOLD
);
343 CHECK_ACTIVE_BOX(IS_ENABLED(FLIGHT_MODE(HEADFREE_MODE
)), BOXHEADFREE
);
344 CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXHEADADJ
)), BOXHEADADJ
);
345 CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXCAMSTAB
)), BOXCAMSTAB
);
346 CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXFPVANGLEMIX
)), BOXFPVANGLEMIX
);
347 CHECK_ACTIVE_BOX(IS_ENABLED(FLIGHT_MODE(MANUAL_MODE
)), BOXMANUAL
);
348 CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXBEEPERON
)), BOXBEEPERON
);
349 CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXLEDLOW
)), BOXLEDLOW
);
350 CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXLIGHTS
)), BOXLIGHTS
);
351 CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXOSD
)), BOXOSD
);
352 CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXTELEMETRY
)), BOXTELEMETRY
);
353 CHECK_ACTIVE_BOX(IS_ENABLED(ARMING_FLAG(ARMED
)), BOXARM
);
354 CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXBLACKBOX
)), BOXBLACKBOX
);
355 CHECK_ACTIVE_BOX(IS_ENABLED(FLIGHT_MODE(FAILSAFE_MODE
)), BOXFAILSAFE
);
356 CHECK_ACTIVE_BOX(IS_ENABLED(FLIGHT_MODE(NAV_ALTHOLD_MODE
)), BOXNAVALTHOLD
);
357 CHECK_ACTIVE_BOX(IS_ENABLED(FLIGHT_MODE(NAV_POSHOLD_MODE
)), BOXNAVPOSHOLD
);
358 CHECK_ACTIVE_BOX(IS_ENABLED(FLIGHT_MODE(NAV_COURSE_HOLD_MODE
)), BOXNAVCOURSEHOLD
);
359 CHECK_ACTIVE_BOX(IS_ENABLED(FLIGHT_MODE(NAV_COURSE_HOLD_MODE
)) && IS_ENABLED(FLIGHT_MODE(NAV_ALTHOLD_MODE
)), BOXNAVCRUISE
);
360 CHECK_ACTIVE_BOX(IS_ENABLED(FLIGHT_MODE(NAV_RTH_MODE
)), BOXNAVRTH
);
361 CHECK_ACTIVE_BOX(IS_ENABLED(FLIGHT_MODE(NAV_WP_MODE
)), BOXNAVWP
);
362 CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXAIRMODE
)), BOXAIRMODE
);
363 CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXGCSNAV
)), BOXGCSNAV
);
364 #ifdef USE_FLM_FLAPERON
365 CHECK_ACTIVE_BOX(IS_ENABLED(FLIGHT_MODE(FLAPERON
)), BOXFLAPERON
);
367 CHECK_ACTIVE_BOX(IS_ENABLED(FLIGHT_MODE(TURN_ASSISTANT
)), BOXTURNASSIST
);
368 CHECK_ACTIVE_BOX(IS_ENABLED(FLIGHT_MODE(NAV_LAUNCH_MODE
)), BOXNAVLAUNCH
);
369 CHECK_ACTIVE_BOX(IS_ENABLED(FLIGHT_MODE(AUTO_TUNE
)), BOXAUTOTUNE
);
370 CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXAUTOTRIM
)), BOXAUTOTRIM
);
371 CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXKILLSWITCH
)), BOXKILLSWITCH
);
372 CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXHOMERESET
)), BOXHOMERESET
);
373 CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXCAMERA1
)), BOXCAMERA1
);
374 CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXCAMERA2
)), BOXCAMERA2
);
375 CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXCAMERA3
)), BOXCAMERA3
);
376 CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXOSDALT1
)), BOXOSDALT1
);
377 CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXOSDALT2
)), BOXOSDALT2
);
378 CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXOSDALT3
)), BOXOSDALT3
);
379 CHECK_ACTIVE_BOX(IS_ENABLED(navigationTerrainFollowingEnabled()), BOXSURFACE
);
380 CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXBRAKING
)), BOXBRAKING
);
381 CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXUSER1
)), BOXUSER1
);
382 CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXUSER2
)), BOXUSER2
);
383 CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXLOITERDIRCHN
)), BOXLOITERDIRCHN
);
384 #if defined(USE_RX_MSP) && defined(USE_MSP_RC_OVERRIDE)
385 CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXMSPRCOVERRIDE
)), BOXMSPRCOVERRIDE
);
387 CHECK_ACTIVE_BOX(IS_ENABLED(IS_RC_MODE_ACTIVE(BOXAUTOLEVEL
)), BOXAUTOLEVEL
);
389 memset(mspBoxModeFlags
, 0, sizeof(boxBitmask_t
));
390 for (uint32_t i
= 0; i
< activeBoxIdCount
; i
++) {
391 if (activeBoxes
[activeBoxIds
[i
]]) {
392 bitArraySet(mspBoxModeFlags
->bits
, i
);
397 uint16_t packSensorStatus(void)
400 uint16_t sensorStatus
=
401 IS_ENABLED(sensors(SENSOR_ACC
)) << 0 |
402 IS_ENABLED(sensors(SENSOR_BARO
)) << 1 |
403 IS_ENABLED(sensors(SENSOR_MAG
)) << 2 |
404 IS_ENABLED(sensors(SENSOR_GPS
)) << 3 |
405 IS_ENABLED(sensors(SENSOR_RANGEFINDER
)) << 4 |
406 IS_ENABLED(sensors(SENSOR_OPFLOW
)) << 5 |
407 IS_ENABLED(sensors(SENSOR_PITOT
)) << 6 |
408 IS_ENABLED(sensors(SENSOR_TEMP
)) << 7;
410 // Hardware failure indication bit
411 if (!isHardwareHealthy()) {
412 sensorStatus
|= 1 << 15; // Bit 15 of sensor bit field indicates hardware failure