Fix CHECK_ACTIVE_BOX/IS_ENABLED
[inav.git] / src / main / fc / fc_msp_box.c
blob9b0a90e54b1c5165ef4a085153dda87a4d907c0e
1 /*
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/>.
18 #include <stdbool.h>
19 #include <stdint.h>
20 #include <string.h>
22 #include "platform.h"
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"
34 #include "io/osd.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] = {
49 { BOXARM, "ARM", 0 },
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) {
108 return candidate;
111 return NULL;
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) {
119 return candidate;
122 return NULL;
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]);
131 if (box) {
132 replyLengthTotal += strlen(box->boxName) + BOX_SUFFIX_LEN;
136 // Check if we have enough space to send a reply
137 if (sbufBytesRemaining(dst) < replyLengthTotal) {
138 return false;
141 for (int i = 0; i < activeBoxIdCount; i++) {
142 const int activeBoxId = activeBoxIds[i];
143 const box_t *box = findBoxByActiveBoxId(activeBoxId);
144 if (box) {
145 const int len = strlen(box->boxName);
146 sbufWriteData(dst, box->boxName, len);
147 sbufWriteU8(dst, BOX_SUFFIX);
151 return true;
154 void serializeBoxReply(sbuf_t *dst)
156 for (int i = 0; i < activeBoxIdCount; i++) {
157 const box_t *box = findBoxByActiveBoxId(activeBoxIds[i]);
158 if (!box) {
159 continue;
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;
198 #ifdef USE_GPS
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;
234 #endif
236 #endif
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;
253 #endif
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;
269 #ifdef USE_LIGHTS
270 activeBoxIds[activeBoxIdCount++] = BOXLIGHTS;
271 #endif
273 #ifdef USE_LED_STRIP
274 if (feature(FEATURE_LED_STRIP)) {
275 activeBoxIds[activeBoxIdCount++] = BOXLEDLOW;
277 #endif
279 activeBoxIds[activeBoxIdCount++] = BOXOSD;
281 #ifdef USE_TELEMETRY
282 if (feature(FEATURE_TELEMETRY) && telemetryConfig()->telemetry_switch)
283 activeBoxIds[activeBoxIdCount++] = BOXTELEMETRY;
284 #endif
286 #ifdef USE_BLACKBOX
287 if (feature(FEATURE_BLACKBOX)){
288 activeBoxIds[activeBoxIdCount++] = BOXBLACKBOX;
290 #endif
292 activeBoxIds[activeBoxIdCount++] = BOXKILLSWITCH;
293 activeBoxIds[activeBoxIdCount++] = BOXFAILSAFE;
295 #ifdef USE_RCDEVICE
296 activeBoxIds[activeBoxIdCount++] = BOXCAMERA1;
297 activeBoxIds[activeBoxIdCount++] = BOXCAMERA2;
298 activeBoxIds[activeBoxIdCount++] = BOXCAMERA3;
299 #endif
301 #ifdef USE_PINIOBOX
302 // USER modes are only used for PINIO at the moment
303 activeBoxIds[activeBoxIdCount++] = BOXUSER1;
304 activeBoxIds[activeBoxIdCount++] = BOXUSER2;
305 #endif
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;
314 #endif
315 #endif
316 #endif
317 #endif
319 #if defined(USE_RX_MSP) && defined(USE_MSP_RC_OVERRIDE)
320 activeBoxIds[activeBoxIdCount++] = BOXMSPRCOVERRIDE;
321 #endif
323 #ifdef USE_DSHOT
324 if(STATE(MULTIROTOR) && isMotorProtocolDshot())
325 activeBoxIds[activeBoxIdCount++] = BOXTURTLE;
326 #endif
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);
366 #endif
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);
386 #endif
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)
399 // Sensor bits
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
415 return sensorStatus;