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 void menuModelFailsafe(event_t event
);
28 #if defined(PCBTARANIS)
29 uint8_t getSwitchWarningsCount()
32 for (int i
=0; i
<NUM_SWITCHES
; ++i
) {
33 if (SWITCH_WARNING_ALLOWED(i
)) {
41 enum MenuModelSetupItems
{
44 CASE_CPUARM(ITEM_MODEL_TIMER1_NAME
)
45 CASE_PERSISTENT_TIMERS(ITEM_MODEL_TIMER1_PERSISTENT
)
46 ITEM_MODEL_TIMER1_MINUTE_BEEP
,
47 ITEM_MODEL_TIMER1_COUNTDOWN_BEEP
,
49 CASE_CPUARM(ITEM_MODEL_TIMER2_NAME
)
50 CASE_PERSISTENT_TIMERS(ITEM_MODEL_TIMER2_PERSISTENT
)
51 ITEM_MODEL_TIMER2_MINUTE_BEEP
,
52 ITEM_MODEL_TIMER2_COUNTDOWN_BEEP
,
53 CASE_CPUARM(ITEM_MODEL_TIMER3
)
54 CASE_CPUARM(ITEM_MODEL_TIMER3_NAME
)
55 CASE_CPUARM(ITEM_MODEL_TIMER3_PERSISTENT
)
56 CASE_CPUARM(ITEM_MODEL_TIMER3_MINUTE_BEEP
)
57 CASE_CPUARM(ITEM_MODEL_TIMER3_COUNTDOWN_BEEP
)
58 ITEM_MODEL_EXTENDED_LIMITS
,
59 ITEM_MODEL_EXTENDED_TRIMS
,
60 CASE_CPUARM(ITEM_MODEL_DISPLAY_TRIMS
)
62 ITEM_MODEL_THROTTLE_REVERSED
,
63 ITEM_MODEL_THROTTLE_TRACE
,
64 ITEM_MODEL_THROTTLE_TRIM
,
65 CASE_CPUARM(ITEM_MODEL_PREFLIGHT_LABEL
)
66 CASE_CPUARM(ITEM_MODEL_CHECKLIST_DISPLAY
)
67 ITEM_MODEL_THROTTLE_WARNING
,
68 ITEM_MODEL_SWITCHES_WARNING
,
69 #if defined(PCBTARANIS)
70 ITEM_MODEL_SWITCHES_WARNING2
,
71 ITEM_MODEL_POTS_WARNING
,
73 ITEM_MODEL_BEEP_CENTER
,
74 CASE_CPUARM(ITEM_MODEL_USE_GLOBAL_FUNCTIONS
)
75 #if defined(PCBTARANIS)
76 ITEM_MODEL_INTERNAL_MODULE_LABEL
,
77 ITEM_MODEL_INTERNAL_MODULE_MODE
,
78 ITEM_MODEL_INTERNAL_MODULE_CHANNELS
,
79 ITEM_MODEL_INTERNAL_MODULE_BIND
,
80 ITEM_MODEL_INTERNAL_MODULE_FAILSAFE
,
82 ITEM_MODEL_INTERNAL_MODULE_ANTENNA
,
86 ITEM_MODEL_EXTERNAL_MODULE_LABEL
,
87 ITEM_MODEL_EXTERNAL_MODULE_MODE
,
88 #if defined(MULTIMODULE)
89 ITEM_MODEL_EXTERNAL_MODULE_SUBTYPE
,
90 ITEM_MODEL_EXTERNAL_MODULE_STATUS
,
91 ITEM_MODEL_EXTERNAL_MODULE_SYNCSTATUS
,
93 ITEM_MODEL_EXTERNAL_MODULE_CHANNELS
,
94 ITEM_MODEL_EXTERNAL_MODULE_BIND
,
95 #if defined(PCBSKY9X) && defined(REVX)
96 ITEM_MODEL_EXTERNAL_MODULE_OUTPUT_TYPE
,
98 ITEM_MODEL_EXTERNAL_MODULE_FAILSAFE
,
99 ITEM_MODEL_EXTERNAL_MODULE_OPTIONS
,
100 #if defined(MULTIMODULE)
101 ITEM_MODEL_EXTERNAL_MODULE_AUTOBIND
,
103 ITEM_MODEL_EXTERNAL_MODULE_POWER
,
104 #if defined(PCBSKY9X) && !defined(REVA)
105 ITEM_MODEL_EXTRA_MODULE_LABEL
,
106 ITEM_MODEL_EXTRA_MODULE_CHANNELS
,
107 ITEM_MODEL_EXTRA_MODULE_BIND
,
110 ITEM_MODEL_PPM1_PROTOCOL
,
111 ITEM_MODEL_PPM1_PARAMS
,
114 ITEM_MODEL_TRAINER_LABEL
,
115 ITEM_MODEL_TRAINER_MODE
,
116 #if defined(BLUETOOTH)
117 ITEM_MODEL_TRAINER_BLUETOOTH
,
119 ITEM_MODEL_TRAINER_CHANNELS
,
120 ITEM_MODEL_TRAINER_PARAMS
,
121 #elif defined(PCBXLITE)
122 ITEM_MODEL_TRAINER_LABEL
,
123 ITEM_MODEL_TRAINER_MODE
,
124 ITEM_MODEL_TRAINER_BLUETOOTH
,
125 ITEM_MODEL_TRAINER_CHANNELS
,
130 #if defined(PCBSKY9X)
131 #define FIELD_PROTOCOL_MAX 2
133 #define FIELD_PROTOCOL_MAX 1
136 #define MODEL_SETUP_2ND_COLUMN (LCD_W-11*FW)
137 #define MODEL_SETUP_BIND_OFS 2*FW+1
138 #define MODEL_SETUP_RANGE_OFS 4*FW+3
139 #define MODEL_SETUP_SET_FAILSAFE_OFS 7*FW-2
141 #if defined(PCBTARANIS)
142 #define CURRENT_MODULE_EDITED(k) (k>=ITEM_MODEL_TRAINER_LABEL ? TRAINER_MODULE : (k>=ITEM_MODEL_EXTERNAL_MODULE_LABEL ? EXTERNAL_MODULE : INTERNAL_MODULE))
143 #elif defined(PCBSKY9X) && !defined(REVA)
144 #define CURRENT_MODULE_EDITED(k) (k>=ITEM_MODEL_EXTRA_MODULE_LABEL ? EXTRA_MODULE : EXTERNAL_MODULE)
146 #define CURRENT_MODULE_EDITED(k) (EXTERNAL_MODULE)
150 #if defined(PCBXLITE)
151 #define SW_WARN_ROWS uint8_t(NAVIGATION_LINE_BY_LINE|getSwitchWarningsCount()), uint8_t(getSwitchWarningsCount() > 5 ? TITLE_ROW : HIDDEN_ROW) // X-Lite needs an additional column for full line selection (<])
153 #define SW_WARN_ROWS uint8_t(NAVIGATION_LINE_BY_LINE|(getSwitchWarningsCount()-1)), uint8_t(getSwitchWarningsCount() > 5 ? TITLE_ROW : HIDDEN_ROW)
155 #if !defined(TARANIS_INTERNAL_PPM)
156 #define INTERNAL_MODULE_MODE_ROWS 0 // (OFF / RF protocols)
158 #define INTERNAL_MODULE_MODE_ROWS (IS_MODULE_XJT(INTERNAL_MODULE) ? (uint8_t)1 : (uint8_t)0) // Module type + RF protocols
160 #define IF_INTERNAL_MODULE_ON(x) (IS_INTERNAL_MODULE_ENABLED()? (uint8_t)(x) : HIDDEN_ROW )
161 #define IF_EXTERNAL_MODULE_ON(x) (IS_EXTERNAL_MODULE_ENABLED()? (uint8_t)(x) : HIDDEN_ROW)
162 #define INTERNAL_MODULE_CHANNELS_ROWS IF_INTERNAL_MODULE_ON(1)
163 #define EXTERNAL_MODULE_BIND_ROWS() ((IS_MODULE_XJT(EXTERNAL_MODULE) && IS_D8_RX(EXTERNAL_MODULE)) || 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
165 #if defined(PCBSKY9X) && defined(REVX)
166 #define OUTPUT_TYPE_ROWS() (IS_MODULE_PPM(EXTERNAL_MODULE) ? (uint8_t)0 : HIDDEN_ROW) ,
168 #define OUTPUT_TYPE_ROWS()
170 #define PORT_CHANNELS_ROWS(x) (x==EXTERNAL_MODULE ? EXTERNAL_MODULE_CHANNELS_ROWS : 0)
172 #define EXTERNAL_MODULE_MODE_ROWS (IS_MODULE_PXX(EXTERNAL_MODULE) || IS_MODULE_DSM2(EXTERNAL_MODULE) || IS_MODULE_MULTIMODULE(EXTERNAL_MODULE)) ? (uint8_t)1 : (uint8_t)0
174 #define CURSOR_ON_CELL (true)
175 #define MODEL_SETUP_MAX_LINES (HEADER_LINE+ITEM_MODEL_SETUP_MAX)
176 #define POT_WARN_ITEMS() ((g_model.potsWarnMode) ? (uint8_t)(NUM_POTS+NUM_SLIDERS) : (uint8_t)0)
177 #define TIMER_ROWS 2, 0, CASE_PERSISTENT_TIMERS(0) 0, 0
178 #if defined(PCBSKY9X) && !defined(REVA)
179 #define EXTRA_MODULE_ROWS LABEL(ExtraModule), 1, 2,
181 #define EXTRA_MODULE_ROWS
186 #if defined(BLUETOOTH)
187 #define TRAINER_BLUETOOTH_M_ROW ((bluetoothDistantAddr[0] == '\0' || bluetoothState == BLUETOOTH_STATE_CONNECTED) ? (uint8_t)0 : (uint8_t)1)
188 #define TRAINER_BLUETOOTH_S_ROW (bluetoothDistantAddr[0] == '\0' ? HIDDEN_ROW : LABEL())
189 #define TRAINER_BLUETOOTH_ROW (g_model.trainerMode == TRAINER_MODE_MASTER_BLUETOOTH ? TRAINER_BLUETOOTH_M_ROW : (g_model.trainerMode == TRAINER_MODE_SLAVE_BLUETOOTH ? TRAINER_BLUETOOTH_S_ROW : HIDDEN_ROW)),
191 #define TRAINER_BLUETOOTH_ROW
193 #define TRAINER_CHANNELS_ROW (IS_SLAVE_TRAINER() ? (uint8_t)1 : HIDDEN_ROW)
194 #define TRAINER_PARAMS_ROW (IS_SLAVE_TRAINER() ? (uint8_t)2 : HIDDEN_ROW)
195 #define TRAINER_ROWS LABEL(Trainer), 0, TRAINER_BLUETOOTH_ROW TRAINER_CHANNELS_ROW, TRAINER_PARAMS_ROW
196 #elif defined(PCBXLITE)
197 #define ANTENNA_ROW IF_INTERNAL_MODULE_ON(0),
198 #define IF_BT_TRAINER_ON(x) (g_eeGeneral.bluetoothMode == BLUETOOTH_TRAINER ? (uint8_t)(x) : HIDDEN_ROW)
199 #define TRAINER_BLUETOOTH_M_ROW ((bluetoothDistantAddr[0] == '\0' || bluetoothState == BLUETOOTH_STATE_CONNECTED) ? (uint8_t)0 : (uint8_t)1)
200 #define TRAINER_BLUETOOTH_S_ROW (bluetoothDistantAddr[0] == '\0' ? HIDDEN_ROW : LABEL())
201 #define TRAINER_BLUETOOTH_ROW (g_model.trainerMode == TRAINER_MODE_MASTER_BLUETOOTH ? TRAINER_BLUETOOTH_M_ROW : (g_model.trainerMode == TRAINER_MODE_SLAVE_BLUETOOTH ? TRAINER_BLUETOOTH_S_ROW : HIDDEN_ROW))
202 #define TRAINER_CHANNELS_ROW (IS_SLAVE_TRAINER() ? (uint8_t)1 : HIDDEN_ROW)
203 #define TRAINER_ROWS IF_BT_TRAINER_ON(LABEL(Trainer)), IF_BT_TRAINER_ON(0), IF_BT_TRAINER_ON(TRAINER_BLUETOOTH_ROW), IF_BT_TRAINER_ON(TRAINER_CHANNELS_ROW)
208 #elif defined(CPUM64)
209 #define CURSOR_ON_CELL (true)
210 #define MODEL_SETUP_MAX_LINES ((IS_PPM_PROTOCOL(protocol)||IS_DSM2_PROTOCOL(protocol)||IS_PXX_PROTOCOL(protocol)) ? HEADER_LINE+ITEM_MODEL_SETUP_MAX : HEADER_LINE+ITEM_MODEL_SETUP_MAX-1)
211 #elif defined(PCBXLITE)
212 #define CURSOR_ON_CELL (menuHorizontalPosition >= 0)
213 #define MODEL_SETUP_MAX_LINES ((IS_PPM_PROTOCOL(protocol)||IS_DSM2_PROTOCOL(protocol)||IS_PXX_PROTOCOL(protocol)) ? HEADER_LINE+ITEM_MODEL_SETUP_MAX : HEADER_LINE+ITEM_MODEL_SETUP_MAX-1)
215 #define CURSOR_ON_CELL (true)
216 #define MODEL_SETUP_MAX_LINES ((IS_PPM_PROTOCOL(protocol)||IS_DSM2_PROTOCOL(protocol)||IS_PXX_PROTOCOL(protocol)) ? HEADER_LINE+ITEM_MODEL_SETUP_MAX : HEADER_LINE+ITEM_MODEL_SETUP_MAX-1)
219 #if defined(PCBTARANIS)
220 void onBindMenu(const char * result
)
222 uint8_t moduleIdx
= CURRENT_MODULE_EDITED(menuVerticalPosition
);
224 if (result
== STR_BINDING_1_8_TELEM_ON
) {
225 g_model
.moduleData
[moduleIdx
].pxx
.receiver_telem_off
= false;
226 g_model
.moduleData
[moduleIdx
].pxx
.receiver_channel_9_16
= false;
228 else if (result
== STR_BINDING_1_8_TELEM_OFF
) {
229 g_model
.moduleData
[moduleIdx
].pxx
.receiver_telem_off
= true;
230 g_model
.moduleData
[moduleIdx
].pxx
.receiver_channel_9_16
= false;
232 else if (result
== STR_BINDING_9_16_TELEM_ON
) {
233 g_model
.moduleData
[moduleIdx
].pxx
.receiver_telem_off
= false;
234 g_model
.moduleData
[moduleIdx
].pxx
.receiver_channel_9_16
= true;
236 else if (result
== STR_BINDING_9_16_TELEM_OFF
) {
237 g_model
.moduleData
[moduleIdx
].pxx
.receiver_telem_off
= true;
238 g_model
.moduleData
[moduleIdx
].pxx
.receiver_channel_9_16
= true;
244 moduleFlag
[moduleIdx
] = MODULE_BIND
;
249 void menuModelSetup(event_t event
)
251 #if defined(PCBXLITE)
252 // Switch to external antenna confirmation
255 g_model
.moduleData
[INTERNAL_MODULE
].pxx
.external_antenna
= XJT_EXTERNAL_ANTENNA
;
260 static uint8_t selectedPxxPower
= g_model
.moduleData
[EXTERNAL_MODULE
].pxx
.power
; //TODO could go to the reusable struct
263 int8_t old_editMode
= s_editMode
;
265 #if defined(PCBTARANIS)
269 TIMER_ROWS
, TIMER_ROWS
, TIMER_ROWS
,
270 0, // Extended limits
274 0, // Throttle reverse
275 0, // Throttle trace source
277 CASE_CPUARM(LABEL(PreflightCheck
))
278 CASE_CPUARM(0) // Checklist
279 0, // Throttle warning
280 SW_WARN_ROWS
, // Switch warning
281 POT_WARN_ITEMS(), // Pot warning
282 NUM_STICKS
+ NUM_POTS
+ NUM_SLIDERS
+ NUM_ROTARY_ENCODERS
- 1, // Center beeps
283 0, // Global functions
284 LABEL(InternalModule
),
285 INTERNAL_MODULE_MODE_ROWS
,
286 INTERNAL_MODULE_CHANNELS_ROWS
,
287 IF_INTERNAL_MODULE_ON(HAS_RF_PROTOCOL_MODELINDEX(g_model
.moduleData
[INTERNAL_MODULE
].rfProtocol
) ? (uint8_t)2 : (uint8_t)1),
288 IF_INTERNAL_MODULE_ON(FAILSAFE_ROWS(INTERNAL_MODULE
)),
290 LABEL(ExternalModule
),
291 EXTERNAL_MODULE_MODE_ROWS
,
292 MULTIMODULE_SUBTYPE_ROWS(EXTERNAL_MODULE
)
293 MULTIMODULE_STATUS_ROWS
294 EXTERNAL_MODULE_CHANNELS_ROWS
,
295 EXTERNAL_MODULE_BIND_ROWS(),
297 FAILSAFE_ROWS(EXTERNAL_MODULE
),
298 EXTERNAL_MODULE_OPTION_ROW
,
299 MULTIMODULE_MODULE_ROWS
300 EXTERNAL_MODULE_POWER_ROW
,
303 #elif defined(CPUARM)
304 MENU_TAB({ HEADER_LINE_COLUMNS
0, TIMER_ROWS
, TIMER_ROWS
, TIMER_ROWS
, 0, 1, 0, 0, 0, 0, 0, CASE_CPUARM(LABEL(PreflightCheck
)) CASE_CPUARM(0) 0, NUM_SWITCHES
-1, NUM_STICKS
+NUM_POTS
+NUM_SLIDERS
+NUM_ROTARY_ENCODERS
-1, 0,
305 LABEL(ExternalModule
),
306 EXTERNAL_MODULE_MODE_ROWS
,
307 MULTIMODULE_SUBTYPE_ROWS(EXTERNAL_MODULE
)
308 MULTIMODULE_STATUS_ROWS
309 EXTERNAL_MODULE_CHANNELS_ROWS
,
310 EXTERNAL_MODULE_BIND_ROWS(),
312 FAILSAFE_ROWS(EXTERNAL_MODULE
),
313 EXTERNAL_MODULE_OPTION_ROW
,
314 MULTIMODULE_MODULE_ROWS
315 EXTERNAL_MODULE_POWER_ROW
,
318 #elif defined(CPUM64)
319 uint8_t protocol
= g_model
.protocol
;
320 MENU_TAB({ HEADER_LINE_COLUMNS
0, 2, CASE_PERSISTENT_TIMERS(0) 0, 0, 2, CASE_PERSISTENT_TIMERS(0) 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, NUM_STICKS
+NUM_POTS
+NUM_SLIDERS
+NUM_ROTARY_ENCODERS
-1, FIELD_PROTOCOL_MAX
, 2 });
322 uint8_t protocol
= g_model
.protocol
;
323 MENU_TAB({ HEADER_LINE_COLUMNS
0, 2, CASE_PERSISTENT_TIMERS(0) 0, 0, 2, CASE_PERSISTENT_TIMERS(0) 0, 0, 0, 1, 0, 0, 0, 0, 0, NUM_SWITCHES
, NUM_STICKS
+NUM_POTS
+NUM_SLIDERS
+NUM_ROTARY_ENCODERS
-1, FIELD_PROTOCOL_MAX
, 2, CASE_PCBSKY9X(1) CASE_PCBSKY9X(2) });
326 MENU_CHECK(menuTabModel
, MENU_MODEL_SETUP
, HEADER_LINE
+MODEL_SETUP_MAX_LINES
);
328 #if defined(CPUARM) && (defined(DSM2) || defined(PXX))
337 TITLE(STR_MENUSETUP
);
339 uint8_t sub
= menuVerticalPosition
- HEADER_LINE
;
340 int8_t editMode
= s_editMode
;
342 for (uint8_t i
=0; i
<NUM_BODY_LINES
; ++i
) {
343 coord_t y
= MENU_HEADER_HEIGHT
+ 1 + i
*FH
;
344 uint8_t k
= i
+ menuVerticalOffset
;
346 for (int j
=0; j
<=k
; j
++) {
347 if (mstate_tab
[j
+HEADER_LINE
] == HIDDEN_ROW
) {
348 if (++k
>= (int)DIM(mstate_tab
)) {
355 LcdFlags blink
= ((editMode
>0) ? BLINK
|INVERS
: INVERS
);
356 LcdFlags attr
= (sub
== k
? blink
: 0);
359 case ITEM_MODEL_NAME
:
360 editSingleName(MODEL_SETUP_2ND_COLUMN
, y
, STR_MODELNAME
, g_model
.header
.name
, sizeof(g_model
.header
.name
), event
, attr
);
362 memcpy(modelHeaders
[g_eeGeneral
.currModel
].name
, g_model
.header
.name
, sizeof(g_model
.header
.name
));
367 case ITEM_MODEL_TIMER1
:
368 case ITEM_MODEL_TIMER2
:
369 case ITEM_MODEL_TIMER3
:
371 unsigned int timerIdx
= (k
>=ITEM_MODEL_TIMER3
? 2 : (k
>=ITEM_MODEL_TIMER2
? 1 : 0));
372 TimerData
* timer
= &g_model
.timers
[timerIdx
];
373 drawStringWithIndex(0*FW
, y
, STR_TIMER
, timerIdx
+1);
374 drawTimerMode(MODEL_SETUP_2ND_COLUMN
, y
, timer
->mode
, menuHorizontalPosition
==0 ? attr
: 0);
375 drawTimer(MODEL_SETUP_2ND_COLUMN
+5*FW
-2+5*FWNUM
+1, y
, timer
->start
, RIGHT
| (menuHorizontalPosition
==1 ? attr
: 0), menuHorizontalPosition
==2 ? attr
: 0);
376 if (attr
&& (editMode
>0 || p1valdiff
)) {
377 div_t qr
= div(timer
->start
, 60);
378 switch (menuHorizontalPosition
) {
381 int8_t timerMode
= timer
->mode
;
382 if (timerMode
< 0) timerMode
-= TMRMODE_COUNT
-1;
383 CHECK_INCDEC_MODELVAR_CHECK(event
, timerMode
, -TMRMODE_COUNT
-SWSRC_LAST
+1, TMRMODE_COUNT
+SWSRC_LAST
-1, isSwitchAvailableInTimers
);
384 if (timerMode
< 0) timerMode
+= TMRMODE_COUNT
-1;
385 timer
->mode
= timerMode
;
386 #if defined(AUTOSWITCH)
388 int8_t val
= timer
->mode
- (TMRMODE_COUNT
-1);
389 int8_t switchVal
= checkIncDecMovedSwitch(val
);
390 if (val
!= switchVal
) {
391 timer
->mode
= switchVal
+ (TMRMODE_COUNT
-1);
392 storageDirty(EE_MODEL
);
399 CHECK_INCDEC_MODELVAR_ZERO(event
, qr
.quot
, 539); // 8:59
400 timer
->start
= qr
.rem
+ qr
.quot
*60;
403 qr
.rem
-= checkIncDecModel(event
, qr
.rem
+2, 1, 62)-2;
404 timer
->start
-= qr
.rem
;
405 if ((int16_t)timer
->start
< 0) timer
->start
=0;
406 if ((int16_t)timer
->start
> 5999) timer
->start
=32399; // 8:59:59
413 case ITEM_MODEL_TIMER1_NAME
:
414 case ITEM_MODEL_TIMER2_NAME
:
415 case ITEM_MODEL_TIMER3_NAME
:
417 TimerData
* timer
= &g_model
.timers
[k
>=ITEM_MODEL_TIMER3
? 2 : (k
>=ITEM_MODEL_TIMER2
? 1 : 0)];
418 editSingleName(MODEL_SETUP_2ND_COLUMN
, y
, STR_TIMER_NAME
, timer
->name
, sizeof(timer
->name
), event
, attr
);
422 case ITEM_MODEL_TIMER1_MINUTE_BEEP
:
423 case ITEM_MODEL_TIMER2_MINUTE_BEEP
:
424 case ITEM_MODEL_TIMER3_MINUTE_BEEP
:
426 TimerData
* timer
= &g_model
.timers
[k
>=ITEM_MODEL_TIMER3
? 2 : (k
>=ITEM_MODEL_TIMER2
? 1 : 0)];
427 timer
->minuteBeep
= editCheckBox(timer
->minuteBeep
, MODEL_SETUP_2ND_COLUMN
, y
, STR_MINUTEBEEP
, attr
, event
);
431 case ITEM_MODEL_TIMER1_COUNTDOWN_BEEP
:
432 case ITEM_MODEL_TIMER2_COUNTDOWN_BEEP
:
433 case ITEM_MODEL_TIMER3_COUNTDOWN_BEEP
:
435 TimerData
* timer
= &g_model
.timers
[k
>=ITEM_MODEL_TIMER3
? 2 : (k
>=ITEM_MODEL_TIMER2
? 1 : 0)];
436 timer
->countdownBeep
= editChoice(MODEL_SETUP_2ND_COLUMN
, y
, STR_BEEPCOUNTDOWN
, STR_VBEEPCOUNTDOWN
, timer
->countdownBeep
, COUNTDOWN_SILENT
, COUNTDOWN_COUNT
-1, attr
, event
);
440 case ITEM_MODEL_TIMER1_PERSISTENT
:
441 case ITEM_MODEL_TIMER2_PERSISTENT
:
442 case ITEM_MODEL_TIMER3_PERSISTENT
:
444 TimerData
* timer
= &g_model
.timers
[k
>=ITEM_MODEL_TIMER3
? 2 : (k
>=ITEM_MODEL_TIMER2
? 1 : 0)];
445 timer
->persistent
= editChoice(MODEL_SETUP_2ND_COLUMN
, y
, STR_PERSISTENT
, STR_VPERSISTENT
, timer
->persistent
, 0, 2, attr
, event
);
449 case ITEM_MODEL_TIMER1
:
450 case ITEM_MODEL_TIMER2
:
451 case ITEM_MODEL_TIMER1_MINUTE_BEEP
:
452 case ITEM_MODEL_TIMER2_MINUTE_BEEP
:
453 case ITEM_MODEL_TIMER1_COUNTDOWN_BEEP
:
454 case ITEM_MODEL_TIMER2_COUNTDOWN_BEEP
:
456 TimerData
*timer
= &g_model
.timers
[k
>=ITEM_MODEL_TIMER2
? 1 : 0];
457 if (k
==ITEM_MODEL_TIMER1_MINUTE_BEEP
|| k
==ITEM_MODEL_TIMER2_MINUTE_BEEP
) {
458 timer
->minuteBeep
= editCheckBox(timer
->minuteBeep
, MODEL_SETUP_2ND_COLUMN
, y
, STR_MINUTEBEEP
, attr
, event
);
460 else if (k
==ITEM_MODEL_TIMER1_COUNTDOWN_BEEP
|| k
==ITEM_MODEL_TIMER2_COUNTDOWN_BEEP
) {
461 timer
->countdownBeep
= editCheckBox(timer
->countdownBeep
, MODEL_SETUP_2ND_COLUMN
, y
, STR_BEEPCOUNTDOWN
, attr
, event
);
464 drawStringWithIndex(0*FW
, y
, STR_TIMER
, k
>=ITEM_MODEL_TIMER2
? 2 : 1);
465 drawTimerMode(MODEL_SETUP_2ND_COLUMN
, y
, timer
->mode
, menuHorizontalPosition
==0 ? attr
: 0);
466 drawTimer(MODEL_SETUP_2ND_COLUMN
+5*FW
-2+5*FWNUM
+1, y
, timer
->start
, menuHorizontalPosition
==1 ? attr
: 0, menuHorizontalPosition
==2 ? attr
: 0);
467 if (attr
&& (editMode
>0 || p1valdiff
)) {
468 div_t qr
= div(timer
->start
, 60);
469 switch (menuHorizontalPosition
) {
471 CHECK_INCDEC_MODELVAR_CHECK(event
, timer
->mode
, SWSRC_FIRST
, TMRMODE_COUNT
+SWSRC_LAST
-1/*SWSRC_None removed*/, isSwitchAvailableInTimers
);
474 CHECK_INCDEC_MODELVAR_ZERO(event
, qr
.quot
, 59);
475 timer
->start
= qr
.rem
+ qr
.quot
*60;
478 qr
.rem
-= checkIncDecModel(event
, qr
.rem
+2, 1, 62) - 2;
479 if ((int16_t)timer
->start
>= qr
.rem
) {
480 timer
->start
-= qr
.rem
;
482 if ((int16_t)timer
->start
> 3599) {
483 timer
->start
= 3599; // 59:59
492 #if defined(CPUM2560)
493 case ITEM_MODEL_TIMER1_PERSISTENT
:
494 case ITEM_MODEL_TIMER2_PERSISTENT
:
496 TimerData
&timer
= g_model
.timers
[k
==ITEM_MODEL_TIMER2_PERSISTENT
];
497 timer
.persistent
= editChoice(MODEL_SETUP_2ND_COLUMN
, y
, STR_PERSISTENT
, STR_VPERSISTENT
, timer
.persistent
, 0, 2, attr
, event
);
503 case ITEM_MODEL_EXTENDED_LIMITS
:
504 ON_OFF_MENU_ITEM(g_model
.extendedLimits
, MODEL_SETUP_2ND_COLUMN
, y
, STR_ELIMITS
, attr
, event
);
507 case ITEM_MODEL_EXTENDED_TRIMS
:
509 ON_OFF_MENU_ITEM(g_model
.extendedTrims
, MODEL_SETUP_2ND_COLUMN
, y
, STR_ETRIMS
, attr
, event
);
511 ON_OFF_MENU_ITEM(g_model
.extendedTrims
, MODEL_SETUP_2ND_COLUMN
, y
, STR_ETRIMS
, menuHorizontalPosition
<=0 ? attr
: 0, event
==EVT_KEY_BREAK(KEY_ENTER
) ? event
: 0);
512 lcdDrawText(MODEL_SETUP_2ND_COLUMN
+4*FW
, y
, STR_RESET_BTN
, (menuHorizontalPosition
>0 && !NO_HIGHLIGHT()) ? attr
: 0);
513 if (attr
&& menuHorizontalPosition
>0) {
515 if (event
==EVT_KEY_LONG(KEY_ENTER
)) {
516 START_NO_HIGHLIGHT();
517 for (uint8_t i
=0; i
<MAX_FLIGHT_MODES
; i
++) {
518 memclear(&g_model
.flightModeData
[i
], TRIMS_ARRAY_SIZE
);
520 storageDirty(EE_MODEL
);
528 case ITEM_MODEL_DISPLAY_TRIMS
:
529 g_model
.displayTrims
= editChoice(MODEL_SETUP_2ND_COLUMN
, y
, STR_DISPLAY_TRIMS
, STR_VDISPLAYTRIMS
, g_model
.displayTrims
, 0, 2, attr
, event
);
533 case ITEM_MODEL_TRIM_INC
:
534 g_model
.trimInc
= editChoice(MODEL_SETUP_2ND_COLUMN
, y
, STR_TRIMINC
, STR_VTRIMINC
, g_model
.trimInc
, -2, 2, attr
, event
);
537 case ITEM_MODEL_THROTTLE_REVERSED
:
538 ON_OFF_MENU_ITEM(g_model
.throttleReversed
, MODEL_SETUP_2ND_COLUMN
, y
, STR_THROTTLEREVERSE
, attr
, event
) ;
541 case ITEM_MODEL_THROTTLE_TRACE
:
543 lcdDrawTextAlignedLeft(y
, STR_TTRACE
);
544 if (attr
) CHECK_INCDEC_MODELVAR_ZERO(event
, g_model
.thrTraceSrc
, NUM_POTS
+NUM_SLIDERS
+MAX_OUTPUT_CHANNELS
);
545 uint8_t idx
= g_model
.thrTraceSrc
+ MIXSRC_Thr
;
546 if (idx
> MIXSRC_Thr
)
548 if (idx
>= MIXSRC_FIRST_POT
+NUM_POTS
+NUM_SLIDERS
)
549 idx
+= MIXSRC_CH1
- MIXSRC_FIRST_POT
- NUM_POTS
- NUM_SLIDERS
;
550 drawSource(MODEL_SETUP_2ND_COLUMN
, y
, idx
, attr
);
554 case ITEM_MODEL_THROTTLE_TRIM
:
555 ON_OFF_MENU_ITEM(g_model
.thrTrim
, MODEL_SETUP_2ND_COLUMN
, y
, STR_TTRIM
, attr
, event
);
559 case ITEM_MODEL_PREFLIGHT_LABEL
:
560 lcdDrawTextAlignedLeft(y
, STR_PREFLIGHT
);
563 case ITEM_MODEL_CHECKLIST_DISPLAY
:
564 ON_OFF_MENU_ITEM(g_model
.displayChecklist
, MODEL_SETUP_2ND_COLUMN
, y
, STR_CHECKLIST
, attr
, event
);
568 case ITEM_MODEL_THROTTLE_WARNING
:
569 g_model
.disableThrottleWarning
= !editCheckBox(!g_model
.disableThrottleWarning
, MODEL_SETUP_2ND_COLUMN
, y
, STR_THROTTLEWARNING
, attr
, event
);
572 #if defined(PCBTARANIS)
573 case ITEM_MODEL_SWITCHES_WARNING2
:
575 if (CURSOR_MOVED_LEFT(event
))
576 menuVerticalOffset
--;
578 menuVerticalOffset
++;
583 case ITEM_MODEL_SWITCHES_WARNING
:
584 #if defined(PCBTARANIS)
586 #define FIRSTSW_STR STR_VSRCRAW+(MIXSRC_FIRST_SWITCH-MIXSRC_Rud+1)*length
587 uint8_t length
= pgm_read_byte(STR_VSRCRAW
);
588 horzpos_t l_posHorz
= menuHorizontalPosition
;
590 if (i
>=NUM_BODY_LINES
-2 && getSwitchWarningsCount() > 5*(NUM_BODY_LINES
-i
)) {
591 if (CURSOR_MOVED_LEFT(event
))
592 menuVerticalOffset
--;
594 menuVerticalOffset
++;
598 swarnstate_t states
= g_model
.switchWarningState
;
601 lcdDrawTextAlignedLeft(y
, STR_SWITCHWARNING
);
602 #if defined(PCBXLITE)
603 lcdDrawText(LCD_W
, y
, "<]", RIGHT
);
605 if (menuHorizontalPosition
> NUM_SWITCHES
)
606 menuHorizontalPosition
= NUM_SWITCHES
;
608 if (attr
&& menuHorizontalPosition
== NUM_SWITCHES
) {
615 case EVT_KEY_BREAK(KEY_ENTER
):
618 case EVT_KEY_LONG(KEY_ENTER
):
619 if (menuHorizontalPosition
< 0 || menuHorizontalPosition
>= NUM_SWITCHES
) {
620 START_NO_HIGHLIGHT();
622 g_model
.switchWarningState
= switches_states
;
624 storageDirty(EE_MODEL
);
632 LcdFlags line
= attr
;
635 for (int i
=0; i
<NUM_SWITCHES
; i
++) {
636 if (SWITCH_WARNING_ALLOWED(i
)) {
637 div_t qr
= div(current
, 5);
638 if (!READ_ONLY() && event
==EVT_KEY_BREAK(KEY_ENTER
) && line
&& l_posHorz
==current
) {
639 g_model
.switchWarningEnable
^= (1 << i
);
640 storageDirty(EE_MODEL
);
641 #if defined(PCBXLITE)
645 uint8_t swactive
= !(g_model
.switchWarningEnable
& (1<<i
));
646 c
= "\300-\301"[states
& 0x03];
647 //lcdDrawChar(MODEL_SETUP_2ND_COLUMN+qr.rem*(2*FW+1), y+FH*qr.quot, 'A'+i, line && (menuHorizontalPosition==current) ? INVERS : 0);
648 lcdDrawSizedText(MODEL_SETUP_2ND_COLUMN
+ qr
.rem
*((2*FW
)+1), y
+FH
*qr
.quot
, FIRSTSW_STR
+(i
*length
)+3, 1, line
&& (menuHorizontalPosition
==current
) ? INVERS
: 0);
649 if (swactive
) lcdDrawChar(lcdNextPos
, y
+FH
*qr
.quot
, c
);
654 if (attr
&& ((menuHorizontalPosition
< 0) || menuHorizontalPosition
>= NUM_SWITCHES
)) {
655 lcdDrawFilledRect(MODEL_SETUP_2ND_COLUMN
-1, y
-1, 8*(2*FW
+1), 1+FH
*((current
+4)/5));
659 lcdDrawTextAlignedLeft(y
, STR_SWITCHWARNING
);
660 swarnstate_t states
= g_model
.switchWarningState
;
666 CASE_EVT_ROTARY_BREAK
667 case EVT_KEY_BREAK(KEY_ENTER
):
669 g_model
.switchWarningEnable
^= (1 << menuHorizontalPosition
);
670 storageDirty(EE_MODEL
);
672 if (menuHorizontalPosition
< NUM_SWITCHES
-1) {
673 g_model
.switchWarningEnable
^= (1 << menuHorizontalPosition
);
674 storageDirty(EE_MODEL
);
679 case EVT_KEY_LONG(KEY_ENTER
):
682 g_model
.switchWarningState
= switches_states
;
684 storageDirty(EE_MODEL
);
686 if (menuHorizontalPosition
== NUM_SWITCHES
-1) {
687 START_NO_HIGHLIGHT();
689 g_model
.switchWarningState
= switches_states
;
691 storageDirty(EE_MODEL
);
699 LcdFlags line
= attr
;
701 for (uint8_t i
=0; i
<NUM_SWITCHES
-1/*not on TRN switch*/; i
++) {
702 uint8_t swactive
= !(g_model
.switchWarningEnable
& 1 << i
);
706 c
= '0'+(states
& 0x03);
710 if ((states
& 0x01) && swactive
)
712 c
= pgm_read_byte(STR_VSWITCHES
- 2 + 9 + (3*(i
+1)));
715 if (line
&& (menuHorizontalPosition
== i
)) {
716 attr
= BLINK
| INVERS
;
719 lcdDrawChar(MODEL_SETUP_2ND_COLUMN
+i
*FW
, y
, (swactive
) ? c
: '-', attr
);
721 lcdDrawChar(MODEL_SETUP_2ND_COLUMN
+i
*FW
, y
, (swactive
|| (attr
& BLINK
)) ? c
: '-', attr
);
724 lcdDrawText(MODEL_SETUP_2ND_COLUMN
+(NUM_SWITCHES
*FW
), y
, PSTR("<]"), (menuHorizontalPosition
== NUM_SWITCHES
-1 && !NO_HIGHLIGHT()) ? line
: 0);
730 #if defined(PCBTARANIS)
731 case ITEM_MODEL_POTS_WARNING
:
732 lcdDrawTextAlignedLeft(y
, STR_POTWARNING
);
733 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
, y
, PSTR("\004""OFF\0""Man\0""Auto"), g_model
.potsWarnMode
, (menuHorizontalPosition
== 0) ? attr
: 0);
734 if (attr
&& (menuHorizontalPosition
== 0)) {
735 CHECK_INCDEC_MODELVAR(event
, g_model
.potsWarnMode
, POTS_WARN_OFF
, POTS_WARN_AUTO
);
736 storageDirty(EE_MODEL
);
740 if (menuHorizontalPosition
> 0) s_editMode
= 0;
741 if (!READ_ONLY() && menuHorizontalPosition
> 0) {
743 case EVT_KEY_LONG(KEY_ENTER
):
745 if (g_model
.potsWarnMode
== POTS_WARN_MANUAL
) {
746 SAVE_POT_POSITION(menuHorizontalPosition
-1);
748 storageDirty(EE_MODEL
);
751 case EVT_KEY_BREAK(KEY_ENTER
):
752 g_model
.potsWarnEnabled
^= (1 << (menuHorizontalPosition
-1));
753 storageDirty(EE_MODEL
);
758 if (g_model
.potsWarnMode
) {
759 coord_t x
= MODEL_SETUP_2ND_COLUMN
+28;
760 for (int i
=0; i
<NUM_POTS
+NUM_SLIDERS
; ++i
) {
761 if (i
<NUM_XPOTS
&& !IS_POT_SLIDER_AVAILABLE(POT1
+i
)) {
762 if (attr
&& (menuHorizontalPosition
==i
+1)) REPEAT_LAST_CURSOR_MOVE();
765 LcdFlags flags
= ((menuHorizontalPosition
==i
+1) && attr
) ? BLINK
: 0;
766 if ((!attr
|| menuHorizontalPosition
>= 0) && !(g_model
.potsWarnEnabled
& (1 << i
))) {
770 // TODO add a new function
771 lcdDrawSizedText(x
, y
, STR_VSRCRAW
+2+STR_VSRCRAW
[0]*(NUM_STICKS
+1+i
), STR_VSRCRAW
[0]-1, flags
& ~ZCHAR
);
779 case ITEM_MODEL_BEEP_CENTER
:
780 lcdDrawTextAlignedLeft(y
, STR_BEEPCTR
);
781 for (uint8_t i
=0; i
<NUM_STICKS
+NUM_POTS
+NUM_SLIDERS
+NUM_ROTARY_ENCODERS
; i
++) {
782 // TODO flash saving, \001 not needed in STR_RETA123
783 coord_t x
= MODEL_SETUP_2ND_COLUMN
+i
*FW
;
784 lcdDrawTextAtIndex(x
, y
, STR_RETA123
, i
, ((menuHorizontalPosition
==i
) && attr
) ? BLINK
|INVERS
: (((g_model
.beepANACenter
& ((BeepANACenter
)1<<i
)) || (attr
&& CURSOR_ON_LINE())) ? INVERS
: 0 ) );
786 if (attr
&& CURSOR_ON_CELL
) {
787 if (event
==EVT_KEY_BREAK(KEY_ENTER
) || p1valdiff
) {
788 if (READ_ONLY_UNLOCKED()) {
790 g_model
.beepANACenter
^= ((BeepANACenter
)1<<menuHorizontalPosition
);
791 storageDirty(EE_MODEL
);
798 case ITEM_MODEL_USE_GLOBAL_FUNCTIONS
:
799 lcdDrawTextAlignedLeft(y
, STR_USE_GLOBAL_FUNCS
);
800 drawCheckBox(MODEL_SETUP_2ND_COLUMN
, y
, !g_model
.noGlobalFunctions
, attr
);
801 if (attr
) g_model
.noGlobalFunctions
= !checkIncDecModel(event
, !g_model
.noGlobalFunctions
, 0, 1);
805 #if defined(PCBTARANIS)
806 case ITEM_MODEL_INTERNAL_MODULE_LABEL
:
807 lcdDrawTextAlignedLeft(y
, TR_INTERNALRF
);
810 case ITEM_MODEL_INTERNAL_MODULE_MODE
:
811 lcdDrawTextAlignedLeft(y
, STR_MODE
);
812 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
, y
, STR_XJT_PROTOCOLS
, 1+g_model
.moduleData
[0].rfProtocol
, attr
);
814 g_model
.moduleData
[INTERNAL_MODULE
].rfProtocol
= checkIncDec(event
, g_model
.moduleData
[INTERNAL_MODULE
].rfProtocol
, RF_PROTO_OFF
, RF_PROTO_LAST
, EE_MODEL
, isRfProtocolAvailable
);
815 if (checkIncDec_Ret
) {
816 g_model
.moduleData
[0].type
= MODULE_TYPE_XJT
;
817 g_model
.moduleData
[0].channelsStart
= 0;
818 g_model
.moduleData
[0].channelsCount
= DEFAULT_CHANNELS(INTERNAL_MODULE
);
819 if (g_model
.moduleData
[INTERNAL_MODULE
].rfProtocol
== RF_PROTO_OFF
)
820 g_model
.moduleData
[INTERNAL_MODULE
].type
= MODULE_TYPE_NONE
;
826 #if defined(PCBSKY9X)
827 case ITEM_MODEL_EXTRA_MODULE_LABEL
:
828 lcdDrawTextAlignedLeft(y
, "RF Port 2 (PPM)");
833 case ITEM_MODEL_EXTERNAL_MODULE_LABEL
:
834 lcdDrawTextAlignedLeft(y
, TR_EXTERNALRF
);
837 case ITEM_MODEL_EXTERNAL_MODULE_MODE
:
838 lcdDrawTextAlignedLeft(y
, STR_MODE
);
839 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
, y
, STR_TARANIS_PROTOCOLS
, g_model
.moduleData
[EXTERNAL_MODULE
].type
, menuHorizontalPosition
==0 ? attr
: 0);
840 if (IS_MODULE_XJT(EXTERNAL_MODULE
))
841 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
+5*FW
, y
, STR_XJT_PROTOCOLS
, 1+g_model
.moduleData
[EXTERNAL_MODULE
].rfProtocol
, menuHorizontalPosition
==1 ? attr
: 0);
842 else if (IS_MODULE_DSM2(EXTERNAL_MODULE
))
843 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
+5*FW
, y
, STR_DSM_PROTOCOLS
, g_model
.moduleData
[EXTERNAL_MODULE
].rfProtocol
, menuHorizontalPosition
==1 ? attr
: 0);
844 else if (IS_MODULE_R9M(EXTERNAL_MODULE
))
845 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
+5*FW
, y
, STR_R9M_REGION
, g_model
.moduleData
[EXTERNAL_MODULE
].subType
, (menuHorizontalPosition
==1 ? attr
: 0));
846 #if defined(MULTIMODULE)
847 else if (IS_MODULE_MULTIMODULE(EXTERNAL_MODULE
)) {
848 int multi_rfProto
= g_model
.moduleData
[EXTERNAL_MODULE
].getMultiProtocol(false);
849 if (g_model
.moduleData
[EXTERNAL_MODULE
].multi
.customProto
)
850 lcdDrawText(MODEL_SETUP_2ND_COLUMN
+5*FW
, y
, STR_MULTI_CUSTOM
, menuHorizontalPosition
==1 ? attr
: 0);
852 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
+5*FW
, y
, STR_MULTI_PROTOCOLS
, multi_rfProto
, menuHorizontalPosition
==1 ? attr
: 0);
855 if (attr
&& (editMode
>0 || p1valdiff
)) {
856 switch (menuHorizontalPosition
) {
858 g_model
.moduleData
[EXTERNAL_MODULE
].type
= checkIncDec(event
, g_model
.moduleData
[EXTERNAL_MODULE
].type
, MODULE_TYPE_NONE
, IS_TRAINER_EXTERNAL_MODULE() ? MODULE_TYPE_NONE
: MODULE_TYPE_COUNT
-1, EE_MODEL
, isModuleAvailable
);
859 if (checkIncDec_Ret
) {
860 g_model
.moduleData
[EXTERNAL_MODULE
].rfProtocol
= 0;
861 g_model
.moduleData
[EXTERNAL_MODULE
].channelsStart
= 0;
862 g_model
.moduleData
[EXTERNAL_MODULE
].channelsCount
= DEFAULT_CHANNELS(EXTERNAL_MODULE
);
863 if (IS_MODULE_SBUS(EXTERNAL_MODULE
))
864 g_model
.moduleData
[EXTERNAL_MODULE
].sbus
.refreshRate
= -31;
868 if (IS_MODULE_DSM2(EXTERNAL_MODULE
))
869 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[EXTERNAL_MODULE
].rfProtocol
, DSM2_PROTO_LP45
, DSM2_PROTO_DSMX
);
870 else if (IS_MODULE_R9M(EXTERNAL_MODULE
))
871 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[EXTERNAL_MODULE
].subType
, MODULE_SUBTYPE_R9M_FCC
, MODULE_SUBTYPE_R9M_LBT
);
872 #if defined(MULTIMODULE)
873 else if (IS_MODULE_MULTIMODULE(EXTERNAL_MODULE
)) {
874 int multiRfProto
= g_model
.moduleData
[EXTERNAL_MODULE
].multi
.customProto
== 1 ? MM_RF_PROTO_CUSTOM
: g_model
.moduleData
[EXTERNAL_MODULE
].getMultiProtocol(false);
875 CHECK_INCDEC_MODELVAR(event
, multiRfProto
, MM_RF_PROTO_FIRST
, MM_RF_PROTO_LAST
);
876 if (checkIncDec_Ret
) {
877 g_model
.moduleData
[EXTERNAL_MODULE
].multi
.customProto
= (multiRfProto
== MM_RF_PROTO_CUSTOM
);
878 if (!g_model
.moduleData
[EXTERNAL_MODULE
].multi
.customProto
)
879 g_model
.moduleData
[EXTERNAL_MODULE
].setMultiProtocol(multiRfProto
);
880 g_model
.moduleData
[EXTERNAL_MODULE
].subType
= 0;
881 // Sensible default for DSM2 (same as for ppm): 7ch@22ms + Autodetect settings enabled
882 if (g_model
.moduleData
[EXTERNAL_MODULE
].getMultiProtocol(true) == MM_RF_PROTO_DSM2
) {
883 g_model
.moduleData
[EXTERNAL_MODULE
].multi
.autoBindMode
= 1;
886 g_model
.moduleData
[EXTERNAL_MODULE
].multi
.autoBindMode
= 0;
888 g_model
.moduleData
[EXTERNAL_MODULE
].multi
.optionValue
= 0;
893 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[EXTERNAL_MODULE
].rfProtocol
, RF_PROTO_X16
, RF_PROTO_LAST
);
895 if (checkIncDec_Ret
) {
896 g_model
.moduleData
[EXTERNAL_MODULE
].channelsStart
= 0;
897 g_model
.moduleData
[EXTERNAL_MODULE
].channelsCount
= DEFAULT_CHANNELS(EXTERNAL_MODULE
);
904 #if defined(MULTIMODULE)
905 case ITEM_MODEL_EXTERNAL_MODULE_SUBTYPE
:
907 lcdDrawTextAlignedLeft(y
, STR_SUBTYPE
);
908 uint8_t multi_rfProto
= g_model
.moduleData
[EXTERNAL_MODULE
].getMultiProtocol(true);
909 const mm_protocol_definition
* pdef
= getMultiProtocolDefinition(multi_rfProto
);
911 if (multi_rfProto
== MM_RF_CUSTOM_SELECTED
) {
912 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN
+ 3 * FW
, y
, g_model
.moduleData
[EXTERNAL_MODULE
].getMultiProtocol(false), RIGHT
| (menuHorizontalPosition
== 0 ? attr
: 0), 2);
913 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN
+ 5 * FW
, y
, g_model
.moduleData
[EXTERNAL_MODULE
].subType
, RIGHT
| (menuHorizontalPosition
== 1 ? attr
: 0), 2);
916 if (pdef
->subTypeString
!= nullptr)
917 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
, y
, pdef
->subTypeString
, g_model
.moduleData
[EXTERNAL_MODULE
].subType
, attr
);
919 if (attr
&& (editMode
> 0 || p1valdiff
)) {
920 switch (menuHorizontalPosition
) {
922 if (multi_rfProto
== MM_RF_CUSTOM_SELECTED
)
923 g_model
.moduleData
[EXTERNAL_MODULE
].setMultiProtocol(checkIncDec(event
, g_model
.moduleData
[EXTERNAL_MODULE
].getMultiProtocol(false), 0, 63, EE_MODEL
));
924 else if (pdef
->maxSubtype
> 0)
925 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[EXTERNAL_MODULE
].subType
, 0, pdef
->maxSubtype
);
928 // Custom protocol, third column is subtype
929 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[EXTERNAL_MODULE
].subType
, 0, 7);
937 #if defined(PCBTARANIS)
938 case ITEM_MODEL_TRAINER_LABEL
:
939 lcdDrawTextAlignedLeft(y
, STR_TRAINER
);
942 case ITEM_MODEL_TRAINER_MODE
:
943 lcdDrawTextAlignedLeft(y
, STR_MODE
);
944 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
, y
, STR_VTRAINERMODES
, g_model
.trainerMode
, attr
);
946 g_model
.trainerMode
= checkIncDec(event
, g_model
.trainerMode
, 0, TRAINER_MODE_MAX(), EE_MODEL
, isTrainerModeAvailable
);
947 #if defined(BLUETOOTH)
948 if (checkIncDec_Ret
) {
949 bluetoothState
= BLUETOOTH_STATE_OFF
;
950 bluetoothDistantAddr
[0] = 0;
957 #if defined(PCBTARANIS) && defined(BLUETOOTH)
958 case ITEM_MODEL_TRAINER_BLUETOOTH
:
959 if (g_model
.trainerMode
== TRAINER_MODE_MASTER_BLUETOOTH
) {
963 if (bluetoothDistantAddr
[0]) {
964 lcdDrawText(INDENT_WIDTH
, y
+1, bluetoothDistantAddr
, TINSIZE
);
965 if (bluetoothState
!= BLUETOOTH_STATE_CONNECTED
) {
966 lcdDrawText(MODEL_SETUP_2ND_COLUMN
, y
, BUTTON("Bind"), menuHorizontalPosition
== 0 ? attr
: 0);
967 lcdDrawText(MODEL_SETUP_2ND_COLUMN
+5*FW
, y
, BUTTON("Clear"), menuHorizontalPosition
== 1 ? attr
: 0);
970 lcdDrawText(MODEL_SETUP_2ND_COLUMN
, y
, BUTTON("Clear"), attr
);
972 if (attr
&& event
== EVT_KEY_FIRST(KEY_ENTER
)) {
973 if (bluetoothState
== BLUETOOTH_STATE_CONNECTED
|| menuHorizontalPosition
== 1) {
974 bluetoothState
= BLUETOOTH_STATE_OFF
;
975 bluetoothDistantAddr
[0] = 0;
978 bluetoothState
= BLUETOOTH_STATE_BIND_REQUESTED
;
983 lcdDrawText(INDENT_WIDTH
, y
, "---");
984 if (bluetoothState
< BLUETOOTH_STATE_IDLE
)
985 lcdDrawText(MODEL_SETUP_2ND_COLUMN
, y
, BUTTON("Init"), attr
);
987 lcdDrawText(MODEL_SETUP_2ND_COLUMN
, y
, BUTTON("Discover"), attr
);
988 if (attr
&& event
== EVT_KEY_FIRST(KEY_ENTER
)) {
989 if (bluetoothState
< BLUETOOTH_STATE_IDLE
)
990 bluetoothState
= BLUETOOTH_STATE_OFF
;
992 bluetoothState
= BLUETOOTH_STATE_DISCOVER_REQUESTED
;
997 if (bluetoothDistantAddr
[0])
998 lcdDrawText(INDENT_WIDTH
, y
+1, bluetoothDistantAddr
, TINSIZE
);
1000 lcdDrawText(INDENT_WIDTH
, y
, "---");
1001 lcdDrawText(MODEL_SETUP_2ND_COLUMN
, y
, bluetoothState
== BLUETOOTH_STATE_CONNECTED
? "Connected" : "!Connected");
1006 #if defined(PCBTARANIS)
1007 case ITEM_MODEL_TRAINER_CHANNELS
:
1008 case ITEM_MODEL_INTERNAL_MODULE_CHANNELS
:
1010 #if defined(PCBSKY9X)
1011 case ITEM_MODEL_EXTRA_MODULE_CHANNELS
:
1014 case ITEM_MODEL_EXTERNAL_MODULE_CHANNELS
:
1016 uint8_t moduleIdx
= CURRENT_MODULE_EDITED(k
);
1017 ModuleData
& moduleData
= g_model
.moduleData
[moduleIdx
];
1018 lcdDrawTextAlignedLeft(y
, STR_CHANNELRANGE
);
1019 if ((int8_t)PORT_CHANNELS_ROWS(moduleIdx
) >= 0) {
1020 lcdDrawText(MODEL_SETUP_2ND_COLUMN
, y
, STR_CH
, menuHorizontalPosition
==0 ? attr
: 0);
1021 lcdDrawNumber(lcdLastRightPos
, y
, moduleData
.channelsStart
+1, LEFT
| (menuHorizontalPosition
==0 ? attr
: 0));
1022 lcdDrawChar(lcdLastRightPos
, y
, '-');
1023 lcdDrawNumber(lcdLastRightPos
+ FW
+1, y
, moduleData
.channelsStart
+NUM_CHANNELS(moduleIdx
), LEFT
| (menuHorizontalPosition
==1 ? attr
: 0));
1024 if (attr
&& (editMode
>0 || p1valdiff
)) {
1025 switch (menuHorizontalPosition
) {
1027 CHECK_INCDEC_MODELVAR_ZERO(event
, moduleData
.channelsStart
, 32-8-moduleData
.channelsCount
);
1030 CHECK_INCDEC_MODELVAR(event
, moduleData
.channelsCount
, -4, min
<int8_t>(MAX_CHANNELS(moduleIdx
), 32-8-moduleData
.channelsStart
));
1031 if ((k
== ITEM_MODEL_EXTERNAL_MODULE_CHANNELS
&& g_model
.moduleData
[EXTERNAL_MODULE
].type
== MODULE_TYPE_PPM
)) {
1032 SET_DEFAULT_PPM_FRAME_LENGTH(moduleIdx
);
1043 case ITEM_MODEL_TRAINER_PARAMS
:
1045 #if defined(PCBTARANIS)
1046 case ITEM_MODEL_INTERNAL_MODULE_BIND
:
1048 #if defined(PCBSKY9X)
1049 case ITEM_MODEL_EXTRA_MODULE_BIND
:
1052 case ITEM_MODEL_EXTERNAL_MODULE_BIND
:
1054 uint8_t moduleIdx
= CURRENT_MODULE_EDITED(k
);
1055 ModuleData
& moduleData
= g_model
.moduleData
[moduleIdx
];
1056 if (IS_MODULE_PPM(moduleIdx
)) {
1057 lcdDrawTextAlignedLeft(y
, STR_PPMFRAME
);
1058 lcdDrawText(MODEL_SETUP_2ND_COLUMN
+3*FW
, y
, STR_MS
);
1059 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN
, y
, (int16_t)moduleData
.ppm
.frameLength
*5 + 225, (menuHorizontalPosition
<=0 ? attr
: 0) | PREC1
|LEFT
);
1060 lcdDrawChar(MODEL_SETUP_2ND_COLUMN
+8*FW
+2, y
, 'u');
1061 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN
+8*FW
+2, y
, (moduleData
.ppm
.delay
*50)+300, RIGHT
| ((CURSOR_ON_LINE() || menuHorizontalPosition
==1) ? attr
: 0));
1062 lcdDrawChar(MODEL_SETUP_2ND_COLUMN
+10*FW
, y
, moduleData
.ppm
.pulsePol
? '+' : '-', (CURSOR_ON_LINE() || menuHorizontalPosition
==2) ? attr
: 0);
1063 if (attr
&& (editMode
>0 || p1valdiff
)) {
1064 switch (menuHorizontalPosition
) {
1066 CHECK_INCDEC_MODELVAR(event
, moduleData
.ppm
.frameLength
, -20, 35);
1069 CHECK_INCDEC_MODELVAR(event
, moduleData
.ppm
.delay
, -4, 10);
1072 CHECK_INCDEC_MODELVAR_ZERO(event
, moduleData
.ppm
.pulsePol
, 1);
1077 else if (IS_MODULE_SBUS(moduleIdx
)) {
1078 lcdDrawTextAlignedLeft(y
, STR_REFRESHRATE
);
1079 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN
, y
, (int16_t)moduleData
.ppm
.frameLength
*5 + 225, (menuHorizontalPosition
<=0 ? attr
: 0) | PREC1
|LEFT
);
1080 lcdDrawText(lcdLastRightPos
, y
, STR_MS
);
1081 lcdDrawText(MODEL_SETUP_2ND_COLUMN
+5*FW
+2, y
, moduleData
.sbus
.noninverted
? "no inv" : "normal", (CURSOR_ON_LINE() || menuHorizontalPosition
==1) ? attr
: 0);
1083 if (attr
&& s_editMode
>0) {
1084 switch (menuHorizontalPosition
) {
1086 CHECK_INCDEC_MODELVAR(event
, moduleData
.ppm
.frameLength
, -33, 35);
1089 CHECK_INCDEC_MODELVAR_ZERO(event
, moduleData
.sbus
.noninverted
, 1);
1095 horzpos_t l_posHorz
= menuHorizontalPosition
;
1096 coord_t xOffsetBind
= MODEL_SETUP_BIND_OFS
;
1097 if (IS_MODULE_XJT(moduleIdx
) && IS_D8_RX(moduleIdx
)) {
1099 lcdDrawTextAlignedLeft(y
, STR_RECEIVER
);
1100 if (attr
) l_posHorz
+= 1;
1103 lcdDrawTextAlignedLeft(y
, STR_RECEIVER_NUM
);
1105 if (IS_MODULE_PXX(moduleIdx
) || IS_MODULE_DSM2(moduleIdx
) || IS_MODULE_MULTIMODULE(moduleIdx
)) {
1107 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN
, y
, g_model
.header
.modelId
[moduleIdx
], (l_posHorz
==0 ? attr
: 0) | LEADING0
|LEFT
, 2);
1108 if (attr
&& l_posHorz
== 0) {
1109 if (editMode
>0 || p1valdiff
) {
1110 CHECK_INCDEC_MODELVAR_ZERO(event
, g_model
.header
.modelId
[moduleIdx
], MAX_RX_NUM(moduleIdx
));
1111 if (checkIncDec_Ret
) {
1112 modelHeaders
[g_eeGeneral
.currModel
].modelId
[moduleIdx
] = g_model
.header
.modelId
[moduleIdx
];
1114 else if (event
== EVT_KEY_LONG(KEY_ENTER
)) {
1116 uint8_t newVal
= findNextUnusedModelId(g_eeGeneral
.currModel
, moduleIdx
);
1117 if (newVal
!= g_model
.header
.modelId
[moduleIdx
]) {
1118 modelHeaders
[g_eeGeneral
.currModel
].modelId
[moduleIdx
] = g_model
.header
.modelId
[moduleIdx
] = newVal
;
1119 storageDirty(EE_MODEL
);
1124 lcdDrawText(MODEL_SETUP_2ND_COLUMN
+xOffsetBind
, y
, STR_MODULE_BIND
, l_posHorz
==1 ? attr
: 0);
1125 lcdDrawText(MODEL_SETUP_2ND_COLUMN
+MODEL_SETUP_RANGE_OFS
+xOffsetBind
, y
, STR_MODULE_RANGE
, l_posHorz
==2 ? attr
: 0);
1126 uint8_t newFlag
= 0;
1127 #if defined(MULTIMODULE)
1128 if (multiBindStatus
== MULTI_BIND_FINISHED
) {
1129 multiBindStatus
= MULTI_NORMAL_OPERATION
;
1133 #if defined(PCBTARANIS)
1134 if (attr
&& l_posHorz
> 0) {
1135 if (s_editMode
> 0) {
1136 if (l_posHorz
== 1) {
1137 if (IS_MODULE_R9M(moduleIdx
) || (IS_MODULE_XJT(moduleIdx
) && g_model
.moduleData
[moduleIdx
].rfProtocol
== RF_PROTO_X16
)) {
1138 #if defined(PCBXLITE)
1139 if (EVT_KEY_MASK(event
) == KEY_ENTER
) {
1141 if (event
== EVT_KEY_BREAK(KEY_ENTER
)) {
1144 uint8_t default_selection
= 0; // R9M_LBT should default to 0 as available options are variables
1145 if (IS_MODULE_R9M_LBT(moduleIdx
)) {
1146 if (BIND_TELEM_ALLOWED(moduleIdx
))
1147 POPUP_MENU_ADD_ITEM(STR_BINDING_1_8_TELEM_ON
);
1148 POPUP_MENU_ADD_ITEM(STR_BINDING_1_8_TELEM_OFF
);
1149 if (BIND_TELEM_ALLOWED(moduleIdx
) && BIND_CH9TO16_ALLOWED(moduleIdx
))
1150 POPUP_MENU_ADD_ITEM(STR_BINDING_9_16_TELEM_ON
);
1151 if (BIND_CH9TO16_ALLOWED(moduleIdx
))
1152 POPUP_MENU_ADD_ITEM(STR_BINDING_9_16_TELEM_OFF
);
1155 if (BIND_TELEM_ALLOWED(moduleIdx
))
1156 POPUP_MENU_ADD_ITEM(STR_BINDING_1_8_TELEM_ON
);
1157 POPUP_MENU_ADD_ITEM(STR_BINDING_1_8_TELEM_OFF
);
1158 if (BIND_TELEM_ALLOWED(moduleIdx
))
1159 POPUP_MENU_ADD_ITEM(STR_BINDING_9_16_TELEM_ON
);
1160 POPUP_MENU_ADD_ITEM(STR_BINDING_9_16_TELEM_OFF
);
1161 default_selection
= g_model
.moduleData
[INTERNAL_MODULE
].pxx
.receiver_telem_off
+ (g_model
.moduleData
[INTERNAL_MODULE
].pxx
.receiver_channel_9_16
<< 1);
1163 POPUP_MENU_SELECT_ITEM(default_selection
);
1164 POPUP_MENU_START(onBindMenu
);
1167 if (moduleFlag
[moduleIdx
] == MODULE_BIND
) {
1168 newFlag
= MODULE_BIND
;
1171 if (!popupMenuNoItems
) {
1172 s_editMode
= 0; // this is when popup is exited before a choice is made
1177 newFlag
= MODULE_BIND
;
1180 else if (l_posHorz
== 2) {
1181 newFlag
= MODULE_RANGECHECK
;
1186 if (attr
&& l_posHorz
>0 && s_editMode
>0) {
1188 newFlag
= MODULE_BIND
;
1189 else if (l_posHorz
== 2)
1190 newFlag
= MODULE_RANGECHECK
;
1193 moduleFlag
[moduleIdx
] = newFlag
;
1195 #if defined(MULTIMODULE)
1196 if (newFlag
== MODULE_BIND
) {
1197 multiBindStatus
= MULTI_BIND_INITIATED
;
1207 #if defined(PCBSKY9X) && defined(REVX)
1208 case ITEM_MODEL_EXTERNAL_MODULE_OUTPUT_TYPE
:
1210 uint8_t moduleIdx
= CURRENT_MODULE_EDITED(k
);
1211 ModuleData
& moduleData
= g_model
.moduleData
[moduleIdx
];
1212 moduleData
.ppm
.outputType
= editChoice(MODEL_SETUP_2ND_COLUMN
, y
, STR_OUTPUT_TYPE
, STR_VOUTPUT_TYPE
, moduleData
.ppm
.outputType
, 0, 1, attr
, event
);
1217 #if defined(PCBTARANIS)
1218 case ITEM_MODEL_INTERNAL_MODULE_FAILSAFE
:
1221 case ITEM_MODEL_EXTERNAL_MODULE_FAILSAFE
: {
1222 uint8_t moduleIdx
= CURRENT_MODULE_EDITED(k
);
1223 ModuleData
&moduleData
= g_model
.moduleData
[moduleIdx
];
1224 lcdDrawTextAlignedLeft(y
, STR_FAILSAFE
);
1225 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
, y
, STR_VFAILSAFE
, moduleData
.failsafeMode
, menuHorizontalPosition
== 0 ? attr
: 0);
1226 if (moduleData
.failsafeMode
== FAILSAFE_CUSTOM
)
1227 lcdDrawText(MODEL_SETUP_2ND_COLUMN
+ MODEL_SETUP_SET_FAILSAFE_OFS
, y
, STR_SET
, menuHorizontalPosition
== 1 ? attr
: 0);
1229 if (moduleData
.failsafeMode
!= FAILSAFE_CUSTOM
)
1230 menuHorizontalPosition
= 0;
1231 if (menuHorizontalPosition
== 0) {
1232 if (editMode
> 0 || p1valdiff
) {
1233 CHECK_INCDEC_MODELVAR_ZERO(event
, moduleData
.failsafeMode
, FAILSAFE_LAST
);
1234 if (checkIncDec_Ret
) SEND_FAILSAFE_NOW(moduleIdx
);
1236 } else if (menuHorizontalPosition
== 1) {
1238 if (moduleData
.failsafeMode
== FAILSAFE_CUSTOM
) {
1239 if (event
== EVT_KEY_LONG(KEY_ENTER
)) {
1241 setCustomFailsafe(moduleIdx
);
1242 storageDirty(EE_MODEL
);
1244 SEND_FAILSAFE_NOW(moduleIdx
);
1246 else if (event
== EVT_KEY_BREAK(KEY_ENTER
)) {
1247 g_moduleIdx
= moduleIdx
;
1248 pushMenu(menuModelFailsafe
);
1252 lcdDrawSolidFilledRect(MODEL_SETUP_2ND_COLUMN
, y
, LCD_W
- MODEL_SETUP_2ND_COLUMN
, 8);
1258 #if defined(PCBXLITE)
1259 case ITEM_MODEL_INTERNAL_MODULE_ANTENNA
:
1261 uint8_t newAntennaSel
= editChoice(MODEL_SETUP_2ND_COLUMN
, y
, STR_ANTENNASELECTION
, STR_VANTENNATYPES
, g_model
.moduleData
[INTERNAL_MODULE
].pxx
.external_antenna
, 0, 1, attr
, event
);
1262 if (newAntennaSel
!= g_model
.moduleData
[INTERNAL_MODULE
].pxx
.external_antenna
&& newAntennaSel
== XJT_EXTERNAL_ANTENNA
) {
1263 POPUP_CONFIRMATION(STR_ANTENNACONFIRM1
);
1264 const char * w
= STR_ANTENNACONFIRM2
;
1265 SET_WARNING_INFO(w
, strlen(w
), 0);
1268 g_model
.moduleData
[INTERNAL_MODULE
].pxx
.external_antenna
= newAntennaSel
;
1273 case ITEM_MODEL_EXTERNAL_MODULE_OPTIONS
:
1275 uint8_t moduleIdx
= CURRENT_MODULE_EDITED(k
);
1276 #if defined(MULTIMODULE)
1277 if (IS_MODULE_MULTIMODULE(moduleIdx
)) {
1278 int optionValue
= g_model
.moduleData
[moduleIdx
].multi
.optionValue
;
1280 const uint8_t multi_proto
= g_model
.moduleData
[EXTERNAL_MODULE
].getMultiProtocol(true);
1281 const mm_protocol_definition
* pdef
= getMultiProtocolDefinition(multi_proto
);
1282 if (pdef
->optionsstr
)
1283 lcdDrawTextAlignedLeft(y
, pdef
->optionsstr
);
1285 if (multi_proto
== MM_RF_PROTO_FS_AFHDS2A
)
1286 optionValue
= 50 + 5 * optionValue
;
1288 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN
, y
, optionValue
, LEFT
| attr
);
1290 if (multi_proto
== MM_RF_PROTO_FS_AFHDS2A
) {
1291 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[moduleIdx
].multi
.optionValue
, 0, 70);
1293 else if (multi_proto
== MM_RF_PROTO_OLRS
) {
1294 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[moduleIdx
].multi
.optionValue
, -1, 7);
1297 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[moduleIdx
].multi
.optionValue
, -128, 127);
1302 if (IS_MODULE_R9M(moduleIdx
)) {
1303 lcdDrawTextAlignedLeft(y
, STR_MODULE_TELEMETRY
);
1304 if (IS_TELEMETRY_INTERNAL_MODULE()) {
1305 lcdDrawText(MODEL_SETUP_2ND_COLUMN
, y
, STR_DISABLE_INTERNAL
);
1308 lcdDrawText(MODEL_SETUP_2ND_COLUMN
, y
, STR_MODULE_TELEM_ON
);
1311 else if (IS_MODULE_SBUS(moduleIdx
)) {
1312 lcdDrawTextAlignedLeft(y
, STR_WARN_BATTVOLTAGE
);
1313 putsVolts(lcdLastRightPos
, y
, getBatteryVoltage(), attr
| PREC2
| LEFT
);
1318 case ITEM_MODEL_EXTERNAL_MODULE_POWER
:
1320 uint8_t moduleIdx
= CURRENT_MODULE_EDITED(k
);
1321 if (IS_MODULE_R9M(moduleIdx
)) {
1322 lcdDrawTextAlignedLeft(y
, TR_MULTI_RFPOWER
);
1323 if (IS_MODULE_R9M_FCC(moduleIdx
)) {
1324 g_model
.moduleData
[moduleIdx
].pxx
.power
= min((uint8_t)g_model
.moduleData
[moduleIdx
].pxx
.power
, (uint8_t)R9M_FCC_POWER_MAX
); // Lite FCC has only one setting
1325 #if defined(MODULE_R9M_FULLSIZE)
1326 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
, y
, STR_R9M_FCC_POWER_VALUES
, g_model
.moduleData
[moduleIdx
].pxx
.power
, LEFT
| attr
);
1328 CHECK_INCDEC_MODELVAR_ZERO(event
, g_model
.moduleData
[moduleIdx
].pxx
.power
, R9M_FCC_POWER_MAX
);
1330 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
, y
, STR_R9M_FCC_POWER_VALUES
, g_model
.moduleData
[moduleIdx
].pxx
.power
, LEFT
);
1332 REPEAT_LAST_CURSOR_MOVE();
1336 g_model
.moduleData
[moduleIdx
].pxx
.power
= min((uint8_t)g_model
.moduleData
[moduleIdx
].pxx
.power
, (uint8_t)R9M_LBT_POWER_MAX
);
1337 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
, y
, STR_R9M_LBT_POWER_VALUES
, selectedPxxPower
, LEFT
| attr
);
1339 CHECK_INCDEC_MODELVAR_ZERO(event
, selectedPxxPower
, R9M_LBT_POWER_MAX
);
1341 if (attr
&& editMode
== 0 && selectedPxxPower
!= g_model
.moduleData
[moduleIdx
].pxx
.power
) {
1342 if((selectedPxxPower
+ g_model
.moduleData
[moduleIdx
].pxx
.power
) < 5) //switching between mode 2 and 3 does not require rebind
1343 POPUP_WARNING(STR_REBIND
);
1344 g_model
.moduleData
[moduleIdx
].pxx
.power
= selectedPxxPower
;
1348 #if defined(MULTIMODULE)
1349 else if (IS_MODULE_MULTIMODULE(moduleIdx
)) {
1350 g_model
.moduleData
[EXTERNAL_MODULE
].multi
.lowPowerMode
= editCheckBox(g_model
.moduleData
[EXTERNAL_MODULE
].multi
.lowPowerMode
, MODEL_SETUP_2ND_COLUMN
, y
, STR_MULTI_LOWPOWER
, attr
, event
);
1357 #if defined(MULTIMODULE)
1358 case ITEM_MODEL_EXTERNAL_MODULE_AUTOBIND
:
1359 if (g_model
.moduleData
[EXTERNAL_MODULE
].getMultiProtocol(true) == MM_RF_PROTO_DSM2
)
1360 g_model
.moduleData
[EXTERNAL_MODULE
].multi
.autoBindMode
= editCheckBox(g_model
.moduleData
[EXTERNAL_MODULE
].multi
.autoBindMode
, MODEL_SETUP_2ND_COLUMN
, y
, STR_MULTI_DSM_AUTODTECT
, attr
, event
);
1362 g_model
.moduleData
[EXTERNAL_MODULE
].multi
.autoBindMode
= editCheckBox(g_model
.moduleData
[EXTERNAL_MODULE
].multi
.autoBindMode
, MODEL_SETUP_2ND_COLUMN
, y
, STR_MULTI_AUTOBIND
, attr
, event
);
1365 case ITEM_MODEL_EXTERNAL_MODULE_STATUS
: {
1366 lcdDrawTextAlignedLeft(y
, STR_MODULE_STATUS
);
1368 char statusText
[64];
1369 multiModuleStatus
.getStatusString(statusText
);
1370 lcdDrawText(MODEL_SETUP_2ND_COLUMN
, y
, statusText
);
1374 case ITEM_MODEL_EXTERNAL_MODULE_SYNCSTATUS
: {
1375 lcdDrawTextAlignedLeft(y
, STR_MODULE_SYNC
);
1377 char statusText
[64];
1378 multiSyncStatus
.getRefreshString(statusText
);
1379 lcdDrawText(MODEL_SETUP_2ND_COLUMN
, y
, statusText
);
1385 #if !defined(CPUARM)
1386 case ITEM_MODEL_PPM1_PROTOCOL
:
1387 lcdDrawTextAlignedLeft(y
, NO_INDENT(STR_PROTO
));
1388 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
, y
, STR_VPROTOS
, protocol
, menuHorizontalPosition
<=0 ? attr
: 0);
1389 if (IS_PPM_PROTOCOL(protocol
)) {
1390 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
+7*FW
, y
, STR_NCHANNELS
, g_model
.ppmNCH
+2, menuHorizontalPosition
!=0 ? attr
: 0);
1392 else if (menuHorizontalPosition
>0 && attr
) {
1393 MOVE_CURSOR_FROM_HERE();
1395 if (attr
&& (editMode
>0 || p1valdiff
|| (!IS_PPM_PROTOCOL(protocol
) && !IS_DSM2_PROTOCOL(protocol
)))) {
1396 switch (menuHorizontalPosition
) {
1398 CHECK_INCDEC_MODELVAR_ZERO(event
, g_model
.protocol
, PROTO_MAX
-1);
1401 CHECK_INCDEC_MODELVAR(event
, g_model
.ppmNCH
, -2, 4);
1402 g_model
.ppmFrameLength
= g_model
.ppmNCH
* 8;
1410 case ITEM_MODEL_PPM2_PROTOCOL
:
1411 lcdDrawTextAlignedLeft(y
, PSTR("Port2"));
1412 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
, y
, STR_VPROTOS
, 0, 0);
1413 lcdDrawText(MODEL_SETUP_2ND_COLUMN
+4*FW
+3, y
, STR_CH
, menuHorizontalPosition
<=0 ? attr
: 0);
1414 lcdDrawNumber(lcdLastRightPos
, y
, g_model
.moduleData
[1].channelsStart
+1, LEFT
| (menuHorizontalPosition
<=0 ? attr
: 0));
1415 lcdDrawChar(lcdLastRightPos
, y
, '-');
1416 lcdDrawNumber(lcdLastRightPos
+ FW
+1, y
, g_model
.moduleData
[1].channelsStart
+8+g_model
.moduleData
[1].channelsCount
, LEFT
| (menuHorizontalPosition
!=0 ? attr
: 0));
1417 if (attr
&& (editMode
>0 || p1valdiff
)) {
1418 switch (menuHorizontalPosition
) {
1420 CHECK_INCDEC_MODELVAR_ZERO(event
, g_model
.moduleData
[1].channelsStart
, 32-8-g_model
.moduleData
[1].channelsCount
);
1421 SET_DEFAULT_PPM_FRAME_LENGTH(1);
1424 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[1].channelsCount
, -4, min
<int8_t>(8, 32-8-g_model
.moduleData
[1].channelsStart
));
1425 SET_DEFAULT_PPM_FRAME_LENGTH(1);
1431 case ITEM_MODEL_PPM2_PARAMS
:
1432 lcdDrawTextAlignedLeft(y
, STR_PPMFRAME
);
1433 lcdDrawText(MODEL_SETUP_2ND_COLUMN
+3*FW
, y
, STR_MS
);
1434 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN
, y
, (int16_t)g_model
.moduleData
[1].ppmFrameLength
*5 + 225, (menuHorizontalPosition
<=0 ? attr
: 0) | PREC1
| LEFT
);
1435 lcdDrawChar(MODEL_SETUP_2ND_COLUMN
+8*FW
+2, y
, 'u');
1436 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN
+8*FW
+2, y
, (g_model
.moduleData
[1].ppmDelay
*50)+300, RIGHT
| ((menuHorizontalPosition
< 0 || menuHorizontalPosition
==1) ? attr
: 0));
1437 lcdDrawChar(MODEL_SETUP_2ND_COLUMN
+10*FW
, y
, g_model
.moduleData
[1].ppmPulsePol
? '+' : '-', (menuHorizontalPosition
< 0 || menuHorizontalPosition
==2) ? attr
: 0);
1438 if (attr
&& (editMode
>0 || p1valdiff
)) {
1439 switch (menuHorizontalPosition
) {
1441 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[1].ppmFrameLength
, -20, 35);
1444 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[1].ppmDelay
, -4, 10);
1447 CHECK_INCDEC_MODELVAR_ZERO(event
, g_model
.moduleData
[1].ppmPulsePol
, 1);
1454 #if !defined(CPUARM)
1455 case ITEM_MODEL_PPM1_PARAMS
:
1456 if (IS_PPM_PROTOCOL(protocol
)) {
1457 lcdDrawTextAlignedLeft(y
, STR_PPMFRAME
);
1458 lcdDrawText(MODEL_SETUP_2ND_COLUMN
+3*FW
, y
, STR_MS
);
1459 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN
, y
, (int16_t)g_model
.ppmFrameLength
*5 + 225, (menuHorizontalPosition
<=0 ? attr
: 0) | PREC1
|LEFT
);
1460 lcdDrawChar(MODEL_SETUP_2ND_COLUMN
+8*FW
+2, y
, 'u');
1461 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN
+8*FW
+2, y
, (g_model
.ppmDelay
*50)+300, (CURSOR_ON_LINE() || menuHorizontalPosition
==1) ? attr
: 0);
1462 lcdDrawChar(MODEL_SETUP_2ND_COLUMN
+10*FW
, y
, g_model
.pulsePol
? '+' : '-', (CURSOR_ON_LINE() || menuHorizontalPosition
==2) ? attr
: 0);
1463 if (attr
&& (editMode
>0 || p1valdiff
)) {
1464 switch (menuHorizontalPosition
) {
1466 CHECK_INCDEC_MODELVAR(event
, g_model
.ppmFrameLength
, -20, 35);
1469 CHECK_INCDEC_MODELVAR(event
, g_model
.ppmDelay
, -4, 10);
1472 CHECK_INCDEC_MODELVAR_ZERO(event
, g_model
.pulsePol
, 1);
1477 #if defined(DSM2) || defined(PXX)
1478 else if (IS_DSM2_PROTOCOL(protocol
) || IS_PXX_PROTOCOL(protocol
)) {
1479 if (attr
&& menuHorizontalPosition
> 1) {
1480 REPEAT_LAST_CURSOR_MOVE(); // limit 3 column row to 2 colums (Rx_Num and RANGE fields)
1482 lcdDrawTextAlignedLeft(y
, STR_RECEIVER_NUM
);
1483 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN
, y
, g_model
.header
.modelId
[0], (menuHorizontalPosition
<=0 ? attr
: 0) | LEADING0
|LEFT
, 2);
1484 if (attr
&& (menuHorizontalPosition
==0 && (editMode
>0 || p1valdiff
))) {
1485 CHECK_INCDEC_MODELVAR_ZERO(event
, g_model
.header
.modelId
[0], 99);
1488 if (protocol
== PROTO_PXX
) {
1489 lcdDrawText(MODEL_SETUP_2ND_COLUMN
+4*FW
, y
, STR_SYNCMENU
, menuHorizontalPosition
!=0 ? attr
: 0);
1490 uint8_t newFlag
= 0;
1491 if (attr
&& menuHorizontalPosition
>0 && editMode
>0) {
1493 newFlag
= MODULE_BIND
;
1495 moduleFlag
[0] = newFlag
;
1499 if (IS_DSM2_PROTOCOL(protocol
)) {
1500 lcdDrawText(MODEL_SETUP_2ND_COLUMN
+4*FW
, y
, STR_MODULE_RANGE
, menuHorizontalPosition
!=0 ? attr
: 0);
1501 moduleFlag
[0] = (attr
&& menuHorizontalPosition
>0 && editMode
>0) ? MODULE_RANGECHECK
: 0; // [MENU] key toggles range check mode
1511 #if defined(CPUARM) && defined(PXX)
1512 if (IS_RANGECHECK_ENABLE()) {
1513 showMessageBox("RSSI: ");
1514 lcdDrawNumber(16+4*FW
, 5*FH
, TELEMETRY_RSSI(), BOLD
);
1518 // some field just finished being edited
1519 if (old_editMode
> 0 && s_editMode
== 0) {
1520 switch(menuVerticalPosition
) {
1521 #if defined(PCBTARANIS)
1522 case ITEM_MODEL_INTERNAL_MODULE_BIND
:
1523 if (menuHorizontalPosition
== 0)
1524 checkModelIdUnique(g_eeGeneral
.currModel
, INTERNAL_MODULE
);
1527 #if defined(PCBSKY9X)
1528 case ITEM_MODEL_EXTRA_MODULE_BIND
:
1529 if (menuHorizontalPosition
== 0)
1530 checkModelIdUnique(g_eeGeneral
.currModel
, EXTRA_MODULE
);
1534 case ITEM_MODEL_EXTERNAL_MODULE_BIND
:
1535 if (menuHorizontalPosition
== 0)
1536 checkModelIdUnique(g_eeGeneral
.currModel
, EXTERNAL_MODULE
);
1544 void menuModelFailsafe(event_t event
)
1546 const uint8_t channelStart
= g_model
.moduleData
[g_moduleIdx
].channelsStart
;
1547 const int lim
= (g_model
.extendedLimits
? (512 * LIMIT_EXT_PERCENT
/ 100) : 512) * 2;
1548 uint8_t wbar
= LCD_W
- FW
* 4 - FWNUM
* 4;
1549 #if defined(PPM_UNIT_PERCENT_PREC1)
1553 if (event
== EVT_KEY_LONG(KEY_ENTER
)) {
1557 if (menuVerticalPosition
< NUM_CHANNELS(g_moduleIdx
)) {
1559 g_model
.moduleData
[g_moduleIdx
].failsafeChannels
[menuVerticalPosition
] = channelOutputs
[menuVerticalPosition
+channelStart
];
1563 int16_t & failsafe
= g_model
.moduleData
[g_moduleIdx
].failsafeChannels
[menuVerticalPosition
];
1564 if (failsafe
< FAILSAFE_CHANNEL_HOLD
)
1565 failsafe
= FAILSAFE_CHANNEL_HOLD
;
1566 else if (failsafe
== FAILSAFE_CHANNEL_HOLD
)
1567 failsafe
= FAILSAFE_CHANNEL_NOPULSE
;
1573 // "Outputs => Failsafe" menu item
1574 setCustomFailsafe(g_moduleIdx
);
1577 storageDirty(EE_MODEL
);
1579 SEND_FAILSAFE_NOW(g_moduleIdx
);
1582 SIMPLE_SUBMENU_NOTITLE(NUM_CHANNELS(g_moduleIdx
) + 1);
1584 lcdDrawTextAlignedCenter(0, FAILSAFESET
);
1587 const coord_t x
= 1;
1589 uint8_t line
= (menuVerticalPosition
>= NUM_CHANNELS(g_moduleIdx
) ? 2 : 0);
1590 uint8_t ch
= (menuVerticalPosition
>= 8 ? 8 : 0) + line
;
1593 for (; line
< 8; line
++) {
1594 const int32_t channelValue
= channelOutputs
[ch
+channelStart
];
1595 int32_t failsafeValue
= g_model
.moduleData
[g_moduleIdx
].failsafeChannels
[ch
];
1598 putsChn(x
+1, y
, ch
+1, SMLSIZE
);
1601 LcdFlags flags
= TINSIZE
;
1602 if (menuVerticalPosition
== ch
) {
1605 if (failsafeValue
== FAILSAFE_CHANNEL_HOLD
|| failsafeValue
== FAILSAFE_CHANNEL_NOPULSE
) {
1610 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[g_moduleIdx
].failsafeChannels
[ch
], -lim
, +lim
);
1615 uint8_t xValue
= x
+LCD_W
-4-wbar
;
1616 if (failsafeValue
== FAILSAFE_CHANNEL_HOLD
) {
1617 lcdDrawText(xValue
, y
, STR_HOLD
, RIGHT
|flags
);
1620 else if (failsafeValue
== FAILSAFE_CHANNEL_NOPULSE
) {
1621 lcdDrawText(xValue
, y
, STR_NONE
, RIGHT
|flags
);
1625 #if defined(PPM_UNIT_US)
1626 lcdDrawNumber(xValue
, y
, PPM_CH_CENTER(ch
)+failsafeValue
/2, RIGHT
|flags
);
1627 #elif defined(PPM_UNIT_PERCENT_PREC1)
1628 lcdDrawNumber(xValue
, y
, calcRESXto1000(failsafeValue
), RIGHT
|PREC1
|flags
);
1630 lcdDrawNumber(xValue
, y
, calcRESXto1000(failsafeValue
)/10, RIGHT
|flags
);
1635 #if !defined(PCBX7) // X7 LCD doesn't like too many horizontal lines
1636 lcdDrawRect(x
+LCD_W
-3-wbar
, y
, wbar
+1, 6);
1638 const uint8_t lenChannel
= limit
<uint8_t>(1, (abs(channelValue
) * wbar
/2 + lim
/2) / lim
, wbar
/2);
1639 const uint8_t lenFailsafe
= limit
<uint8_t>(1, (abs(failsafeValue
) * wbar
/2 + lim
/2) / lim
, wbar
/2);
1640 const coord_t xChannel
= (channelValue
>0) ? x
+LCD_W
-3-wbar
/2 : x
+LCD_W
-2-wbar
/2-lenChannel
;
1641 const coord_t xFailsafe
= (failsafeValue
>0) ? x
+LCD_W
-3-wbar
/2 : x
+LCD_W
-2-wbar
/2-lenFailsafe
;
1642 lcdDrawHorizontalLine(xChannel
, y
+1, lenChannel
, DOTTED
, 0);
1643 lcdDrawHorizontalLine(xChannel
, y
+2, lenChannel
, DOTTED
, 0);
1644 lcdDrawSolidHorizontalLine(xFailsafe
, y
+3, lenFailsafe
);
1645 lcdDrawSolidHorizontalLine(xFailsafe
, y
+4, lenFailsafe
);
1649 if (++ch
>= NUM_CHANNELS(g_moduleIdx
))
1653 if (menuVerticalPosition
>= NUM_CHANNELS(g_moduleIdx
)) {
1654 // Outputs => Failsafe
1655 lcdDrawText(CENTER_OFS
, LCD_H
- (FH
+ 1), STR_OUTPUTS2FAILSAFE
, INVERS
);