Xlite PPM/Heartbeat trainer input (#5881)
[opentx.git] / radio / src / gui / gui_common_arm.cpp
blobb3026e3524764428c0cefe3d1b1e2a88d426d4ec
1 /*
2 * Copyright (C) OpenTX
4 * Based on code named
5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
21 #include "opentx.h"
23 int circularIncDec(int current, int inc, int min, int max, IsValueAvailable isValueAvailable)
25 do {
26 current += inc;
27 if (current < min)
28 current = max;
29 else if (current > max)
30 current = min;
31 if (!isValueAvailable || isValueAvailable(current))
32 return current;
33 } while(1);
34 return 0;
37 bool isInputAvailable(int input)
39 for (int i=0; i<MAX_EXPOS; i++) {
40 ExpoData * expo = expoAddress(i);
41 if (!EXPO_VALID(expo))
42 break;
43 if (expo->chn == input)
44 return true;
46 return false;
49 bool isSensorAvailable(int sensor)
51 if (sensor == 0)
52 return true;
53 else
54 return isTelemetryFieldAvailable(abs(sensor) - 1);
57 bool isSensorUnit(int sensor, uint8_t unit)
59 if (sensor <= 0 || sensor > MAX_TELEMETRY_SENSORS ) {
60 return true;
62 else {
63 return g_model.telemetrySensors[sensor-1].unit == unit;
67 bool isCellsSensor(int sensor)
69 return isSensorUnit(sensor, UNIT_CELLS);
72 bool isGPSSensor(int sensor)
74 return isSensorUnit(sensor, UNIT_GPS);
77 bool isAltSensor(int sensor)
79 return isSensorUnit(sensor, UNIT_DIST) || isSensorUnit(sensor, UNIT_FEET);
82 bool isVoltsSensor(int sensor)
84 return isSensorUnit(sensor, UNIT_VOLTS) || isSensorUnit(sensor, UNIT_CELLS);
87 bool isCurrentSensor(int sensor)
89 return isSensorUnit(sensor, UNIT_AMPS);
92 bool isTelemetryFieldAvailable(int index)
94 TelemetrySensor & sensor = g_model.telemetrySensors[index];
95 return sensor.isAvailable();
98 bool isTelemetryFieldComparisonAvailable(int index)
100 if (!isTelemetryFieldAvailable(index))
101 return false;
103 TelemetrySensor & sensor = g_model.telemetrySensors[index];
104 if (sensor.unit >= UNIT_DATETIME)
105 return false;
106 return true;
109 bool isChannelUsed(int channel)
111 for (int i=0; i<MAX_MIXERS; ++i) {
112 MixData *md = mixAddress(i);
113 if (md->srcRaw == 0) return false;
114 if (md->destCh == channel) return true;
115 if (md->destCh > channel) return false;
117 return false;
120 int getChannelsUsed()
122 int result = 0;
123 int lastCh = -1;
124 for (int i=0; i<MAX_MIXERS; ++i) {
125 MixData *md = mixAddress(i);
126 if (md->srcRaw == 0) return result;
127 if (md->destCh != lastCh) { ++result; lastCh = md->destCh; }
129 return result;
132 bool isSourceAvailable(int source)
134 if (source < 0)
135 return false;
137 if (source>=MIXSRC_FIRST_INPUT && source<=MIXSRC_LAST_INPUT) {
138 return isInputAvailable(source - MIXSRC_FIRST_INPUT);
141 #if defined(LUA_MODEL_SCRIPTS)
142 if (source>=MIXSRC_FIRST_LUA && source<=MIXSRC_LAST_LUA) {
143 div_t qr = div(source-MIXSRC_FIRST_LUA, MAX_SCRIPT_OUTPUTS);
144 return (qr.rem<scriptInputsOutputs[qr.quot].outputsCount);
146 #elif defined(LUA_INPUTS)
147 if (source>=MIXSRC_FIRST_LUA && source<=MIXSRC_LAST_LUA)
148 return false;
149 #endif
151 if (source>=MIXSRC_FIRST_POT && source<=MIXSRC_LAST_POT) {
152 return IS_POT_SLIDER_AVAILABLE(POT1+source-MIXSRC_FIRST_POT);
155 #if defined(PCBSKY9X) && defined(REVX)
156 if (source == MIXSRC_REa) {
157 return false;
159 #endif
161 #if defined(PCBX10)
162 if ((source>=MIXSRC_S3 && source<=MIXSRC_S4) || (source>=MIXSRC_MOUSE1 && source<=MIXSRC_MOUSE2))
163 return false;
164 #endif
166 if (source>=MIXSRC_FIRST_SWITCH && source<=MIXSRC_LAST_SWITCH) {
167 return SWITCH_EXISTS(source-MIXSRC_FIRST_SWITCH);
170 #if !defined(HELI)
171 if (source>=MIXSRC_CYC1 && source<=MIXSRC_CYC3)
172 return false;
173 #endif
175 if (source>=MIXSRC_FIRST_CH && source<=MIXSRC_LAST_CH) {
176 return isChannelUsed(source-MIXSRC_FIRST_CH);
179 if (source>=MIXSRC_FIRST_LOGICAL_SWITCH && source<=MIXSRC_LAST_LOGICAL_SWITCH) {
180 LogicalSwitchData * cs = lswAddress(source-MIXSRC_FIRST_LOGICAL_SWITCH);
181 return (cs->func != LS_FUNC_NONE);
184 #if !defined(GVARS)
185 if (source>=MIXSRC_GVAR1 && source<=MIXSRC_LAST_GVAR)
186 return false;
187 #endif
189 if (source>=MIXSRC_FIRST_RESERVE && source<=MIXSRC_LAST_RESERVE)
190 return false;
192 if (source>=MIXSRC_FIRST_TELEM && source<=MIXSRC_LAST_TELEM) {
193 div_t qr = div(source-MIXSRC_FIRST_TELEM, 3);
194 if (qr.rem == 0)
195 return isTelemetryFieldAvailable(qr.quot);
196 else
197 return isTelemetryFieldComparisonAvailable(qr.quot);
200 return true;
203 bool isSourceAvailableInGlobalFunctions(int source)
205 if (source>=MIXSRC_FIRST_TELEM && source<=MIXSRC_LAST_TELEM) {
206 return false;
208 return isSourceAvailable(source);
211 bool isSourceAvailableInCustomSwitches(int source)
213 bool result = isSourceAvailable(source);
215 #if defined(TELEMETRY_FRSKY)
216 if (result && source>=MIXSRC_FIRST_TELEM && source<=MIXSRC_LAST_TELEM) {
217 div_t qr = div(source-MIXSRC_FIRST_TELEM, 3);
218 result = isTelemetryFieldComparisonAvailable(qr.quot);
220 #endif
222 return result;
225 bool isInputSourceAvailable(int source)
227 if (source>=MIXSRC_FIRST_POT && source<=MIXSRC_LAST_POT) {
228 return IS_POT_SLIDER_AVAILABLE(POT1+source-MIXSRC_FIRST_POT);
231 if (source>=MIXSRC_Rud && source<=MIXSRC_MAX)
232 return true;
234 if (source>=MIXSRC_FIRST_TRIM && source<=MIXSRC_LAST_TRIM)
235 return true;
237 if (source>=MIXSRC_FIRST_SWITCH && source<=MIXSRC_LAST_SWITCH)
238 return SWITCH_EXISTS(source-MIXSRC_FIRST_SWITCH);
240 if (source>=MIXSRC_FIRST_CH && source<=MIXSRC_LAST_CH)
241 return true;
243 if (source>=MIXSRC_FIRST_LOGICAL_SWITCH && source<=MIXSRC_LAST_LOGICAL_SWITCH) {
244 LogicalSwitchData * cs = lswAddress(source-MIXSRC_SW1);
245 return (cs->func != LS_FUNC_NONE);
248 if (source>=MIXSRC_FIRST_TRAINER && source<=MIXSRC_LAST_TRAINER)
249 return true;
251 if (source>=MIXSRC_FIRST_TELEM && source<=MIXSRC_LAST_TELEM) {
252 div_t qr = div(source-MIXSRC_FIRST_TELEM, 3);
253 return isTelemetryFieldAvailable(qr.quot) && isTelemetryFieldComparisonAvailable(qr.quot);
256 return false;
259 enum SwitchContext
261 LogicalSwitchesContext,
262 ModelCustomFunctionsContext,
263 GeneralCustomFunctionsContext,
264 TimersContext,
265 MixesContext
268 bool isLogicalSwitchAvailable(int index)
270 LogicalSwitchData * lsw = lswAddress(index);
271 return (lsw->func != LS_FUNC_NONE);
274 bool isSwitchAvailable(int swtch, SwitchContext context)
276 bool negative = false;
278 if (swtch < 0) {
279 if (swtch == -SWSRC_ON || swtch == -SWSRC_ONE) {
280 return false;
282 negative = true;
283 swtch = -swtch;
286 #if defined(PCBSKY9X)
287 if (swtch >= SWSRC_FIRST_SWITCH && swtch <= SWSRC_LAST_SWITCH) {
288 UNUSED(negative);
289 return true;
291 #else
292 if (swtch >= SWSRC_FIRST_SWITCH && swtch <= SWSRC_LAST_SWITCH) {
293 div_t swinfo = switchInfo(swtch);
294 if (!SWITCH_EXISTS(swinfo.quot)) {
295 return false;
297 if (!IS_CONFIG_3POS(swinfo.quot)) {
298 if (negative) {
299 return false;
301 if (swinfo.rem == 1) {
302 // mid position not available for 2POS switches
303 return false;
306 return true;
308 #endif
310 #if NUM_XPOTS > 0
311 if (swtch >= SWSRC_FIRST_MULTIPOS_SWITCH && swtch <= SWSRC_LAST_MULTIPOS_SWITCH) {
312 int index = (swtch - SWSRC_FIRST_MULTIPOS_SWITCH) / XPOTS_MULTIPOS_COUNT;
313 if (IS_POT_MULTIPOS(POT1+index)) {
314 StepsCalibData * calib = (StepsCalibData *) &g_eeGeneral.calib[POT1+index];
315 return (calib->count >= ((swtch - SWSRC_FIRST_MULTIPOS_SWITCH) % XPOTS_MULTIPOS_COUNT));
317 else {
318 return false;
321 #endif
323 #if defined(PCBSKY9X) && defined(REVX)
324 if (swtch == SWSRC_REa) {
325 return false;
327 #endif
329 if (swtch >= SWSRC_FIRST_LOGICAL_SWITCH && swtch <= SWSRC_LAST_LOGICAL_SWITCH) {
330 if (context == GeneralCustomFunctionsContext) {
331 return false;
333 else if (context != LogicalSwitchesContext) {
334 return isLogicalSwitchAvailable(swtch - SWSRC_FIRST_LOGICAL_SWITCH);
338 if (context != ModelCustomFunctionsContext && context != GeneralCustomFunctionsContext && (swtch == SWSRC_ON || swtch == SWSRC_ONE)) {
339 return false;
342 if (swtch >= SWSRC_FIRST_FLIGHT_MODE && swtch <= SWSRC_LAST_FLIGHT_MODE) {
343 if (context == MixesContext || context == GeneralCustomFunctionsContext) {
344 return false;
346 else {
347 swtch -= SWSRC_FIRST_FLIGHT_MODE;
348 if (swtch == 0) {
349 return true;
351 FlightModeData * fm = flightModeAddress(swtch);
352 return (fm->swtch != SWSRC_NONE);
356 if (swtch >= SWSRC_FIRST_SENSOR && swtch <= SWSRC_LAST_SENSOR) {
357 if (context == GeneralCustomFunctionsContext)
358 return false;
359 else
360 return isTelemetryFieldAvailable(swtch - SWSRC_FIRST_SENSOR);
363 return true;
366 bool isSwitchAvailableInLogicalSwitches(int swtch)
368 return isSwitchAvailable(swtch, LogicalSwitchesContext);
371 bool isSwitchAvailableInCustomFunctions(int swtch)
373 if (menuHandlers[menuLevel] == menuModelSpecialFunctions)
374 return isSwitchAvailable(swtch, ModelCustomFunctionsContext);
375 else
376 return isSwitchAvailable(swtch, GeneralCustomFunctionsContext);
379 bool isSwitchAvailableInMixes(int swtch)
381 return isSwitchAvailable(swtch, MixesContext);
384 #if defined(COLORLCD)
385 bool isSwitch2POSWarningStateAvailable(int state)
387 return (state != 2); // two pos switch - middle state not available
389 #endif // #if defined(COLORLCD)
391 bool isSwitchAvailableInTimers(int swtch)
393 if (swtch >= 0) {
394 if (swtch < TMRMODE_COUNT)
395 return true;
396 else
397 swtch -= TMRMODE_COUNT-1;
399 else {
400 if (swtch > -TMRMODE_COUNT)
401 return false;
402 else
403 swtch += TMRMODE_COUNT-1;
406 return isSwitchAvailable(swtch, TimersContext);
409 bool isThrottleSourceAvailable(int source)
411 if (source >= THROTTLE_SOURCE_FIRST_POT && source < THROTTLE_SOURCE_FIRST_POT+NUM_POTS+NUM_SLIDERS && !IS_POT_SLIDER_AVAILABLE(POT1+source-THROTTLE_SOURCE_FIRST_POT))
412 return false;
413 else
414 return true;
417 bool isLogicalSwitchFunctionAvailable(int function)
419 return function != LS_FUNC_RANGE;
422 bool isAssignableFunctionAvailable(int function)
424 #if defined(OVERRIDE_CHANNEL_FUNCTION) || defined(GVARS)
425 bool modelFunctions = (menuHandlers[menuLevel] == menuModelSpecialFunctions);
426 #endif
428 switch (function) {
429 case FUNC_OVERRIDE_CHANNEL:
430 #if defined(OVERRIDE_CHANNEL_FUNCTION)
431 return modelFunctions;
432 #else
433 return false;
434 #endif
435 case FUNC_ADJUST_GVAR:
436 #if defined(GVARS)
437 return modelFunctions;
438 #else
439 return false;
440 #endif
441 #if !defined(HAPTIC)
442 case FUNC_HAPTIC:
443 #endif
444 case FUNC_RESERVE4:
445 #if !defined(DANGEROUS_MODULE_FUNCTIONS)
446 case FUNC_RANGECHECK:
447 case FUNC_BIND:
448 #endif
449 #if !defined(LUA)
450 case FUNC_PLAY_SCRIPT:
451 #endif
452 case FUNC_RESERVE5:
453 return false;
455 default:
456 return true;
460 bool isSourceAvailableInGlobalResetSpecialFunction(int index)
462 if (index >= FUNC_RESET_PARAM_FIRST_TELEM)
463 return false;
464 else
465 return isSourceAvailableInResetSpecialFunction(index);
468 bool isSourceAvailableInResetSpecialFunction(int index)
470 if (index >= FUNC_RESET_PARAM_FIRST_TELEM) {
471 TelemetrySensor & telemetrySensor = g_model.telemetrySensors[index-FUNC_RESET_PARAM_FIRST_TELEM];
472 return telemetrySensor.isAvailable();
474 #if TIMERS < 3
475 else if (index == FUNC_RESET_TIMER3) {
476 return false;
478 #endif
479 #if TIMERS < 2
480 else if (index == FUNC_RESET_TIMER2) {
481 return false;
483 #endif
484 else {
485 return true;
490 bool isModuleAvailable(int module)
492 #if defined(CROSSFIRE)
493 if (module == MODULE_TYPE_CROSSFIRE && g_model.moduleData[INTERNAL_MODULE].type != MODULE_TYPE_NONE) {
494 return false;
496 #else
497 if (module == MODULE_TYPE_CROSSFIRE) {
498 return false;
500 #endif
501 #if !defined(DSM2)
502 if (module == MODULE_TYPE_DSM2) {
503 return false;
505 #endif
506 #if !defined(MULTIMODULE)
507 if (module == MODULE_TYPE_MULTIMODULE) {
508 return false;
510 #endif
511 return true;
514 bool isRfProtocolAvailable(int protocol)
516 #if defined(CROSSFIRE)
517 if (protocol != RF_PROTO_OFF && g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_CROSSFIRE) {
518 return false;
520 #endif
521 #if defined(MODULE_D16_EU_ONLY_SUPPORT)
522 if (protocol == RF_PROTO_D8) {
523 return false;
525 #endif
526 return true;
529 bool isTelemetryProtocolAvailable(int protocol)
531 #if defined(PCBTARANIS)
532 if (protocol == PROTOCOL_FRSKY_D_SECONDARY && g_eeGeneral.serial2Mode != UART_MODE_TELEMETRY) {
533 return false;
535 #endif
537 if (protocol== PROTOCOL_PULSES_CROSSFIRE) {
538 return false;
541 #if !defined(MULTIMODULE)
542 if (protocol == PROTOCOL_SPEKTRUM || protocol == PROTOCOL_FLYSKY_IBUS || protocol == PROTOCOL_MULTIMODULE) {
543 return false;
545 #endif
547 #if defined(PCBHORUS)
548 if (protocol == PROTOCOL_FRSKY_D_SECONDARY) {
549 return false;
551 #endif
553 return true;
556 #if defined(PCBHORUS)
557 bool isTrainerModeAvailable(int mode)
559 return true;
561 #elif defined(PCBX9E)
562 bool isTrainerModeAvailable(int mode)
564 if (IS_EXTERNAL_MODULE_ENABLED() && (mode == TRAINER_MODE_MASTER_SBUS_EXTERNAL_MODULE || mode == TRAINER_MODE_MASTER_CPPM_EXTERNAL_MODULE))
565 return false;
566 #if defined(USEHORUSBT)
567 else if (mode == TRAINER_MODE_MASTER_BATTERY_COMPARTMENT)
568 #else
569 else if (mode == TRAINER_MODE_MASTER_BLUETOOTH || mode == TRAINER_MODE_MASTER_BATTERY_COMPARTMENT || mode == TRAINER_MODE_SLAVE_BLUETOOTH)
570 #endif
571 return false;
572 else
573 return true;
575 #elif defined(PCBX9)
576 bool isTrainerModeAvailable(int mode)
578 if (IS_EXTERNAL_MODULE_ENABLED() && (mode == TRAINER_MODE_MASTER_SBUS_EXTERNAL_MODULE || mode == TRAINER_MODE_MASTER_CPPM_EXTERNAL_MODULE))
579 return false;
580 else
581 return true;
583 #elif defined(PCBX7)
584 bool isTrainerModeAvailable(int mode)
586 if (IS_EXTERNAL_MODULE_ENABLED() && (mode == TRAINER_MODE_MASTER_SBUS_EXTERNAL_MODULE || mode == TRAINER_MODE_MASTER_CPPM_EXTERNAL_MODULE))
587 return false;
588 else if (mode == TRAINER_MODE_MASTER_BATTERY_COMPARTMENT)
589 return false;
590 #if defined(BLUETOOTH)
591 else if (g_eeGeneral.bluetoothMode != BLUETOOTH_TRAINER && (mode == TRAINER_MODE_MASTER_BLUETOOTH || mode == TRAINER_MODE_SLAVE_BLUETOOTH))
592 return false;
593 #endif
594 else
595 return true;
597 #elif defined(PCBXLITE)
598 bool isTrainerModeAvailable(int mode)
600 if ((g_eeGeneral.bluetoothMode == BLUETOOTH_TRAINER && (mode == TRAINER_MODE_MASTER_BLUETOOTH || mode == TRAINER_MODE_SLAVE_BLUETOOTH)) || mode == TRAINER_MODE_MASTER_CPPM_EXTERNAL_MODULE)
601 return true;
602 else
603 return false;
605 #endif
607 bool modelHasNotes()
609 char filename[sizeof(MODELS_PATH)+1+sizeof(g_model.header.name)+sizeof(TEXT_EXT)] = MODELS_PATH "/";
610 char *buf = strcat_currentmodelname(&filename[sizeof(MODELS_PATH)]);
611 strcpy(buf, TEXT_EXT);
612 if (isFileAvailable(filename)) {
613 return true;
616 #if !defined(EEPROM)
617 buf = strAppendFilename(&filename[sizeof(MODELS_PATH)], g_eeGeneral.currModelFilename, LEN_MODEL_FILENAME);
618 strcpy(buf, TEXT_EXT);
619 if (isFileAvailable(filename)) {
620 return true;
622 #endif
624 return false;
627 int getFirstAvailable(int min, int max, IsValueAvailable isValueAvailable)
629 int retval = 0;
630 for (int i = min; i <= max; i++) {
631 if (isValueAvailable(i)) {
632 retval = i;
633 break;
636 return retval;
638 #if defined(MULTIMODULE)
639 // Third row is number of subtypes -1 (max valid subtype)
640 #define NO_SUBTYPE nullptr
642 // Table is designed to be shared with companion multi.cpp
644 // The subtype and options strings are only referenced here, so
645 // define them here to avoid duplication in all language files
646 // Also since these strings are ARM only and likely stay ARM only
647 // we don't need the special eeprom/flash string handling, just define them as
648 // local strings
650 const pm_char STR_SUBTYPE_FLYSKY[] PROGMEM = "\004""Std\0""V9x9""V6x6""V912""CX20";
652 const pm_char STR_SUBTYPE_AFHDS2A[] PROGMEM = "\010""PWM,IBUS""PPM,IBUS""PWM,SBUS""PPM,SBUS";
654 const pm_char STR_SUBTYPE_FRSKY[] PROGMEM = "\007""D16\0 ""D8\0 ""D16 8ch""V8\0 ""LBT(EU)""LBT 8ch";
656 const pm_char STR_SUBTYPE_HISKY[] PROGMEM = "\005""HiSky""HK310";
658 const pm_char STR_SUBTYPE_DSM[] PROGMEM = "\006""2 22ms""2 11ms""X 22ms""X 11ms";
660 const pm_char STR_SUBTYPE_YD717[] PROGMEM = "\007""YD717\0 ""SKYWLKR""Syma X2""XINXUN\0""NIHUI\0 ";
662 const pm_char STR_SUBTYPE_SYMAX[] PROGMEM = "\003""Std""5c\0";
664 const pm_char STR_SUBTYPE_SLT[] PROGMEM = "\005""SLT\0 ""Vista";
666 const pm_char STR_SUBTYPE_CX10[] PROGMEM = "\007""Green\0 ""Blue\0 ""DM007\0 ""-\0 ""JC3015a""JC3015b""MK33041""Q242\0 ";
668 const pm_char STR_SUBTYPE_CG023[] PROGMEM = "\005""CG023""YD829";
670 const pm_char STR_SUBTYPE_KN[] PROGMEM = "\006""WLtoys""FeiLun";
672 const pm_char STR_SUBTYPE_MT99[] PROGMEM = "\005""MT99\0""H7\0 ""YZ\0 ""LS\0 ""FY805";
674 const pm_char STR_SUBTYPE_MJXQ[] PROGMEM = "\005""WLH08""X600\0""X800\0""H26D\0""E010\0""H26WH";
676 const pm_char STR_SUBTYPE_HONTAI[] PROGMEM = "\007""Std\0 ""JJRC X1""X5C1cln";
678 const pm_char STR_SUBTYPE_Q2X2[] PROGMEM = "\004""Q222""Q242""Q282";
680 const pm_char STR_SUBTYPE_Q303[] PROGMEM = "\006""Q303\0 ""CX35\0 ""CX10D\0""CX10WD";
682 const pm_char STR_SUBTYPE_WK2x01[] PROGMEM = "\006""WK2801""WK2401""W6_5_1""W6_6_1""W6_Hel""W6_HeI";
684 const pm_char STR_SUBTYPE_V2X2[] PROGMEM = "\006""V2x2\0 ""JXD506";
686 const pm_char STR_SUBTYPE_BAYANG[] PROGMEM = "\007""Bayang\0""H8S3D\0 ""X16 AH\0 ""irdrone";
688 const pm_char STR_SUBTYPE_FY326[] PROGMEM = "\005""FY326""FY319";
690 const pm_char STR_SUBTYPE_CABELL[] PROGMEM = "\006""CAB_V3""C_TELM""-\0 ""-\0 ""-\0 ""-\0 ""F_SAFE""UNBIND";
692 const pm_char STR_SUBTYPE_H83D[] PROGMEM = "\006""H8_3D\0""H20H\0 ""H20Mini""H30Mini";
694 const mm_protocol_definition multi_protocols[] = {
696 {MM_RF_PROTO_FLYSKY, 4, false, STR_SUBTYPE_FLYSKY, nullptr},
697 {MM_RF_PROTO_HUBSAN, 0, false, NO_SUBTYPE, STR_MULTI_VIDFREQ},
698 {MM_RF_PROTO_FRSKY, 5, false, STR_SUBTYPE_FRSKY, STR_MULTI_RFTUNE},
699 {MM_RF_PROTO_HISKY, 1, false, STR_SUBTYPE_HISKY, nullptr},
700 {MM_RF_PROTO_V2X2, 1, false, STR_SUBTYPE_V2X2, nullptr},
701 {MM_RF_PROTO_DSM2, 3, false, STR_SUBTYPE_DSM, nullptr},
702 {MM_RF_PROTO_YD717, 4, false, STR_SUBTYPE_YD717, nullptr},
703 {MM_RF_PROTO_KN, 1, false, STR_SUBTYPE_KN, nullptr},
704 {MM_RF_PROTO_SYMAX, 1, false, STR_SUBTYPE_SYMAX, nullptr},
705 {MM_RF_PROTO_SLT, 1, false, STR_SUBTYPE_SLT, nullptr},
706 {MM_RF_PROTO_CX10, 7, false, STR_SUBTYPE_CX10, nullptr},
707 {MM_RF_PROTO_CG023, 1, false, STR_SUBTYPE_CG023, nullptr},
708 {MM_RF_PROTO_BAYANG, 3, false, STR_SUBTYPE_BAYANG, STR_MULTI_TELEMETRY},
709 {MM_RF_PROTO_MT99XX, 4, false, STR_SUBTYPE_MT99, nullptr},
710 {MM_RF_PROTO_MJXQ, 5, false, STR_SUBTYPE_MJXQ, nullptr},
711 {MM_RF_PROTO_FY326, 1, false, STR_SUBTYPE_FY326, nullptr},
712 {MM_RF_PROTO_SFHSS, 0, true, NO_SUBTYPE, STR_MULTI_RFTUNE},
713 {MM_RF_PROTO_HONTAI, 2, false, STR_SUBTYPE_HONTAI, nullptr},
714 {MM_RF_PROTO_OLRS, 0, false, NO_SUBTYPE, STR_MULTI_RFPOWER},
715 {MM_RF_PROTO_FS_AFHDS2A, 3, true, STR_SUBTYPE_AFHDS2A, STR_MULTI_SERVOFREQ},
716 {MM_RF_PROTO_Q2X2, 2, false, STR_SUBTYPE_Q2X2, nullptr},
717 {MM_RF_PROTO_WK_2X01, 5, false, STR_SUBTYPE_WK2x01, nullptr},
718 {MM_RF_PROTO_Q303, 3, false, STR_SUBTYPE_Q303, nullptr},
719 {MM_RF_PROTO_CABELL, 7, false, STR_SUBTYPE_CABELL, STR_MULTI_OPTION},
720 {MM_RF_PROTO_H83D, 3, false, STR_SUBTYPE_H83D, nullptr},
721 {MM_RF_CUSTOM_SELECTED, 7, true, NO_SUBTYPE, STR_MULTI_OPTION},
723 // Sentinel and default for protocols not listed above (MM_RF_CUSTOM is 0xff)
724 {0xfe, 0, false, NO_SUBTYPE, nullptr}
727 #undef NO_SUBTYPE
729 const mm_protocol_definition *getMultiProtocolDefinition (uint8_t protocol)
731 const mm_protocol_definition *pdef;
732 for (pdef = multi_protocols; pdef->protocol != 0xfe; pdef++) {
733 if (pdef->protocol == protocol)
734 return pdef;
736 // Return the empty last protocol
737 return pdef;
739 #endif
741 void editStickHardwareSettings(coord_t x, coord_t y, int idx, event_t event, LcdFlags flags)
743 lcdDrawTextAtIndex(INDENT_WIDTH, y, STR_VSRCRAW, idx+1, 0);
744 if (ZEXIST(g_eeGeneral.anaNames[idx]) || (flags && s_editMode > 0))
745 editName(x, y, g_eeGeneral.anaNames[idx], LEN_ANA_NAME, event, flags);
746 else
747 lcdDrawMMM(x, y, flags);