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.
25 enum MenuModelSetupItems
{
29 ITEM_MODEL_TIMER1_NAME
,
30 ITEM_MODEL_TIMER1_PERSISTENT
,
31 ITEM_MODEL_TIMER1_MINUTE_BEEP
,
32 ITEM_MODEL_TIMER1_COUNTDOWN_BEEP
,
35 ITEM_MODEL_TIMER2_NAME
,
36 ITEM_MODEL_TIMER2_PERSISTENT
,
37 ITEM_MODEL_TIMER2_MINUTE_BEEP
,
38 ITEM_MODEL_TIMER2_COUNTDOWN_BEEP
,
42 ITEM_MODEL_TIMER3_NAME
,
43 ITEM_MODEL_TIMER3_PERSISTENT
,
44 ITEM_MODEL_TIMER3_MINUTE_BEEP
,
45 ITEM_MODEL_TIMER3_COUNTDOWN_BEEP
,
47 ITEM_MODEL_EXTENDED_LIMITS
,
48 ITEM_MODEL_EXTENDED_TRIMS
,
49 ITEM_MODEL_DISPLAY_TRIMS
,
51 ITEM_MODEL_THROTTLE_LABEL
,
52 ITEM_MODEL_THROTTLE_REVERSED
,
53 ITEM_MODEL_THROTTLE_TRACE
,
54 ITEM_MODEL_THROTTLE_TRIM
,
55 ITEM_MODEL_PREFLIGHT_LABEL
,
56 ITEM_MODEL_CHECKLIST_DISPLAY
,
57 ITEM_MODEL_THROTTLE_WARNING
,
58 ITEM_MODEL_SWITCHES_WARNING
,
59 ITEM_MODEL_SLIDPOT_WARNING_STATE
,
60 ITEM_MODEL_POTS_WARNING
,
61 ITEM_MODEL_SLIDERS_WARNING
,
62 ITEM_MODEL_BEEP_CENTER
,
63 ITEM_MODEL_USE_GLOBAL_FUNCTIONS
,
64 ITEM_MODEL_INTERNAL_MODULE_LABEL
,
65 ITEM_MODEL_INTERNAL_MODULE_MODE
,
66 ITEM_MODEL_INTERNAL_MODULE_CHANNELS
,
67 ITEM_MODEL_INTERNAL_MODULE_BIND
,
68 ITEM_MODEL_INTERNAL_MODULE_FAILSAFE
,
69 ITEM_MODEL_INTERNAL_MODULE_ANTENNA
,
70 ITEM_MODEL_EXTERNAL_MODULE_LABEL
,
71 ITEM_MODEL_EXTERNAL_MODULE_MODE
,
72 #if defined(MULTIMODULE)
73 ITEM_MODEL_EXTERNAL_MODULE_STATUS
,
74 ITEM_MODEL_EXTERNAL_MODULE_SYNCSTATUS
,
76 ITEM_MODEL_EXTERNAL_MODULE_CHANNELS
,
77 ITEM_MODEL_EXTERNAL_MODULE_BIND
,
78 ITEM_MODEL_EXTERNAL_MODULE_FAILSAFE
,
79 ITEM_MODEL_EXTERNAL_MODULE_OPTIONS
,
80 #if defined(MULTIMODULE)
81 ITEM_MODEL_EXTERNAL_MODULE_AUTOBIND
,
83 ITEM_MODEL_EXTERNAL_MODULE_POWER
,
84 ITEM_MODEL_TRAINER_LABEL
,
85 ITEM_MODEL_TRAINER_MODE
,
86 ITEM_MODEL_TRAINER_LINE1
,
87 ITEM_MODEL_TRAINER_LINE2
,
91 #define MODEL_SETUP_2ND_COLUMN 200
92 #define MODEL_SETUP_3RD_COLUMN 270
93 #define MODEL_SETUP_4TH_COLUMN 350
94 #define MODEL_SETUP_BIND_OFS 40
95 #define MODEL_SETUP_RANGE_OFS 80
96 #define MODEL_SETUP_SET_FAILSAFE_OFS 100
97 #define MODEL_SETUP_SLIDPOT_SPACING 45
99 #define CURRENT_MODULE_EDITED(k) (k>=ITEM_MODEL_TRAINER_LABEL ? TRAINER_MODULE : (k>=ITEM_MODEL_EXTERNAL_MODULE_LABEL ? EXTERNAL_MODULE : INTERNAL_MODULE))
101 void onBindMenu(const char * result
)
103 uint8_t moduleIdx
= CURRENT_MODULE_EDITED(menuVerticalPosition
);
105 if (result
== STR_BINDING_25MW_CH1_8_TELEM_OFF
) {
106 g_model
.moduleData
[moduleIdx
].pxx
.power
= R9M_LBT_POWER_25
;
107 g_model
.moduleData
[moduleIdx
].pxx
.receiver_telem_off
= true;
108 g_model
.moduleData
[moduleIdx
].pxx
.receiver_channel_9_16
= false;
110 else if (result
== STR_BINDING_25MW_CH1_8_TELEM_ON
) {
111 g_model
.moduleData
[moduleIdx
].pxx
.power
= R9M_LBT_POWER_25
;
112 g_model
.moduleData
[moduleIdx
].pxx
.receiver_telem_off
= false;
113 g_model
.moduleData
[moduleIdx
].pxx
.receiver_channel_9_16
= false;
115 else if (result
== STR_BINDING_500MW_CH1_8_TELEM_OFF
) {
116 g_model
.moduleData
[moduleIdx
].pxx
.power
= R9M_LBT_POWER_500
;
117 g_model
.moduleData
[moduleIdx
].pxx
.receiver_telem_off
= true;
118 g_model
.moduleData
[moduleIdx
].pxx
.receiver_channel_9_16
= false;
120 else if (result
== STR_BINDING_500MW_CH9_16_TELEM_OFF
) {
121 g_model
.moduleData
[moduleIdx
].pxx
.power
= R9M_LBT_POWER_500
;
122 g_model
.moduleData
[moduleIdx
].pxx
.receiver_telem_off
= true;
123 g_model
.moduleData
[moduleIdx
].pxx
.receiver_channel_9_16
= true;
125 else if (result
== STR_BINDING_1_8_TELEM_ON
) {
126 g_model
.moduleData
[moduleIdx
].pxx
.receiver_telem_off
= false;
127 g_model
.moduleData
[moduleIdx
].pxx
.receiver_channel_9_16
= false;
129 else if (result
== STR_BINDING_1_8_TELEM_OFF
) {
130 g_model
.moduleData
[moduleIdx
].pxx
.receiver_telem_off
= true;
131 g_model
.moduleData
[moduleIdx
].pxx
.receiver_channel_9_16
= false;
133 else if (result
== STR_BINDING_9_16_TELEM_ON
) {
134 g_model
.moduleData
[moduleIdx
].pxx
.receiver_telem_off
= false;
135 g_model
.moduleData
[moduleIdx
].pxx
.receiver_channel_9_16
= true;
137 else if (result
== STR_BINDING_9_16_TELEM_OFF
) {
138 g_model
.moduleData
[moduleIdx
].pxx
.receiver_telem_off
= true;
139 g_model
.moduleData
[moduleIdx
].pxx
.receiver_channel_9_16
= true;
145 moduleFlag
[moduleIdx
] = MODULE_BIND
;
148 void onModelSetupBitmapMenu(const char * result
)
150 if (result
== STR_UPDATE_LIST
) {
151 if (!sdListFiles(BITMAPS_PATH
, BITMAPS_EXT
, sizeof(g_model
.header
.bitmap
)-LEN_BITMAPS_EXT
, NULL
)) {
152 POPUP_WARNING(STR_NO_BITMAPS_ON_SD
);
156 // The user choosed a bmp file in the list
157 copySelection(g_model
.header
.bitmap
, result
, sizeof(g_model
.header
.bitmap
));
158 storageDirty(EE_MODEL
);
162 void editTimerMode(int timerIdx
, coord_t y
, LcdFlags attr
, event_t event
)
164 TimerData
& timer
= g_model
.timers
[timerIdx
];
165 if (attr
&& menuHorizontalPosition
< 0) {
166 lcdDrawSolidFilledRect(MODEL_SETUP_2ND_COLUMN
-INVERT_HORZ_MARGIN
, y
-INVERT_VERT_MARGIN
+1, 115+2*INVERT_HORZ_MARGIN
, INVERT_LINE_HEIGHT
, TEXT_INVERTED_BGCOLOR
);
168 drawStringWithIndex(MENUS_MARGIN_LEFT
, y
, STR_TIMER
, timerIdx
+1);
169 drawTimerMode(MODEL_SETUP_2ND_COLUMN
, y
, timer
.mode
, (menuHorizontalPosition
<=0 ? attr
: 0));
170 drawTimer(MODEL_SETUP_2ND_COLUMN
+50, y
, timer
.start
, (menuHorizontalPosition
!=0 ? attr
|TIMEHOUR
: TIMEHOUR
));
171 if (attr
&& s_editMode
>0) {
172 switch (menuHorizontalPosition
) {
175 int32_t timerMode
= timer
.mode
;
176 if (timerMode
< 0) timerMode
-= TMRMODE_COUNT
-1;
177 CHECK_INCDEC_MODELVAR_CHECK(event
, timerMode
, -TMRMODE_COUNT
-SWSRC_LAST
+1, TMRMODE_COUNT
+SWSRC_LAST
-1, isSwitchAvailableInTimers
);
178 if (timerMode
< 0) timerMode
+= TMRMODE_COUNT
-1;
179 timer
.mode
= timerMode
;
180 #if defined(AUTOSWITCH)
182 int8_t val
= timer
.mode
- (TMRMODE_COUNT
-1);
183 int8_t switchVal
= checkIncDecMovedSwitch(val
);
184 if (val
!= switchVal
) {
185 timer
.mode
= switchVal
+ (TMRMODE_COUNT
-1);
186 storageDirty(EE_MODEL
);
194 const int stopsMinutes
[] = { 8, 60, 120, 180, 240, 300, 600, 900, 1200 };
195 timer
.start
= checkIncDec(event
, timer
.start
, 0, TIMER_MAX
, EE_MODEL
, NULL
, (const CheckIncDecStops
&)stopsMinutes
);
202 void editTimerCountdown(int timerIdx
, coord_t y
, LcdFlags attr
, event_t event
)
204 TimerData
& timer
= g_model
.timers
[timerIdx
];
205 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_BEEPCOUNTDOWN
);
206 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
, y
, STR_VBEEPCOUNTDOWN
, timer
.countdownBeep
, (menuHorizontalPosition
== 0 ? attr
: 0));
207 if (timer
.countdownBeep
!= COUNTDOWN_SILENT
) {
208 lcdDrawNumber(MODEL_SETUP_3RD_COLUMN
, y
, TIMER_COUNTDOWN_START(timerIdx
), (menuHorizontalPosition
== 1 ? attr
: 0) | LEFT
, 0, NULL
, "s");
210 if (attr
&& s_editMode
> 0) {
211 switch (menuHorizontalPosition
) {
213 CHECK_INCDEC_MODELVAR(event
, timer
.countdownBeep
, COUNTDOWN_SILENT
, COUNTDOWN_COUNT
- 1);
216 timer
.countdownStart
= -checkIncDecModel(event
, -timer
.countdownStart
, -1, +2);
222 int getSwitchWarningsCount()
225 for (int i
=0; i
<NUM_SWITCHES
; ++i
) {
226 if (SWITCH_WARNING_ALLOWED(i
)) {
233 #define IF_INTERNAL_MODULE_ON(x) (IS_INTERNAL_MODULE_ENABLED() ? (uint8_t)(x) : HIDDEN_ROW)
234 #define IF_EXTERNAL_MODULE_ON(x) (IS_EXTERNAL_MODULE_ENABLED() ? (uint8_t)(x) : HIDDEN_ROW)
236 #define INTERNAL_MODULE_MODE_ROWS (uint8_t)0
237 #define INTERNAL_MODULE_CHANNELS_ROWS IF_INTERNAL_MODULE_ON(1)
238 #define PORT_CHANNELS_ROWS(x) (x==INTERNAL_MODULE ? INTERNAL_MODULE_CHANNELS_ROWS : (x==EXTERNAL_MODULE ? EXTERNAL_MODULE_CHANNELS_ROWS : 1))
240 #define TIMER_ROWS(x) NAVIGATION_LINE_BY_LINE|1, 0, 0, 0, g_model.timers[x].countdownBeep != COUNTDOWN_SILENT ? (uint8_t)1 : (uint8_t)0
242 #define EXTERNAL_MODULE_MODE_ROWS (IS_MODULE_PXX(EXTERNAL_MODULE) || IS_MODULE_DSM2(EXTERNAL_MODULE)) ? (uint8_t)1 : IS_MODULE_MULTIMODULE(EXTERNAL_MODULE) ? MULTIMODULE_MODE_ROWS(EXTERNAL_MODULE) : (uint8_t)0
245 #define TIMERS_ROWS TIMER_ROWS(0)
247 #define TIMERS_ROWS TIMER_ROWS(0), TIMER_ROWS(1)
249 #define TIMERS_ROWS TIMER_ROWS(0), TIMER_ROWS(1), TIMER_ROWS(2)
252 #define SW_WARN_ITEMS() uint8_t(NAVIGATION_LINE_BY_LINE|(getSwitchWarningsCount()-1))
253 #define POT_WARN_ROWS (uint8_t)0
254 #define POT_WARN_ITEMS() ((g_model.potsWarnMode) ? uint8_t(NAVIGATION_LINE_BY_LINE|(NUM_POTS-1)) : (uint8_t)0)
255 #define SLIDER_WARN_ITEMS() ((g_model.potsWarnMode) ? uint8_t(NAVIGATION_LINE_BY_LINE|(NUM_SLIDERS-1)) : (uint8_t)0)
257 #define TRAINER_LINE1_BLUETOOTH_M_ROWS ((bluetoothDistantAddr[0] == 0 || bluetoothState == BLUETOOTH_STATE_CONNECTED) ? (uint8_t)0 : (uint8_t)1)
258 #define TRAINER_LINE1_ROWS (g_model.trainerMode == TRAINER_MODE_SLAVE ? (uint8_t)1 : (g_model.trainerMode == TRAINER_MODE_MASTER_BLUETOOTH ? TRAINER_LINE1_BLUETOOTH_M_ROWS : (g_model.trainerMode == TRAINER_MODE_SLAVE_BLUETOOTH ? (uint8_t)1 : HIDDEN_ROW)))
259 #define TRAINER_LINE2_ROWS (g_model.trainerMode == TRAINER_MODE_SLAVE ? (uint8_t)2 : HIDDEN_ROW)
261 bool menuModelSetup(event_t event
)
263 bool CURSOR_ON_CELL
= (menuHorizontalPosition
>= 0);
265 // Switch to external antenna confirmation
269 g_model
.moduleData
[INTERNAL_MODULE
].pxx
.external_antenna
= XJT_EXTERNAL_ANTENNA
;
272 MENU(STR_MENUSETUP
, MODEL_ICONS
, menuTabModel
, MENU_MODEL_SETUP
, ITEM_MODEL_SETUP_MAX
,
273 { 0, 0, TIMERS_ROWS
, 0, 1, 0, 0,
274 LABEL(Throttle
), 0, 0, 0,
275 LABEL(PreflightCheck
), 0, 0, SW_WARN_ITEMS(), POT_WARN_ROWS
, (g_model
.potsWarnMode
? POT_WARN_ITEMS() : HIDDEN_ROW
), (g_model
.potsWarnMode
? SLIDER_WARN_ITEMS() : HIDDEN_ROW
), NAVIGATION_LINE_BY_LINE
|(NUM_STICKS
+NUM_POTS
+NUM_SLIDERS
+NUM_ROTARY_ENCODERS
-1), 0,
276 LABEL(InternalModule
),
277 INTERNAL_MODULE_MODE_ROWS
,
278 INTERNAL_MODULE_CHANNELS_ROWS
,
279 IF_INTERNAL_MODULE_ON(IS_MODULE_XJT(INTERNAL_MODULE
) ? (HAS_RF_PROTOCOL_MODELINDEX(g_model
.moduleData
[INTERNAL_MODULE
].rfProtocol
) ? (uint8_t)2 : (uint8_t)1) : (IS_MODULE_PPM(INTERNAL_MODULE
) ? (uint8_t)1 : HIDDEN_ROW
)),
280 IF_INTERNAL_MODULE_ON((IS_MODULE_XJT(INTERNAL_MODULE
)) ? FAILSAFE_ROWS(INTERNAL_MODULE
) : HIDDEN_ROW
),
281 IF_INTERNAL_MODULE_ON(0),
282 LABEL(ExternalModule
),
283 EXTERNAL_MODULE_MODE_ROWS
,
284 MULTIMODULE_STATUS_ROWS
285 EXTERNAL_MODULE_CHANNELS_ROWS
,
286 ((IS_MODULE_XJT(EXTERNAL_MODULE
) && !HAS_RF_PROTOCOL_MODELINDEX(g_model
.moduleData
[EXTERNAL_MODULE
].rfProtocol
)) || IS_MODULE_SBUS(EXTERNAL_MODULE
)) ? (uint8_t)1 : (IS_MODULE_PPM(EXTERNAL_MODULE
) || IS_MODULE_PXX(EXTERNAL_MODULE
) || IS_MODULE_DSM2(EXTERNAL_MODULE
) || IS_MODULE_MULTIMODULE(EXTERNAL_MODULE
)) ? (uint8_t)2 : HIDDEN_ROW
,
287 FAILSAFE_ROWS(EXTERNAL_MODULE
),
288 EXTERNAL_MODULE_OPTION_ROW
,
289 MULTIMODULE_MODULE_ROWS
290 EXTERNAL_MODULE_POWER_ROW
,
294 TRAINER_LINE2_ROWS
});
301 int sub
= menuVerticalPosition
;
303 for (uint8_t i
=0; i
<NUM_BODY_LINES
; ++i
) {
304 coord_t y
= MENU_CONTENT_TOP
+ i
*FH
;
305 uint8_t k
= i
+ menuVerticalOffset
;
306 for (int j
=0; j
<=k
; j
++) {
307 if (mstate_tab
[j
] == HIDDEN_ROW
)
311 LcdFlags blink
= ((s_editMode
>0) ? BLINK
|INVERS
: INVERS
);
312 LcdFlags attr
= (sub
== k
? blink
: 0);
315 case ITEM_MODEL_NAME
:
316 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_MODELNAME
);
317 editName(MODEL_SETUP_2ND_COLUMN
, y
, g_model
.header
.name
, sizeof(g_model
.header
.name
), event
, attr
);
320 case ITEM_MODEL_BITMAP
:
321 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_BITMAP
);
322 if (ZEXIST(g_model
.header
.bitmap
))
323 lcdDrawSizedText(MODEL_SETUP_2ND_COLUMN
, y
, g_model
.header
.bitmap
, sizeof(g_model
.header
.bitmap
), attr
);
325 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
, y
, STR_VCSWFUNC
, 0, attr
);
326 if (attr
&& event
==EVT_KEY_BREAK(KEY_ENTER
) && READ_ONLY_UNLOCKED()) {
328 if (sdListFiles(BITMAPS_PATH
, BITMAPS_EXT
, sizeof(g_model
.header
.bitmap
)-LEN_BITMAPS_EXT
, g_model
.header
.bitmap
, LIST_NONE_SD_FILE
| LIST_SD_FILE_EXT
)) {
329 POPUP_MENU_START(onModelSetupBitmapMenu
);
332 POPUP_WARNING(STR_NO_BITMAPS_ON_SD
);
337 case ITEM_MODEL_TIMER1
:
338 editTimerMode(0, y
, attr
, event
);
341 case ITEM_MODEL_TIMER1_NAME
:
342 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_TIMER_NAME
);
343 editName(MODEL_SETUP_2ND_COLUMN
, y
, g_model
.timers
[0].name
, LEN_TIMER_NAME
, event
, attr
);
346 case ITEM_MODEL_TIMER1_MINUTE_BEEP
:
347 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_MINUTEBEEP
);
348 g_model
.timers
[0].minuteBeep
= editCheckBox(g_model
.timers
[0].minuteBeep
, MODEL_SETUP_2ND_COLUMN
, y
, attr
, event
);
351 case ITEM_MODEL_TIMER1_COUNTDOWN_BEEP
:
352 editTimerCountdown(0, y
, attr
, event
);
355 case ITEM_MODEL_TIMER1_PERSISTENT
:
356 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_PERSISTENT
);
357 g_model
.timers
[0].persistent
= editChoice(MODEL_SETUP_2ND_COLUMN
, y
, STR_VPERSISTENT
, g_model
.timers
[0].persistent
, 0, 2, attr
, event
);
361 case ITEM_MODEL_TIMER2
:
362 editTimerMode(1, y
, attr
, event
);
365 case ITEM_MODEL_TIMER2_NAME
:
366 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_TIMER_NAME
);
367 editName(MODEL_SETUP_2ND_COLUMN
, y
, g_model
.timers
[1].name
, LEN_TIMER_NAME
, event
, attr
);
370 case ITEM_MODEL_TIMER2_MINUTE_BEEP
:
371 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_MINUTEBEEP
);
372 g_model
.timers
[1].minuteBeep
= editCheckBox(g_model
.timers
[1].minuteBeep
, MODEL_SETUP_2ND_COLUMN
, y
, attr
, event
);
375 case ITEM_MODEL_TIMER2_COUNTDOWN_BEEP
:
376 editTimerCountdown(1, y
, attr
, event
);
379 case ITEM_MODEL_TIMER2_PERSISTENT
:
380 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_PERSISTENT
);
381 g_model
.timers
[1].persistent
= editChoice(MODEL_SETUP_2ND_COLUMN
, y
, STR_VPERSISTENT
, g_model
.timers
[1].persistent
, 0, 2, attr
, event
);
386 case ITEM_MODEL_TIMER3
:
387 editTimerMode(2, y
, attr
, event
);
390 case ITEM_MODEL_TIMER3_NAME
:
391 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_TIMER_NAME
);
392 editName(MODEL_SETUP_2ND_COLUMN
, y
, g_model
.timers
[2].name
, LEN_TIMER_NAME
, event
, attr
);
395 case ITEM_MODEL_TIMER3_MINUTE_BEEP
:
396 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_MINUTEBEEP
);
397 g_model
.timers
[2].minuteBeep
= editCheckBox(g_model
.timers
[2].minuteBeep
, MODEL_SETUP_2ND_COLUMN
, y
, attr
, event
);
400 case ITEM_MODEL_TIMER3_COUNTDOWN_BEEP
:
401 editTimerCountdown(2, y
, attr
, event
);
404 case ITEM_MODEL_TIMER3_PERSISTENT
:
405 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_PERSISTENT
);
406 g_model
.timers
[2].persistent
= editChoice(MODEL_SETUP_2ND_COLUMN
, y
, STR_VPERSISTENT
, g_model
.timers
[2].persistent
, 0, 2, attr
, event
);
410 case ITEM_MODEL_EXTENDED_LIMITS
:
411 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_ELIMITS
);
412 g_model
.extendedLimits
= editCheckBox(g_model
.extendedLimits
, MODEL_SETUP_2ND_COLUMN
, y
, attr
, event
);
415 case ITEM_MODEL_EXTENDED_TRIMS
:
416 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_ETRIMS
);
417 g_model
.extendedTrims
= editCheckBox(g_model
.extendedTrims
, MODEL_SETUP_2ND_COLUMN
, y
, menuHorizontalPosition
<=0 ? attr
: 0, event
==EVT_KEY_BREAK(KEY_ENTER
) ? event
: 0);
418 lcdDrawText(MODEL_SETUP_2ND_COLUMN
+18, y
, STR_RESET_BTN
, menuHorizontalPosition
>0 && !NO_HIGHLIGHT() ? attr
: 0);
419 if (attr
&& menuHorizontalPosition
>0) {
421 if (event
==EVT_KEY_LONG(KEY_ENTER
)) {
422 START_NO_HIGHLIGHT();
423 for (uint8_t i
=0; i
<MAX_FLIGHT_MODES
; i
++) {
424 memclear(&g_model
.flightModeData
[i
], TRIMS_ARRAY_SIZE
);
426 storageDirty(EE_MODEL
);
432 case ITEM_MODEL_DISPLAY_TRIMS
:
433 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_DISPLAY_TRIMS
);
434 g_model
.displayTrims
= editChoice(MODEL_SETUP_2ND_COLUMN
, y
, "\006No\0 ChangeYes", g_model
.displayTrims
, 0, 2, attr
, event
);
437 case ITEM_MODEL_TRIM_INC
:
438 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_TRIMINC
);
439 g_model
.trimInc
= editChoice(MODEL_SETUP_2ND_COLUMN
, y
, STR_VTRIMINC
, g_model
.trimInc
, -2, 2, attr
, event
);
442 case ITEM_MODEL_THROTTLE_LABEL
:
443 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_THROTTLE_LABEL
);
446 case ITEM_MODEL_THROTTLE_REVERSED
:
447 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_THROTTLEREVERSE
);
448 g_model
.throttleReversed
= editCheckBox(g_model
.throttleReversed
, MODEL_SETUP_2ND_COLUMN
, y
, attr
, event
);
451 case ITEM_MODEL_THROTTLE_TRACE
:
453 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_TTRACE
);
454 if (attr
) CHECK_INCDEC_MODELVAR_ZERO(event
, g_model
.thrTraceSrc
, NUM_POTS
+NUM_SLIDERS
+MAX_OUTPUT_CHANNELS
);
455 uint8_t idx
= g_model
.thrTraceSrc
+ MIXSRC_Thr
;
456 if (idx
> MIXSRC_Thr
)
458 if (idx
>= MIXSRC_FIRST_POT
+NUM_POTS
+NUM_SLIDERS
)
459 idx
+= MIXSRC_CH1
- MIXSRC_FIRST_POT
- NUM_POTS
- NUM_SLIDERS
;
460 drawSource(MODEL_SETUP_2ND_COLUMN
, y
, idx
, attr
);
464 case ITEM_MODEL_THROTTLE_TRIM
:
465 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_TTRIM
);
466 g_model
.thrTrim
= editCheckBox(g_model
.thrTrim
, MODEL_SETUP_2ND_COLUMN
, y
, attr
, event
);
469 case ITEM_MODEL_PREFLIGHT_LABEL
:
470 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_PREFLIGHT
);
473 case ITEM_MODEL_CHECKLIST_DISPLAY
:
474 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_CHECKLIST
);
475 g_model
.displayChecklist
= editCheckBox(g_model
.displayChecklist
, MODEL_SETUP_2ND_COLUMN
, y
, attr
, event
);
478 case ITEM_MODEL_THROTTLE_WARNING
:
479 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_THROTTLEWARNING
);
480 g_model
.disableThrottleWarning
= !editCheckBox(!g_model
.disableThrottleWarning
, MODEL_SETUP_2ND_COLUMN
, y
, attr
, event
);
483 case ITEM_MODEL_SWITCHES_WARNING
:
485 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_SWITCHWARNING
);
486 if (!READ_ONLY() && attr
&& menuHorizontalPosition
<0 && event
==EVT_KEY_LONG(KEY_ENTER
)) {
488 START_NO_HIGHLIGHT();
490 for (int i
=0; i
<NUM_SWITCHES
; i
++) {
491 bool enabled
= ((g_model
.switchWarningState
>> (3*i
)) & 0x07);
493 g_model
.switchWarningState
&= ~(0x07 << (3*i
));
494 unsigned int newState
= (switches_states
>> (2*i
) & 0x03) + 1;
495 g_model
.switchWarningState
|= (newState
<< (3*i
));
499 storageDirty(EE_MODEL
);
502 if (attr
&& menuHorizontalPosition
< 0) {
503 lcdDrawSolidFilledRect(MODEL_SETUP_2ND_COLUMN
-INVERT_HORZ_MARGIN
, y
-INVERT_VERT_MARGIN
+1, (NUM_SWITCHES
-1)*25+INVERT_HORZ_MARGIN
, INVERT_LINE_HEIGHT
, TEXT_INVERTED_BGCOLOR
);
506 unsigned int newStates
= 0;
507 for (int i
=0, current
=0; i
<NUM_SWITCHES
; i
++) {
508 if (SWITCH_WARNING_ALLOWED(i
)) {
509 unsigned int state
= ((g_model
.switchWarningState
>> (3*i
)) & 0x07);
510 LcdFlags color
= (state
> 0 ? TEXT_COLOR
: TEXT_DISABLE_COLOR
);
511 if (attr
&& menuHorizontalPosition
< 0) {
516 s
[1] = "x\300-\301"[state
];
518 lcdDrawText(MODEL_SETUP_2ND_COLUMN
+i
*25, y
, s
, color
|(menuHorizontalPosition
==current
? attr
: 0));
519 if (!READ_ONLY() && attr
&& menuHorizontalPosition
==current
) {
520 CHECK_INCDEC_MODELVAR_ZERO_CHECK(event
, state
, 3, IS_CONFIG_3POS(i
) ? NULL
: isSwitch2POSWarningStateAvailable
);
522 newStates
|= (state
<< (3*i
));
526 g_model
.switchWarningState
= newStates
;
530 case ITEM_MODEL_SLIDPOT_WARNING_STATE
:
531 lcdDrawText(MENUS_MARGIN_LEFT
, y
,STR_POTWARNINGSTATE
);
532 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
, y
, PSTR("\004""OFF\0""Man\0""Auto"), g_model
.potsWarnMode
, attr
);
534 CHECK_INCDEC_MODELVAR(event
, g_model
.potsWarnMode
, POTS_WARN_OFF
, POTS_WARN_AUTO
);
535 storageDirty(EE_MODEL
);
539 case ITEM_MODEL_POTS_WARNING
:
541 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_POTWARNING
);
543 if (!READ_ONLY() && menuHorizontalPosition
>= 0 && event
==EVT_KEY_LONG(KEY_ENTER
)) {
545 if (g_model
.potsWarnMode
== POTS_WARN_MANUAL
) {
546 SAVE_POT_POSITION(menuHorizontalPosition
);
548 storageDirty(EE_MODEL
);
552 if (!READ_ONLY() && menuHorizontalPosition
>= 0 && s_editMode
&& event
==EVT_KEY_BREAK(KEY_ENTER
)) {
554 g_model
.potsWarnEnabled
^= (1 << (menuHorizontalPosition
));
555 storageDirty(EE_MODEL
);
559 if (attr
&& menuHorizontalPosition
< 0) {
560 lcdDrawSolidFilledRect(MODEL_SETUP_2ND_COLUMN
-INVERT_HORZ_MARGIN
, y
-INVERT_VERT_MARGIN
+1, NUM_POTS
*MODEL_SETUP_SLIDPOT_SPACING
+INVERT_HORZ_MARGIN
, INVERT_LINE_HEIGHT
, TEXT_INVERTED_BGCOLOR
);
563 if (g_model
.potsWarnMode
) {
564 coord_t x
= MODEL_SETUP_2ND_COLUMN
;
565 for (int i
=0; i
<NUM_POTS
; ++i
) {
566 LcdFlags flags
= (((menuHorizontalPosition
==i
) && attr
) ? INVERS
: 0);
567 flags
|= (g_model
.potsWarnEnabled
& (1 << i
)) ? TEXT_DISABLE_COLOR
: TEXT_COLOR
;
568 if (attr
&& menuHorizontalPosition
< 0) {
571 lcdDrawTextAtIndex(x
, y
, STR_VSRCRAW
, NUM_STICKS
+1+i
, flags
);
572 x
+= MODEL_SETUP_SLIDPOT_SPACING
;
578 case ITEM_MODEL_SLIDERS_WARNING
:
579 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_SLIDERWARNING
);
581 if (!READ_ONLY() && menuHorizontalPosition
+1 && event
==EVT_KEY_LONG(KEY_ENTER
)) {
583 if (g_model
.potsWarnMode
== POTS_WARN_MANUAL
) {
584 SAVE_POT_POSITION(menuHorizontalPosition
+NUM_POTS
);
586 storageDirty(EE_MODEL
);
590 if (!READ_ONLY() && menuHorizontalPosition
+1 && s_editMode
&& event
==EVT_KEY_BREAK(KEY_ENTER
)) {
592 g_model
.potsWarnEnabled
^= (1 << (menuHorizontalPosition
+NUM_POTS
));
593 storageDirty(EE_MODEL
);
597 if (attr
&& menuHorizontalPosition
< 0) {
598 lcdDrawSolidFilledRect(MODEL_SETUP_2ND_COLUMN
-INVERT_HORZ_MARGIN
, y
-INVERT_VERT_MARGIN
+1, NUM_SLIDERS
*MODEL_SETUP_SLIDPOT_SPACING
+INVERT_HORZ_MARGIN
, INVERT_LINE_HEIGHT
, TEXT_INVERTED_BGCOLOR
);
601 if (g_model
.potsWarnMode
) {
602 coord_t x
= MODEL_SETUP_2ND_COLUMN
;
603 for (int i
=NUM_POTS
; i
<NUM_POTS
+NUM_SLIDERS
; ++i
) {
604 LcdFlags flags
= (((menuHorizontalPosition
==i
-NUM_POTS
) && attr
) ? INVERS
: 0);
605 flags
|= (g_model
.potsWarnEnabled
& (1 << i
)) ? TEXT_DISABLE_COLOR
: TEXT_COLOR
;
606 if (attr
&& menuHorizontalPosition
< 0) {
609 lcdDrawTextAtIndex(x
, y
, STR_VSRCRAW
, NUM_STICKS
+1+i
, flags
);
610 x
+= MODEL_SETUP_SLIDPOT_SPACING
;
615 case ITEM_MODEL_BEEP_CENTER
:
616 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_BEEPCTR
);
617 lcdNextPos
= MODEL_SETUP_2ND_COLUMN
- 3;
618 for (int i
=0; i
<NUM_STICKS
+NUM_POTS
+NUM_SLIDERS
; i
++) {
619 LcdFlags flags
= ((menuHorizontalPosition
==i
&& attr
) ? INVERS
: 0);
620 flags
|= (g_model
.beepANACenter
& ((BeepANACenter
)1<<i
)) ? TEXT_COLOR
: (TEXT_DISABLE_COLOR
| NO_FONTCACHE
);
621 if (attr
&& menuHorizontalPosition
< 0) flags
|= INVERS
;
622 lcdDrawTextAtIndex(lcdNextPos
+3, y
, STR_RETA123
, i
, flags
);
624 if (attr
&& CURSOR_ON_CELL
) {
625 if (event
==EVT_KEY_BREAK(KEY_ENTER
)) {
626 if (READ_ONLY_UNLOCKED()) {
628 g_model
.beepANACenter
^= ((BeepANACenter
)1<<menuHorizontalPosition
);
629 storageDirty(EE_MODEL
);
635 case ITEM_MODEL_USE_GLOBAL_FUNCTIONS
:
636 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_USE_GLOBAL_FUNCS
);
637 drawCheckBox(MODEL_SETUP_2ND_COLUMN
, y
, !g_model
.noGlobalFunctions
, attr
);
638 if (attr
) g_model
.noGlobalFunctions
= !checkIncDecModel(event
, !g_model
.noGlobalFunctions
, 0, 1);
641 case ITEM_MODEL_INTERNAL_MODULE_LABEL
:
642 lcdDrawText(MENUS_MARGIN_LEFT
, y
, TR_INTERNALRF
);
645 case ITEM_MODEL_INTERNAL_MODULE_MODE
:
646 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_MODE
);
647 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
, y
, STR_XJT_PROTOCOLS
, 1+g_model
.moduleData
[0].rfProtocol
, attr
);
649 g_model
.moduleData
[INTERNAL_MODULE
].rfProtocol
= checkIncDec(event
, g_model
.moduleData
[INTERNAL_MODULE
].rfProtocol
, RF_PROTO_OFF
, RF_PROTO_LAST
, EE_MODEL
, isRfProtocolAvailable
);
650 if (checkIncDec_Ret
) {
651 g_model
.moduleData
[0].type
= MODULE_TYPE_XJT
;
652 g_model
.moduleData
[0].channelsStart
= 0;
653 g_model
.moduleData
[0].channelsCount
= DEFAULT_CHANNELS(INTERNAL_MODULE
);
654 if (g_model
.moduleData
[INTERNAL_MODULE
].rfProtocol
== RF_PROTO_OFF
)
655 g_model
.moduleData
[INTERNAL_MODULE
].type
= MODULE_TYPE_NONE
;
661 case ITEM_MODEL_INTERNAL_MODULE_ANTENNA
:
662 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_ANTENNASELECTION
);
663 newAntennaSel
= editChoice(MODEL_SETUP_2ND_COLUMN
, y
, STR_VANTENNATYPES
, g_model
.moduleData
[INTERNAL_MODULE
].pxx
.external_antenna
, 0, 1, attr
, event
);
664 if (newAntennaSel
!= g_model
.moduleData
[INTERNAL_MODULE
].pxx
.external_antenna
&& newAntennaSel
== XJT_EXTERNAL_ANTENNA
) {
665 POPUP_CONFIRMATION(STR_ANTENNACONFIRM1
);
666 const char * w
= STR_ANTENNACONFIRM2
;
667 SET_WARNING_INFO(w
, strlen(w
), 0);
670 g_model
.moduleData
[INTERNAL_MODULE
].pxx
.external_antenna
= newAntennaSel
;
674 case ITEM_MODEL_TRAINER_MODE
:
675 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_MODE
);
676 g_model
.trainerMode
= editChoice(MODEL_SETUP_2ND_COLUMN
, y
, STR_VTRAINERMODES
, g_model
.trainerMode
, 0, TRAINER_MODE_MAX(), attr
, event
);
677 if (attr
&& checkIncDec_Ret
) {
678 bluetoothState
= BLUETOOTH_STATE_OFF
;
679 bluetoothDistantAddr
[0] = 0;
683 case ITEM_MODEL_EXTERNAL_MODULE_LABEL
:
684 lcdDrawText(MENUS_MARGIN_LEFT
, y
, TR_EXTERNALRF
);
687 case ITEM_MODEL_EXTERNAL_MODULE_MODE
:
688 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_MODE
);
689 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
, y
, STR_TARANIS_PROTOCOLS
, g_model
.moduleData
[EXTERNAL_MODULE
].type
, menuHorizontalPosition
==0 ? attr
: 0);
690 if (IS_MODULE_XJT(EXTERNAL_MODULE
))
691 lcdDrawTextAtIndex(MODEL_SETUP_3RD_COLUMN
, y
, STR_XJT_PROTOCOLS
, 1+g_model
.moduleData
[EXTERNAL_MODULE
].rfProtocol
, (menuHorizontalPosition
==1 ? attr
: 0));
692 else if (IS_MODULE_DSM2(EXTERNAL_MODULE
))
693 lcdDrawTextAtIndex(MODEL_SETUP_3RD_COLUMN
, y
, STR_DSM_PROTOCOLS
, g_model
.moduleData
[EXTERNAL_MODULE
].rfProtocol
, (menuHorizontalPosition
==1 ? attr
: 0));
694 else if (IS_MODULE_R9M(EXTERNAL_MODULE
))
695 lcdDrawTextAtIndex(MODEL_SETUP_3RD_COLUMN
, y
, STR_R9M_MODES
, g_model
.moduleData
[EXTERNAL_MODULE
].subType
, (menuHorizontalPosition
==1 ? attr
: 0));
696 #if defined(MULTIMODULE)
697 else if (IS_MODULE_MULTIMODULE(EXTERNAL_MODULE
)) {
698 int multi_rfProto
= g_model
.moduleData
[EXTERNAL_MODULE
].getMultiProtocol(false);
699 if (g_model
.moduleData
[EXTERNAL_MODULE
].multi
.customProto
) {
700 lcdDrawText(MODEL_SETUP_3RD_COLUMN
, y
, STR_MULTI_CUSTOM
, menuHorizontalPosition
== 1 ? attr
: 0);
701 lcdDrawNumber(MODEL_SETUP_4TH_COLUMN
, y
, multi_rfProto
, menuHorizontalPosition
==2 ? attr
: 0, 2);
702 lcdDrawNumber(MODEL_SETUP_4TH_COLUMN
+ MODEL_SETUP_BIND_OFS
, y
, g_model
.moduleData
[EXTERNAL_MODULE
].subType
, menuHorizontalPosition
==3 ? attr
: 0, 2);
705 const mm_protocol_definition
* pdef
= getMultiProtocolDefinition(multi_rfProto
);
706 lcdDrawTextAtIndex(MODEL_SETUP_3RD_COLUMN
, y
, STR_MULTI_PROTOCOLS
, multi_rfProto
, menuHorizontalPosition
== 1 ? attr
: 0);
707 if (pdef
->subTypeString
!= nullptr)
708 lcdDrawTextAtIndex(MODEL_SETUP_4TH_COLUMN
, y
, pdef
->subTypeString
, g_model
.moduleData
[EXTERNAL_MODULE
].subType
, menuHorizontalPosition
==2 ? attr
: 0);
712 if (attr
&& s_editMode
>0) {
713 switch (menuHorizontalPosition
) {
715 g_model
.moduleData
[EXTERNAL_MODULE
].type
= checkIncDec(event
, g_model
.moduleData
[EXTERNAL_MODULE
].type
, MODULE_TYPE_NONE
, MODULE_TYPE_COUNT
-1, EE_MODEL
, isModuleAvailable
);
716 if (checkIncDec_Ret
) {
717 g_model
.moduleData
[EXTERNAL_MODULE
].rfProtocol
= 0;
718 g_model
.moduleData
[EXTERNAL_MODULE
].channelsStart
= 0;
719 g_model
.moduleData
[EXTERNAL_MODULE
].channelsCount
= DEFAULT_CHANNELS(EXTERNAL_MODULE
);
720 if (IS_MODULE_SBUS(EXTERNAL_MODULE
))
721 g_model
.moduleData
[EXTERNAL_MODULE
].sbus
.refreshRate
= -31;
725 if (IS_MODULE_DSM2(EXTERNAL_MODULE
))
726 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[EXTERNAL_MODULE
].rfProtocol
, DSM2_PROTO_LP45
, DSM2_PROTO_DSMX
);
727 else if (IS_MODULE_R9M(EXTERNAL_MODULE
))
728 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[EXTERNAL_MODULE
].subType
, MODULE_SUBTYPE_R9M_FCC
, MODULE_SUBTYPE_R9M_LBT
);
729 #if defined(MULTIMODULE)
730 else if (IS_MODULE_MULTIMODULE(EXTERNAL_MODULE
)) {
731 int multiRfProto
= g_model
.moduleData
[EXTERNAL_MODULE
].multi
.customProto
== 1 ? MM_RF_PROTO_CUSTOM
: g_model
.moduleData
[EXTERNAL_MODULE
].getMultiProtocol(false);
732 CHECK_INCDEC_MODELVAR(event
, multiRfProto
, MM_RF_PROTO_FIRST
, MM_RF_PROTO_LAST
);
733 if (checkIncDec_Ret
) {
734 g_model
.moduleData
[EXTERNAL_MODULE
].multi
.customProto
= (multiRfProto
== MM_RF_PROTO_CUSTOM
);
735 if (!g_model
.moduleData
[EXTERNAL_MODULE
].multi
.customProto
)
736 g_model
.moduleData
[EXTERNAL_MODULE
].setMultiProtocol(multiRfProto
);
737 g_model
.moduleData
[EXTERNAL_MODULE
].subType
= 0;
738 // Sensible default for DSM2 (same as for ppm): 7ch@22ms + Autodetect settings enabled
739 if (g_model
.moduleData
[EXTERNAL_MODULE
].getMultiProtocol(true) == MM_RF_PROTO_DSM2
) {
740 g_model
.moduleData
[EXTERNAL_MODULE
].multi
.autoBindMode
= 1;
743 g_model
.moduleData
[EXTERNAL_MODULE
].multi
.autoBindMode
= 0;
745 g_model
.moduleData
[EXTERNAL_MODULE
].multi
.optionValue
= 0;
750 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[EXTERNAL_MODULE
].rfProtocol
, RF_PROTO_X16
, RF_PROTO_LAST
);
752 if (checkIncDec_Ret
) {
753 g_model
.moduleData
[EXTERNAL_MODULE
].channelsStart
= 0;
754 g_model
.moduleData
[EXTERNAL_MODULE
].channelsCount
= DEFAULT_CHANNELS(EXTERNAL_MODULE
);
755 g_model
.moduleData
[EXTERNAL_MODULE
].channelsCount
= MAX_EXTERNAL_MODULE_CHANNELS();
758 #if defined(MULTIMODULE)
760 if (g_model
.moduleData
[EXTERNAL_MODULE
].multi
.customProto
) {
761 g_model
.moduleData
[EXTERNAL_MODULE
].setMultiProtocol(checkIncDec(event
, g_model
.moduleData
[EXTERNAL_MODULE
].getMultiProtocol(false), 0, 63, EE_MODEL
));
764 const mm_protocol_definition
*pdef
= getMultiProtocolDefinition(g_model
.moduleData
[EXTERNAL_MODULE
].getMultiProtocol(false));
765 if (pdef
->maxSubtype
> 0)
766 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[EXTERNAL_MODULE
].subType
, 0, pdef
->maxSubtype
);
770 // Custom protocol, third column is subtype
771 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[EXTERNAL_MODULE
].subType
, 0, 7);
778 case ITEM_MODEL_TRAINER_LABEL
:
779 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_TRAINER
);
782 case ITEM_MODEL_TRAINER_LINE1
:
783 if (g_model
.trainerMode
== TRAINER_MODE_MASTER_BLUETOOTH
) {
787 if (bluetoothDistantAddr
[0]) {
788 lcdDrawText(MENUS_MARGIN_LEFT
+ INDENT_WIDTH
, y
, bluetoothDistantAddr
);
789 if (bluetoothState
!= BLUETOOTH_STATE_CONNECTED
) {
790 drawButton(MODEL_SETUP_2ND_COLUMN
, y
, "Bind", menuHorizontalPosition
== 0 ? attr
: 0);
791 drawButton(MODEL_SETUP_2ND_COLUMN
+60, y
, "Clear", menuHorizontalPosition
== 1 ? attr
: 0);
794 drawButton(MODEL_SETUP_2ND_COLUMN
, y
, "Clear", attr
);
796 if (attr
&& event
== EVT_KEY_FIRST(KEY_ENTER
)) {
797 if (bluetoothState
== BLUETOOTH_STATE_CONNECTED
|| menuHorizontalPosition
== 1) {
798 bluetoothState
= BLUETOOTH_STATE_OFF
;
799 bluetoothDistantAddr
[0] = 0;
802 bluetoothState
= BLUETOOTH_STATE_BIND_REQUESTED
;
807 lcdDrawText(MENUS_MARGIN_LEFT
+ INDENT_WIDTH
, y
, "---");
808 if (bluetoothState
< BLUETOOTH_STATE_IDLE
)
809 drawButton(MODEL_SETUP_2ND_COLUMN
, y
, "Init", attr
);
811 drawButton(MODEL_SETUP_2ND_COLUMN
, y
, "Discover", attr
);
812 if (attr
&& event
== EVT_KEY_FIRST(KEY_ENTER
)) {
813 if (bluetoothState
< BLUETOOTH_STATE_IDLE
)
814 bluetoothState
= BLUETOOTH_STATE_OFF
;
816 bluetoothState
= BLUETOOTH_STATE_DISCOVER_REQUESTED
;
823 case ITEM_MODEL_INTERNAL_MODULE_CHANNELS
:
824 case ITEM_MODEL_EXTERNAL_MODULE_CHANNELS
:
826 uint8_t moduleIdx
= CURRENT_MODULE_EDITED(k
);
827 ModuleData
& moduleData
= g_model
.moduleData
[moduleIdx
];
828 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_CHANNELRANGE
);
829 if ((int8_t)PORT_CHANNELS_ROWS(moduleIdx
) >= 0) {
830 drawStringWithIndex(MODEL_SETUP_2ND_COLUMN
, y
, STR_CH
, moduleData
.channelsStart
+1, menuHorizontalPosition
==0 ? attr
: 0);
831 lcdDrawText(lcdNextPos
+5, y
, "-");
832 drawStringWithIndex(lcdNextPos
+5, y
, STR_CH
, moduleData
.channelsStart
+NUM_CHANNELS(moduleIdx
), menuHorizontalPosition
==1 ? attr
: 0);
833 if (IS_R9M_OR_XJTD16(moduleIdx
)) {
834 if (NUM_CHANNELS(moduleIdx
) > 8)
835 lcdDrawText(lcdNextPos
+ 15, y
, "(18ms)");
837 lcdDrawText(lcdNextPos
+ 15, y
, "(9ms)");
839 if (attr
&& s_editMode
>0) {
840 switch (menuHorizontalPosition
) {
842 CHECK_INCDEC_MODELVAR_ZERO(event
, moduleData
.channelsStart
, 32-8-moduleData
.channelsCount
);
845 CHECK_INCDEC_MODELVAR(event
, moduleData
.channelsCount
, -4, min
<int8_t>(MAX_CHANNELS(moduleIdx
), 32-8-moduleData
.channelsStart
));
846 if ((k
== ITEM_MODEL_EXTERNAL_MODULE_CHANNELS
&& g_model
.moduleData
[EXTERNAL_MODULE
].type
== MODULE_TYPE_PPM
)
847 || (k
== ITEM_MODEL_TRAINER_LINE1
)
849 SET_DEFAULT_PPM_FRAME_LENGTH(moduleIdx
);
857 case ITEM_MODEL_INTERNAL_MODULE_BIND
:
858 case ITEM_MODEL_EXTERNAL_MODULE_BIND
:
859 case ITEM_MODEL_TRAINER_LINE2
:
861 uint8_t moduleIdx
= CURRENT_MODULE_EDITED(k
);
862 ModuleData
& moduleData
= g_model
.moduleData
[moduleIdx
];
863 if (IS_MODULE_PPM(moduleIdx
)) {
864 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_PPMFRAME
);
865 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN
, y
, (int16_t)moduleData
.ppm
.frameLength
*5 + 225, (menuHorizontalPosition
<=0 ? attr
: 0) | PREC1
|LEFT
, 0, NULL
, STR_MS
);
866 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN
+80, y
, (moduleData
.ppm
.delay
*50)+300, (CURSOR_ON_LINE() || menuHorizontalPosition
==1) ? attr
|LEFT
: LEFT
, 0, NULL
, "us");
867 lcdDrawText(MODEL_SETUP_2ND_COLUMN
+160, y
, moduleData
.ppm
.pulsePol
? "+" : "-", (CURSOR_ON_LINE() || menuHorizontalPosition
==2) ? attr
: 0);
868 if (attr
&& s_editMode
>0) {
869 switch (menuHorizontalPosition
) {
871 CHECK_INCDEC_MODELVAR(event
, moduleData
.ppm
.frameLength
, -20, 35);
874 CHECK_INCDEC_MODELVAR(event
, moduleData
.ppm
.delay
, -4, 10);
877 CHECK_INCDEC_MODELVAR_ZERO(event
, moduleData
.ppm
.pulsePol
, 1);
882 else if (IS_MODULE_SBUS(moduleIdx
)) {
883 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_REFRESHRATE
);
884 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN
, y
, (int16_t)moduleData
.ppm
.frameLength
*5 + 225, (menuHorizontalPosition
<=0 ? attr
: 0) | PREC1
|LEFT
, 0, NULL
, STR_MS
);
885 lcdDrawText(MODEL_SETUP_3RD_COLUMN
, y
, moduleData
.sbus
.noninverted
? "not inverted" : "normal", (CURSOR_ON_LINE() || menuHorizontalPosition
==1) ? attr
: 0);
887 if (attr
&& s_editMode
>0) {
888 switch (menuHorizontalPosition
) {
890 CHECK_INCDEC_MODELVAR(event
, moduleData
.ppm
.frameLength
, -33, 35);
893 CHECK_INCDEC_MODELVAR_ZERO(event
, moduleData
.sbus
.noninverted
, 1);
899 int l_posHorz
= menuHorizontalPosition
;
900 coord_t xOffsetBind
= MODEL_SETUP_BIND_OFS
;
901 if (IS_MODULE_XJT(moduleIdx
) && IS_D8_RX(moduleIdx
)) {
903 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_RECEIVER
);
904 if (attr
) l_posHorz
+= 1;
907 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_RECEIVER_NUM
);
909 if (IS_MODULE_PXX(moduleIdx
) || IS_MODULE_DSM2(moduleIdx
) || IS_MODULE_MULTIMODULE(moduleIdx
)) {
911 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN
, y
, g_model
.header
.modelId
[moduleIdx
], (l_posHorz
==0 ? attr
: 0) | LEADING0
| LEFT
, 2);
912 if (attr
&& l_posHorz
==0 && s_editMode
>0)
913 CHECK_INCDEC_MODELVAR_ZERO(event
, g_model
.header
.modelId
[moduleIdx
], MAX_RX_NUM(moduleIdx
));
914 drawButton(MODEL_SETUP_2ND_COLUMN
+xOffsetBind
, y
, STR_MODULE_BIND
, (moduleFlag
[moduleIdx
] == MODULE_BIND
? BUTTON_ON
: BUTTON_OFF
) | (l_posHorz
==1 ? attr
: 0));
915 drawButton(MODEL_SETUP_2ND_COLUMN
+MODEL_SETUP_RANGE_OFS
+xOffsetBind
, y
, STR_MODULE_RANGE
, (moduleFlag
[moduleIdx
] == MODULE_RANGECHECK
? BUTTON_ON
: BUTTON_OFF
) | (l_posHorz
==2 ? attr
: 0));
917 #if defined(MULTIMODULE)
918 if (multiBindStatus
== MULTI_BIND_FINISHED
) {
919 multiBindStatus
= MULTI_NORMAL_OPERATION
;
923 if (attr
&& l_posHorz
>0) {
925 if (l_posHorz
== 1) {
926 if (IS_MODULE_R9M(moduleIdx
) || (IS_MODULE_XJT(moduleIdx
) && g_model
.moduleData
[moduleIdx
].rfProtocol
== RF_PROTO_X16
)) {
927 if (event
== EVT_KEY_BREAK(KEY_ENTER
)) {
928 uint8_t default_selection
;
929 if (IS_MODULE_R9M_LBT(moduleIdx
)) {
930 if (IS_TELEMETRY_INTERNAL_MODULE())
931 POPUP_MENU_ADD_ITEM(STR_DISABLE_INTERNAL
);
933 POPUP_MENU_ADD_ITEM(STR_BINDING_25MW_CH1_8_TELEM_ON
);
934 POPUP_MENU_ADD_ITEM(STR_BINDING_25MW_CH1_8_TELEM_OFF
);
935 POPUP_MENU_ADD_ITEM(STR_BINDING_500MW_CH1_8_TELEM_OFF
);
936 POPUP_MENU_ADD_ITEM(STR_BINDING_500MW_CH9_16_TELEM_OFF
);
937 default_selection
= 2;
940 POPUP_MENU_ADD_ITEM(STR_BINDING_1_8_TELEM_ON
);
941 POPUP_MENU_ADD_ITEM(STR_BINDING_1_8_TELEM_OFF
);
942 POPUP_MENU_ADD_ITEM(STR_BINDING_9_16_TELEM_ON
);
943 POPUP_MENU_ADD_ITEM(STR_BINDING_9_16_TELEM_OFF
);
944 default_selection
= g_model
.moduleData
[moduleIdx
].pxx
.receiver_telem_off
+ (g_model
.moduleData
[moduleIdx
].pxx
.receiver_channel_9_16
<< 1);
946 POPUP_MENU_SELECT_ITEM(default_selection
);
947 POPUP_MENU_START(onBindMenu
);
950 if (moduleFlag
[moduleIdx
] == MODULE_BIND
) {
951 newFlag
= MODULE_BIND
;
954 if (!popupMenuNoItems
) {
955 s_editMode
= 0; // this is when popup is exited before a choice is made
960 newFlag
= MODULE_BIND
;
963 else if (l_posHorz
== 2) {
964 newFlag
= MODULE_RANGECHECK
;
968 moduleFlag
[moduleIdx
] = newFlag
;
969 #if defined(MULTIMODULE)
970 if (newFlag
== MODULE_BIND
)
971 multiBindStatus
= MULTI_BIND_INITIATED
;
978 case ITEM_MODEL_INTERNAL_MODULE_FAILSAFE
:
979 case ITEM_MODEL_EXTERNAL_MODULE_FAILSAFE
:
981 uint8_t moduleIdx
= CURRENT_MODULE_EDITED(k
);
982 ModuleData
& moduleData
= g_model
.moduleData
[moduleIdx
];
983 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_FAILSAFE
);
984 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
, y
, STR_VFAILSAFE
, moduleData
.failsafeMode
, menuHorizontalPosition
==0 ? attr
: 0);
985 if (moduleData
.failsafeMode
== FAILSAFE_CUSTOM
) {
986 drawButton(MODEL_SETUP_2ND_COLUMN
+ MODEL_SETUP_SET_FAILSAFE_OFS
, y
, STR_SET
, menuHorizontalPosition
==1 ? attr
: 0);
989 if (moduleData
.failsafeMode
!= FAILSAFE_CUSTOM
)
990 menuHorizontalPosition
= 0;
991 if (menuHorizontalPosition
==0) {
993 CHECK_INCDEC_MODELVAR_ZERO(event
, moduleData
.failsafeMode
, FAILSAFE_LAST
);
994 if (checkIncDec_Ret
) SEND_FAILSAFE_NOW(moduleIdx
);
997 else if (menuHorizontalPosition
==1) {
999 if (moduleData
.failsafeMode
==FAILSAFE_CUSTOM
&& event
==EVT_KEY_FIRST(KEY_ENTER
)) {
1000 g_moduleIdx
= moduleIdx
;
1001 pushMenu(menuModelFailsafe
);
1005 lcdDrawSolidFilledRect(MODEL_SETUP_2ND_COLUMN
, y
, LCD_W
- MODEL_SETUP_2ND_COLUMN
- 2, 8, TEXT_COLOR
);
1011 case ITEM_MODEL_EXTERNAL_MODULE_OPTIONS
:
1013 uint8_t moduleIdx
= CURRENT_MODULE_EDITED(k
);
1014 #if defined(MULTIMODULE)
1015 if (IS_MODULE_MULTIMODULE(moduleIdx
)) {
1016 int optionValue
= g_model
.moduleData
[moduleIdx
].multi
.optionValue
;
1018 const uint8_t multi_proto
= g_model
.moduleData
[EXTERNAL_MODULE
].getMultiProtocol(true);
1019 const mm_protocol_definition
*pdef
= getMultiProtocolDefinition(multi_proto
);
1020 if (pdef
->optionsstr
)
1021 lcdDrawText(MENUS_MARGIN_LEFT
, y
, pdef
->optionsstr
);
1023 if (multi_proto
== MM_RF_PROTO_FS_AFHDS2A
)
1024 optionValue
= 50 + 5 * optionValue
;
1026 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN
, y
, optionValue
, LEFT
| attr
);
1028 if (multi_proto
== MM_RF_PROTO_FS_AFHDS2A
) {
1029 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[moduleIdx
].multi
.optionValue
, 0, 70);
1031 else if (multi_proto
== MM_RF_PROTO_OLRS
) {
1032 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[moduleIdx
].multi
.optionValue
, -1, 7);
1035 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[moduleIdx
].multi
.optionValue
, -128, 127);
1040 if (IS_MODULE_R9M_FCC(moduleIdx
)) {
1041 if (IS_TELEMETRY_INTERNAL_MODULE()) {
1042 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_MODULE_TELEMETRY
);
1043 lcdDrawText(MODEL_SETUP_2ND_COLUMN
, y
, STR_DISABLE_INTERNAL
);
1046 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_MODULE_TELEMETRY
);
1047 g_model
.moduleData
[moduleIdx
].pxx
.sport_out
= editCheckBox(g_model
.moduleData
[EXTERNAL_MODULE
].pxx
.sport_out
, MODEL_SETUP_2ND_COLUMN
, y
, attr
, event
);
1050 else if (IS_MODULE_R9M_LBT(moduleIdx
)) {
1051 if (IS_TELEMETRY_INTERNAL_MODULE()) {
1052 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_MODULE_TELEMETRY
);
1053 lcdDrawText(MODEL_SETUP_2ND_COLUMN
, y
, STR_DISABLE_INTERNAL
);
1056 lcdDrawText(MENUS_MARGIN_LEFT
,y
, STR_MODULE_TELEMETRY
);
1057 lcdDrawText(MODEL_SETUP_2ND_COLUMN
, y
, STR_BINDING_OPTION
);
1060 else if (IS_MODULE_SBUS(moduleIdx
)) {
1061 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_WARN_BATTVOLTAGE
);
1062 drawValueWithUnit(MODEL_SETUP_4TH_COLUMN
, y
, getBatteryVoltage(), UNIT_VOLTS
, attr
|PREC2
|LEFT
);
1067 case ITEM_MODEL_EXTERNAL_MODULE_POWER
:
1069 uint8_t moduleIdx
= CURRENT_MODULE_EDITED(k
);
1070 if (IS_MODULE_R9M_FCC(moduleIdx
)) {
1071 // Power selection is only available on R9M FCC
1072 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_MULTI_RFPOWER
);
1073 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
, y
, STR_R9M_FCC_POWER_VALUES
, g_model
.moduleData
[moduleIdx
].pxx
.power
, LEFT
| attr
);
1075 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[moduleIdx
].pxx
.power
, 0, R9M_FCC_POWER_MAX
);
1077 #if defined(MULTIMODULE)
1078 else if (IS_MODULE_MULTIMODULE(moduleIdx
)) {
1079 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_MULTI_LOWPOWER
);
1080 g_model
.moduleData
[EXTERNAL_MODULE
].multi
.lowPowerMode
= editCheckBox(g_model
.moduleData
[EXTERNAL_MODULE
].multi
.lowPowerMode
, MODEL_SETUP_2ND_COLUMN
, y
, attr
, event
);
1086 #if defined(MULTIMODULE)
1087 case ITEM_MODEL_EXTERNAL_MODULE_AUTOBIND
:
1088 if (g_model
.moduleData
[EXTERNAL_MODULE
].getMultiProtocol(true) == MM_RF_PROTO_DSM2
)
1089 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_MULTI_DSM_AUTODTECT
);
1091 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_MULTI_AUTOBIND
);
1092 g_model
.moduleData
[EXTERNAL_MODULE
].multi
.autoBindMode
= editCheckBox(g_model
.moduleData
[EXTERNAL_MODULE
].multi
.autoBindMode
, MODEL_SETUP_2ND_COLUMN
, y
, attr
, event
);
1094 case ITEM_MODEL_EXTERNAL_MODULE_STATUS
: {
1095 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_MODULE_STATUS
);
1097 char statusText
[64];
1098 multiModuleStatus
.getStatusString(statusText
);
1099 lcdDrawText(MODEL_SETUP_2ND_COLUMN
, y
, statusText
);
1101 case ITEM_MODEL_EXTERNAL_MODULE_SYNCSTATUS
: {
1102 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_MODULE_SYNC
);
1104 char statusText
[64];
1105 multiSyncStatus
.getRefreshString(statusText
);
1106 lcdDrawText(MODEL_SETUP_2ND_COLUMN
, y
, statusText
);
1114 if (IS_RANGECHECK_ENABLE()) {
1115 theme
->drawMessageBox("RSSI :", NULL
, NULL
, WARNING_TYPE_INFO
);
1116 lcdDrawNumber(WARNING_LINE_X
, WARNING_INFOLINE_Y
, TELEMETRY_RSSI(), DBLSIZE
|LEFT
);
1122 bool menuModelFailsafe(event_t event
)
1125 const uint8_t channelStart
= g_model
.moduleData
[g_moduleIdx
].channelsStart
;
1126 const int lim
= (g_model
.extendedLimits
? (512 * LIMIT_EXT_PERCENT
/ 100) : 512) * 2;
1127 const uint8_t SLIDER_W
= 128;
1128 const uint8_t cols
= NUM_CHANNELS(g_moduleIdx
) > 8 ? 2 : 1;
1130 if (event
== EVT_KEY_LONG(KEY_ENTER
)) {
1134 g_model
.moduleData
[g_moduleIdx
].failsafeChannels
[menuVerticalPosition
] = channelOutputs
[menuVerticalPosition
+channelStart
];
1135 storageDirty(EE_MODEL
);
1138 SEND_FAILSAFE_NOW(g_moduleIdx
);
1141 int16_t & failsafe
= g_model
.moduleData
[g_moduleIdx
].failsafeChannels
[menuVerticalPosition
];
1142 if (failsafe
< FAILSAFE_CHANNEL_HOLD
)
1143 failsafe
= FAILSAFE_CHANNEL_HOLD
;
1144 else if (failsafe
== FAILSAFE_CHANNEL_HOLD
)
1145 failsafe
= FAILSAFE_CHANNEL_NOPULSE
;
1148 storageDirty(EE_MODEL
);
1150 SEND_FAILSAFE_NOW(g_moduleIdx
);
1154 SIMPLE_SUBMENU_WITH_OPTIONS("FAILSAFE", ICON_STATS_ANALOGS
, NUM_CHANNELS(g_moduleIdx
), OPTION_MENU_NO_SCROLLBAR
);
1155 drawStringWithIndex(50, 3+FH
, "Module", g_moduleIdx
+1, MENU_TITLE_COLOR
);
1157 for (uint8_t col
=0; col
< cols
; col
++) {
1158 for (uint8_t line
=0; line
< 8; line
++) {
1159 coord_t x
= col
*(LCD_W
/2);
1160 const coord_t y
= MENU_CONTENT_TOP
- FH
+ line
*(FH
+4);
1161 const int32_t channelValue
= channelOutputs
[ch
+channelStart
];
1162 int32_t failsafeValue
= g_model
.moduleData
[g_moduleIdx
].failsafeChannels
[8*col
+line
];
1164 // Channel name if present, number if not
1165 if (g_model
.limitData
[ch
+channelStart
].name
[0] != '\0') {
1166 putsChn(x
+MENUS_MARGIN_LEFT
, y
-3, ch
+1, TINSIZE
);
1167 lcdDrawSizedText(x
+MENUS_MARGIN_LEFT
, y
+5, g_model
.limitData
[ch
+channelStart
].name
, sizeof(g_model
.limitData
[ch
+channelStart
].name
), ZCHAR
|SMLSIZE
);
1170 putsChn(x
+MENUS_MARGIN_LEFT
, y
, ch
+1, 0);
1174 LcdFlags flags
= RIGHT
;
1175 if (menuVerticalPosition
== ch
) {
1178 if (failsafeValue
== FAILSAFE_CHANNEL_HOLD
|| failsafeValue
== FAILSAFE_CHANNEL_NOPULSE
) {
1183 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[g_moduleIdx
].failsafeChannels
[8*col
+line
], -lim
, +lim
);
1188 x
+= (LCD_W
/2)-4-MENUS_MARGIN_LEFT
-SLIDER_W
;
1190 if (failsafeValue
== FAILSAFE_CHANNEL_HOLD
) {
1191 lcdDrawText(x
, y
+2, "HOLD", flags
|SMLSIZE
);
1194 else if (failsafeValue
== FAILSAFE_CHANNEL_NOPULSE
) {
1195 lcdDrawText(x
, y
+2, "NONE", flags
|SMLSIZE
);
1199 #if defined(PPM_UNIT_US)
1200 lcdDrawNumber(x
, y
, PPM_CH_CENTER(ch
)+failsafeValue
/2, flags
);
1201 #elif defined(PPM_UNIT_PERCENT_PREC1)
1202 lcdDrawNumber(x
, y
, calcRESXto1000(failsafeValue
), PREC1
|flags
);
1204 lcdDrawNumber(x
, y
, calcRESXto1000(failsafeValue
)/10, flags
);
1210 lcdDrawRect(x
, y
+3, SLIDER_W
+1, 12);
1211 const coord_t lenChannel
= limit((uint8_t)1, uint8_t((abs(channelValue
) * SLIDER_W
/2 + lim
/2) / lim
), uint8_t(SLIDER_W
/2));
1212 const coord_t lenFailsafe
= limit((uint8_t)1, uint8_t((abs(failsafeValue
) * SLIDER_W
/2 + lim
/2) / lim
), uint8_t(SLIDER_W
/2));
1214 const coord_t xChannel
= (channelValue
>0) ? x
: x
+1-lenChannel
;
1215 const coord_t xFailsafe
= (failsafeValue
>0) ? x
: x
+1-lenFailsafe
;
1216 lcdDrawSolidFilledRect(xChannel
, y
+4, lenChannel
, 5, TEXT_COLOR
);
1217 lcdDrawSolidFilledRect(xFailsafe
, y
+9, lenFailsafe
, 5, ALARM_COLOR
);
1219 if (++ch
>= NUM_CHANNELS(g_moduleIdx
))