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
,
113 #if defined(PCBTARANIS)
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 #if !defined(PCBXLITE)
121 ITEM_MODEL_TRAINER_PARAMS
,
127 #if defined(PCBSKY9X)
128 #define FIELD_PROTOCOL_MAX 2
130 #define FIELD_PROTOCOL_MAX 1
133 #define MODEL_SETUP_2ND_COLUMN (LCD_W-11*FW)
134 #define MODEL_SETUP_BIND_OFS 2*FW+1
135 #define MODEL_SETUP_RANGE_OFS 4*FW+3
136 #define MODEL_SETUP_SET_FAILSAFE_OFS 7*FW-2
138 #if defined(PCBTARANIS)
139 #define CURRENT_MODULE_EDITED(k) (k>=ITEM_MODEL_TRAINER_LABEL ? TRAINER_MODULE : (k>=ITEM_MODEL_EXTERNAL_MODULE_LABEL ? EXTERNAL_MODULE : INTERNAL_MODULE))
140 #elif defined(PCBSKY9X) && !defined(REVA)
141 #define CURRENT_MODULE_EDITED(k) (k>=ITEM_MODEL_EXTRA_MODULE_LABEL ? EXTRA_MODULE : EXTERNAL_MODULE)
143 #define CURRENT_MODULE_EDITED(k) (EXTERNAL_MODULE)
147 #if defined(PCBXLITE)
148 #define SW_WARN_ROWS uint8_t(NAVIGATION_LINE_BY_LINE|getSwitchWarningsCount()), uint8_t(getSwitchWarningsCount() > 5 ? TITLE_ROW : HIDDEN_ROW) //xlite needs an additional column for full line selection (<])
150 #define SW_WARN_ROWS uint8_t(NAVIGATION_LINE_BY_LINE|(getSwitchWarningsCount()-1)), uint8_t(getSwitchWarningsCount() > 5 ? TITLE_ROW : HIDDEN_ROW)
152 #if !defined(TARANIS_INTERNAL_PPM)
153 #define INTERNAL_MODULE_MODE_ROWS 0 // (OFF / RF protocols)
155 #define INTERNAL_MODULE_MODE_ROWS (IS_MODULE_XJT(INTERNAL_MODULE) ? (uint8_t)1 : (uint8_t)0) // Module type + RF protocols
157 #define IF_INTERNAL_MODULE_ON(x) (IS_INTERNAL_MODULE_ENABLED()? (uint8_t)(x) : HIDDEN_ROW )
158 #define IF_EXTERNAL_MODULE_ON(x) (IS_EXTERNAL_MODULE_ENABLED()? (uint8_t)(x) : HIDDEN_ROW)
159 #define INTERNAL_MODULE_CHANNELS_ROWS IF_INTERNAL_MODULE_ON(1)
160 #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
162 #if defined(PCBSKY9X) && defined(REVX)
163 #define OUTPUT_TYPE_ROWS() (IS_MODULE_PPM(EXTERNAL_MODULE) ? (uint8_t)0 : HIDDEN_ROW) ,
165 #define OUTPUT_TYPE_ROWS()
167 #define PORT_CHANNELS_ROWS(x) (x==EXTERNAL_MODULE ? EXTERNAL_MODULE_CHANNELS_ROWS : 0)
169 #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
171 #define CURSOR_ON_CELL (true)
172 #define MODEL_SETUP_MAX_LINES (HEADER_LINE+ITEM_MODEL_SETUP_MAX)
173 #define POT_WARN_ITEMS() ((g_model.potsWarnMode) ? (uint8_t)(NUM_POTS+NUM_SLIDERS) : (uint8_t)0)
174 #define TIMER_ROWS 2, 0, CASE_PERSISTENT_TIMERS(0) 0, 0
175 #if defined(PCBSKY9X) && !defined(REVA)
176 #define EXTRA_MODULE_ROWS LABEL(ExtraModule), 1, 2,
178 #define EXTRA_MODULE_ROWS
182 #if defined(BLUETOOTH)
183 #define TRAINER_BLUETOOTH_M_ROW ((bluetoothDistantAddr[0] == '\0' || bluetoothState == BLUETOOTH_STATE_CONNECTED) ? (uint8_t)0 : (uint8_t)1)
184 #define TRAINER_BLUETOOTH_S_ROW (bluetoothDistantAddr[0] == '\0' ? HIDDEN_ROW : LABEL())
185 #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)),
187 #define TRAINER_BLUETOOTH_ROW
189 #define TRAINER_CHANNELS_ROW (IS_SLAVE_TRAINER() ? (uint8_t)1 : HIDDEN_ROW)
190 #define TRAINER_PARAMS_ROW (IS_SLAVE_TRAINER() ? (uint8_t)2 : HIDDEN_ROW)
191 #define TRAINER_ROWS LABEL(Trainer), 0, TRAINER_BLUETOOTH_ROW TRAINER_CHANNELS_ROW, TRAINER_PARAMS_ROW
192 #elif defined(PCBXLITE)
193 #define TRAINER_BLUETOOTH_M_ROW ((bluetoothDistantAddr[0] == '\0' || bluetoothState == BLUETOOTH_STATE_CONNECTED) ? (uint8_t)0 : (uint8_t)1)
194 #define TRAINER_BLUETOOTH_S_ROW (bluetoothDistantAddr[0] == '\0' ? HIDDEN_ROW : LABEL())
195 #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))
196 #define TRAINER_CHANNELS_ROW (IS_SLAVE_TRAINER() ? (uint8_t)1 : HIDDEN_ROW)
197 #define TRAINER_ROWS LABEL(Trainer), 0, TRAINER_BLUETOOTH_ROW, TRAINER_CHANNELS_ROW
202 #elif defined(CPUM64)
203 #define CURSOR_ON_CELL (true)
204 #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)
205 #elif defined(PCBXLITE)
206 #define CURSOR_ON_CELL (menuHorizontalPosition >= 0)
207 #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)
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)
213 #if defined(PCBTARANIS)
214 void onBindMenu(const char * result
)
216 uint8_t moduleIdx
= CURRENT_MODULE_EDITED(menuVerticalPosition
);
218 if (result
== STR_BINDING_1_8_TELEM_ON
) {
219 g_model
.moduleData
[moduleIdx
].pxx
.receiver_telem_off
= false;
220 g_model
.moduleData
[moduleIdx
].pxx
.receiver_channel_9_16
= false;
222 else if (result
== STR_BINDING_1_8_TELEM_OFF
) {
223 g_model
.moduleData
[moduleIdx
].pxx
.receiver_telem_off
= true;
224 g_model
.moduleData
[moduleIdx
].pxx
.receiver_channel_9_16
= false;
226 else if (result
== STR_BINDING_9_16_TELEM_ON
) {
227 g_model
.moduleData
[moduleIdx
].pxx
.receiver_telem_off
= false;
228 g_model
.moduleData
[moduleIdx
].pxx
.receiver_channel_9_16
= true;
230 else if (result
== STR_BINDING_9_16_TELEM_OFF
) {
231 g_model
.moduleData
[moduleIdx
].pxx
.receiver_telem_off
= true;
232 g_model
.moduleData
[moduleIdx
].pxx
.receiver_channel_9_16
= true;
238 moduleFlag
[moduleIdx
] = MODULE_BIND
;
243 void menuModelSetup(event_t event
)
245 #if defined(PCBXLITE)
246 // Switch to external antenna confirmation
249 g_model
.moduleData
[INTERNAL_MODULE
].pxx
.external_antenna
= XJT_EXTERNAL_ANTENNA
;
254 static uint8_t selectedPxxPower
= g_model
.moduleData
[EXTERNAL_MODULE
].pxx
.power
; //TODO could go to the reusable struct
257 #if defined(PCBXLITE)
258 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, SW_WARN_ROWS
, POT_WARN_ITEMS(), NUM_STICKS
+ NUM_POTS
+ NUM_SLIDERS
+ NUM_ROTARY_ENCODERS
- 1, 0,
259 LABEL(InternalModule
),
260 INTERNAL_MODULE_MODE_ROWS
,
261 INTERNAL_MODULE_CHANNELS_ROWS
,
262 IF_INTERNAL_MODULE_ON(HAS_RF_PROTOCOL_MODELINDEX(g_model
.moduleData
[INTERNAL_MODULE
].rfProtocol
) ? (uint8_t)2 : (uint8_t)1),
263 IF_INTERNAL_MODULE_ON(FAILSAFE_ROWS(INTERNAL_MODULE
)),
264 IF_INTERNAL_MODULE_ON(0),
265 LABEL(ExternalModule
),
266 EXTERNAL_MODULE_MODE_ROWS
,
267 MULTIMODULE_SUBTYPE_ROWS(EXTERNAL_MODULE
)
268 MULTIMODULE_STATUS_ROWS
269 EXTERNAL_MODULE_CHANNELS_ROWS
,
270 EXTERNAL_MODULE_BIND_ROWS(),
272 FAILSAFE_ROWS(EXTERNAL_MODULE
),
273 EXTERNAL_MODULE_OPTION_ROW
,
274 MULTIMODULE_MODULE_ROWS
275 EXTERNAL_MODULE_POWER_ROW
,
278 #elif defined(PCBTARANIS)
279 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, SW_WARN_ROWS
, POT_WARN_ITEMS(), NUM_STICKS
+NUM_POTS
+NUM_SLIDERS
+NUM_ROTARY_ENCODERS
-1, 0,
280 LABEL(InternalModule
),
281 INTERNAL_MODULE_MODE_ROWS
,
282 INTERNAL_MODULE_CHANNELS_ROWS
,
283 IF_INTERNAL_MODULE_ON(HAS_RF_PROTOCOL_MODELINDEX(g_model
.moduleData
[INTERNAL_MODULE
].rfProtocol
) ? (uint8_t)2 : (uint8_t)1),
284 IF_INTERNAL_MODULE_ON(FAILSAFE_ROWS(INTERNAL_MODULE
)),
285 LABEL(ExternalModule
),
286 EXTERNAL_MODULE_MODE_ROWS
,
287 MULTIMODULE_SUBTYPE_ROWS(EXTERNAL_MODULE
)
288 MULTIMODULE_STATUS_ROWS
289 EXTERNAL_MODULE_CHANNELS_ROWS
,
290 EXTERNAL_MODULE_BIND_ROWS(),
292 FAILSAFE_ROWS(EXTERNAL_MODULE
),
293 EXTERNAL_MODULE_OPTION_ROW
,
294 MULTIMODULE_MODULE_ROWS
295 EXTERNAL_MODULE_POWER_ROW
,
298 #elif defined(CPUARM)
299 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,
300 LABEL(ExternalModule
),
301 EXTERNAL_MODULE_MODE_ROWS
,
302 MULTIMODULE_SUBTYPE_ROWS(EXTERNAL_MODULE
)
303 MULTIMODULE_STATUS_ROWS
304 EXTERNAL_MODULE_CHANNELS_ROWS
,
305 EXTERNAL_MODULE_BIND_ROWS(),
307 FAILSAFE_ROWS(EXTERNAL_MODULE
),
308 EXTERNAL_MODULE_OPTION_ROW
,
309 MULTIMODULE_MODULE_ROWS
310 EXTERNAL_MODULE_POWER_ROW
,
313 #elif defined(CPUM64)
314 uint8_t protocol
= g_model
.protocol
;
315 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 });
317 uint8_t protocol
= g_model
.protocol
;
318 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) });
321 MENU_CHECK(menuTabModel
, MENU_MODEL_SETUP
, MODEL_SETUP_MAX_LINES
);
323 #if defined(CPUARM) && (defined(DSM2) || defined(PXX))
332 TITLE(STR_MENUSETUP
);
334 uint8_t sub
= menuVerticalPosition
- HEADER_LINE
;
335 int8_t editMode
= s_editMode
;
337 for (uint8_t i
=0; i
<NUM_BODY_LINES
; ++i
) {
338 coord_t y
= MENU_HEADER_HEIGHT
+ 1 + i
*FH
;
339 uint8_t k
= i
+menuVerticalOffset
;
341 for (int j
=0; j
<=k
; j
++) {
342 if (mstate_tab
[j
+HEADER_LINE
] == HIDDEN_ROW
) {
343 if (++k
>= (int)DIM(mstate_tab
)) {
350 LcdFlags blink
= ((editMode
>0) ? BLINK
|INVERS
: INVERS
);
351 LcdFlags attr
= (sub
== k
? blink
: 0);
354 case ITEM_MODEL_NAME
:
355 editSingleName(MODEL_SETUP_2ND_COLUMN
, y
, STR_MODELNAME
, g_model
.header
.name
, sizeof(g_model
.header
.name
), event
, attr
);
357 memcpy(modelHeaders
[g_eeGeneral
.currModel
].name
, g_model
.header
.name
, sizeof(g_model
.header
.name
));
362 case ITEM_MODEL_TIMER1
:
363 case ITEM_MODEL_TIMER2
:
364 case ITEM_MODEL_TIMER3
:
366 unsigned int timerIdx
= (k
>=ITEM_MODEL_TIMER3
? 2 : (k
>=ITEM_MODEL_TIMER2
? 1 : 0));
367 TimerData
* timer
= &g_model
.timers
[timerIdx
];
368 drawStringWithIndex(0*FW
, y
, STR_TIMER
, timerIdx
+1);
369 drawTimerMode(MODEL_SETUP_2ND_COLUMN
, y
, timer
->mode
, menuHorizontalPosition
==0 ? attr
: 0);
370 drawTimer(MODEL_SETUP_2ND_COLUMN
+5*FW
-2+5*FWNUM
+1, y
, timer
->start
, RIGHT
| (menuHorizontalPosition
==1 ? attr
: 0), menuHorizontalPosition
==2 ? attr
: 0);
371 if (attr
&& (editMode
>0 || p1valdiff
)) {
372 div_t qr
= div(timer
->start
, 60);
373 switch (menuHorizontalPosition
) {
376 int8_t timerMode
= timer
->mode
;
377 if (timerMode
< 0) timerMode
-= TMRMODE_COUNT
-1;
378 CHECK_INCDEC_MODELVAR_CHECK(event
, timerMode
, -TMRMODE_COUNT
-SWSRC_LAST
+1, TMRMODE_COUNT
+SWSRC_LAST
-1, isSwitchAvailableInTimers
);
379 if (timerMode
< 0) timerMode
+= TMRMODE_COUNT
-1;
380 timer
->mode
= timerMode
;
381 #if defined(AUTOSWITCH)
383 int8_t val
= timer
->mode
- (TMRMODE_COUNT
-1);
384 int8_t switchVal
= checkIncDecMovedSwitch(val
);
385 if (val
!= switchVal
) {
386 timer
->mode
= switchVal
+ (TMRMODE_COUNT
-1);
387 storageDirty(EE_MODEL
);
394 CHECK_INCDEC_MODELVAR_ZERO(event
, qr
.quot
, 539); // 8:59
395 timer
->start
= qr
.rem
+ qr
.quot
*60;
398 qr
.rem
-= checkIncDecModel(event
, qr
.rem
+2, 1, 62)-2;
399 timer
->start
-= qr
.rem
;
400 if ((int16_t)timer
->start
< 0) timer
->start
=0;
401 if ((int16_t)timer
->start
> 5999) timer
->start
=32399; // 8:59:59
408 case ITEM_MODEL_TIMER1_NAME
:
409 case ITEM_MODEL_TIMER2_NAME
:
410 case ITEM_MODEL_TIMER3_NAME
:
412 TimerData
* timer
= &g_model
.timers
[k
>=ITEM_MODEL_TIMER3
? 2 : (k
>=ITEM_MODEL_TIMER2
? 1 : 0)];
413 editSingleName(MODEL_SETUP_2ND_COLUMN
, y
, STR_TIMER_NAME
, timer
->name
, sizeof(timer
->name
), event
, attr
);
417 case ITEM_MODEL_TIMER1_MINUTE_BEEP
:
418 case ITEM_MODEL_TIMER2_MINUTE_BEEP
:
419 case ITEM_MODEL_TIMER3_MINUTE_BEEP
:
421 TimerData
* timer
= &g_model
.timers
[k
>=ITEM_MODEL_TIMER3
? 2 : (k
>=ITEM_MODEL_TIMER2
? 1 : 0)];
422 timer
->minuteBeep
= editCheckBox(timer
->minuteBeep
, MODEL_SETUP_2ND_COLUMN
, y
, STR_MINUTEBEEP
, attr
, event
);
426 case ITEM_MODEL_TIMER1_COUNTDOWN_BEEP
:
427 case ITEM_MODEL_TIMER2_COUNTDOWN_BEEP
:
428 case ITEM_MODEL_TIMER3_COUNTDOWN_BEEP
:
430 TimerData
* timer
= &g_model
.timers
[k
>=ITEM_MODEL_TIMER3
? 2 : (k
>=ITEM_MODEL_TIMER2
? 1 : 0)];
431 timer
->countdownBeep
= editChoice(MODEL_SETUP_2ND_COLUMN
, y
, STR_BEEPCOUNTDOWN
, STR_VBEEPCOUNTDOWN
, timer
->countdownBeep
, COUNTDOWN_SILENT
, COUNTDOWN_COUNT
-1, attr
, event
);
435 case ITEM_MODEL_TIMER1_PERSISTENT
:
436 case ITEM_MODEL_TIMER2_PERSISTENT
:
437 case ITEM_MODEL_TIMER3_PERSISTENT
:
439 TimerData
* timer
= &g_model
.timers
[k
>=ITEM_MODEL_TIMER3
? 2 : (k
>=ITEM_MODEL_TIMER2
? 1 : 0)];
440 timer
->persistent
= editChoice(MODEL_SETUP_2ND_COLUMN
, y
, STR_PERSISTENT
, STR_VPERSISTENT
, timer
->persistent
, 0, 2, attr
, event
);
444 case ITEM_MODEL_TIMER1
:
445 case ITEM_MODEL_TIMER2
:
446 case ITEM_MODEL_TIMER1_MINUTE_BEEP
:
447 case ITEM_MODEL_TIMER2_MINUTE_BEEP
:
448 case ITEM_MODEL_TIMER1_COUNTDOWN_BEEP
:
449 case ITEM_MODEL_TIMER2_COUNTDOWN_BEEP
:
451 TimerData
*timer
= &g_model
.timers
[k
>=ITEM_MODEL_TIMER2
? 1 : 0];
452 if (k
==ITEM_MODEL_TIMER1_MINUTE_BEEP
|| k
==ITEM_MODEL_TIMER2_MINUTE_BEEP
) {
453 timer
->minuteBeep
= editCheckBox(timer
->minuteBeep
, MODEL_SETUP_2ND_COLUMN
, y
, STR_MINUTEBEEP
, attr
, event
);
455 else if (k
==ITEM_MODEL_TIMER1_COUNTDOWN_BEEP
|| k
==ITEM_MODEL_TIMER2_COUNTDOWN_BEEP
) {
456 timer
->countdownBeep
= editCheckBox(timer
->countdownBeep
, MODEL_SETUP_2ND_COLUMN
, y
, STR_BEEPCOUNTDOWN
, attr
, event
);
459 drawStringWithIndex(0*FW
, y
, STR_TIMER
, k
>=ITEM_MODEL_TIMER2
? 2 : 1);
460 drawTimerMode(MODEL_SETUP_2ND_COLUMN
, y
, timer
->mode
, menuHorizontalPosition
==0 ? attr
: 0);
461 drawTimer(MODEL_SETUP_2ND_COLUMN
+5*FW
-2+5*FWNUM
+1, y
, timer
->start
, menuHorizontalPosition
==1 ? attr
: 0, menuHorizontalPosition
==2 ? attr
: 0);
462 if (attr
&& (editMode
>0 || p1valdiff
)) {
463 div_t qr
= div(timer
->start
, 60);
464 switch (menuHorizontalPosition
) {
466 CHECK_INCDEC_MODELVAR_CHECK(event
, timer
->mode
, SWSRC_FIRST
, TMRMODE_COUNT
+SWSRC_LAST
-1/*SWSRC_None removed*/, isSwitchAvailableInTimers
);
469 CHECK_INCDEC_MODELVAR_ZERO(event
, qr
.quot
, 59);
470 timer
->start
= qr
.rem
+ qr
.quot
*60;
473 qr
.rem
-= checkIncDecModel(event
, qr
.rem
+2, 1, 62) - 2;
474 if ((int16_t)timer
->start
>= qr
.rem
) {
475 timer
->start
-= qr
.rem
;
477 if ((int16_t)timer
->start
> 3599) {
478 timer
->start
= 3599; // 59:59
487 #if defined(CPUM2560)
488 case ITEM_MODEL_TIMER1_PERSISTENT
:
489 case ITEM_MODEL_TIMER2_PERSISTENT
:
491 TimerData
&timer
= g_model
.timers
[k
==ITEM_MODEL_TIMER2_PERSISTENT
];
492 timer
.persistent
= editChoice(MODEL_SETUP_2ND_COLUMN
, y
, STR_PERSISTENT
, STR_VPERSISTENT
, timer
.persistent
, 0, 2, attr
, event
);
498 case ITEM_MODEL_EXTENDED_LIMITS
:
499 ON_OFF_MENU_ITEM(g_model
.extendedLimits
, MODEL_SETUP_2ND_COLUMN
, y
, STR_ELIMITS
, attr
, event
);
502 case ITEM_MODEL_EXTENDED_TRIMS
:
504 ON_OFF_MENU_ITEM(g_model
.extendedTrims
, MODEL_SETUP_2ND_COLUMN
, y
, STR_ETRIMS
, attr
, event
);
506 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);
507 lcdDrawText(MODEL_SETUP_2ND_COLUMN
+4*FW
, y
, STR_RESET_BTN
, (menuHorizontalPosition
>0 && !NO_HIGHLIGHT()) ? attr
: 0);
508 if (attr
&& menuHorizontalPosition
>0) {
510 if (event
==EVT_KEY_LONG(KEY_ENTER
)) {
511 START_NO_HIGHLIGHT();
512 for (uint8_t i
=0; i
<MAX_FLIGHT_MODES
; i
++) {
513 memclear(&g_model
.flightModeData
[i
], TRIMS_ARRAY_SIZE
);
515 storageDirty(EE_MODEL
);
523 case ITEM_MODEL_DISPLAY_TRIMS
:
524 g_model
.displayTrims
= editChoice(MODEL_SETUP_2ND_COLUMN
, y
, STR_DISPLAY_TRIMS
, STR_VDISPLAYTRIMS
, g_model
.displayTrims
, 0, 2, attr
, event
);
528 case ITEM_MODEL_TRIM_INC
:
529 g_model
.trimInc
= editChoice(MODEL_SETUP_2ND_COLUMN
, y
, STR_TRIMINC
, STR_VTRIMINC
, g_model
.trimInc
, -2, 2, attr
, event
);
532 case ITEM_MODEL_THROTTLE_REVERSED
:
533 ON_OFF_MENU_ITEM(g_model
.throttleReversed
, MODEL_SETUP_2ND_COLUMN
, y
, STR_THROTTLEREVERSE
, attr
, event
) ;
536 case ITEM_MODEL_THROTTLE_TRACE
:
538 lcdDrawTextAlignedLeft(y
, STR_TTRACE
);
539 if (attr
) CHECK_INCDEC_MODELVAR_ZERO(event
, g_model
.thrTraceSrc
, NUM_POTS
+NUM_SLIDERS
+MAX_OUTPUT_CHANNELS
);
540 uint8_t idx
= g_model
.thrTraceSrc
+ MIXSRC_Thr
;
541 if (idx
> MIXSRC_Thr
)
543 if (idx
>= MIXSRC_FIRST_POT
+NUM_POTS
+NUM_SLIDERS
)
544 idx
+= MIXSRC_CH1
- MIXSRC_FIRST_POT
- NUM_POTS
- NUM_SLIDERS
;
545 drawSource(MODEL_SETUP_2ND_COLUMN
, y
, idx
, attr
);
549 case ITEM_MODEL_THROTTLE_TRIM
:
550 ON_OFF_MENU_ITEM(g_model
.thrTrim
, MODEL_SETUP_2ND_COLUMN
, y
, STR_TTRIM
, attr
, event
);
554 case ITEM_MODEL_PREFLIGHT_LABEL
:
555 lcdDrawTextAlignedLeft(y
, STR_PREFLIGHT
);
558 case ITEM_MODEL_CHECKLIST_DISPLAY
:
559 ON_OFF_MENU_ITEM(g_model
.displayChecklist
, MODEL_SETUP_2ND_COLUMN
, y
, STR_CHECKLIST
, attr
, event
);
563 case ITEM_MODEL_THROTTLE_WARNING
:
564 g_model
.disableThrottleWarning
= !editCheckBox(!g_model
.disableThrottleWarning
, MODEL_SETUP_2ND_COLUMN
, y
, STR_THROTTLEWARNING
, attr
, event
);
567 #if defined(PCBTARANIS)
568 case ITEM_MODEL_SWITCHES_WARNING2
:
570 if (CURSOR_MOVED_LEFT(event
))
571 menuVerticalOffset
--;
573 menuVerticalOffset
++;
578 case ITEM_MODEL_SWITCHES_WARNING
:
579 #if defined(PCBTARANIS)
581 #define FIRSTSW_STR STR_VSRCRAW+(MIXSRC_FIRST_SWITCH-MIXSRC_Rud+1)*length
582 uint8_t length
= pgm_read_byte(STR_VSRCRAW
);
583 horzpos_t l_posHorz
= menuHorizontalPosition
;
585 if (i
>=NUM_BODY_LINES
-2 && getSwitchWarningsCount() > 5*(NUM_BODY_LINES
-i
)) {
586 if (CURSOR_MOVED_LEFT(event
))
587 menuVerticalOffset
--;
589 menuVerticalOffset
++;
593 swarnstate_t states
= g_model
.switchWarningState
;
596 lcdDrawTextAlignedLeft(y
, STR_SWITCHWARNING
);
597 #if defined(PCBXLITE)
598 lcdDrawText(LCD_W
, y
, "<]", RIGHT
);
599 if (menuHorizontalPosition
> NUM_SWITCHES
) menuHorizontalPosition
= NUM_SWITCHES
;
600 if ((attr
) && (menuHorizontalPosition
== NUM_SWITCHES
)) {
607 case EVT_KEY_BREAK(KEY_ENTER
):
610 case EVT_KEY_LONG(KEY_ENTER
):
611 if (menuHorizontalPosition
< 0 || menuHorizontalPosition
>= NUM_SWITCHES
) {
612 START_NO_HIGHLIGHT();
614 g_model
.switchWarningState
= switches_states
;
616 storageDirty(EE_MODEL
);
624 LcdFlags line
= attr
;
627 for (int i
=0; i
<NUM_SWITCHES
; i
++) {
628 if (SWITCH_WARNING_ALLOWED(i
)) {
629 div_t qr
= div(current
, 5);
630 if (!READ_ONLY() && event
==EVT_KEY_BREAK(KEY_ENTER
) && line
&& l_posHorz
==current
) {
631 g_model
.switchWarningEnable
^= (1 << i
);
632 storageDirty(EE_MODEL
);
633 #if defined(PCBXLITE)
637 uint8_t swactive
= !(g_model
.switchWarningEnable
& (1<<i
));
638 c
= "\300-\301"[states
& 0x03];
639 //lcdDrawChar(MODEL_SETUP_2ND_COLUMN+qr.rem*(2*FW+1), y+FH*qr.quot, 'A'+i, line && (menuHorizontalPosition==current) ? INVERS : 0);
640 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);
641 if (swactive
) lcdDrawChar(lcdNextPos
, y
+FH
*qr
.quot
, c
);
646 if (attr
&& ((menuHorizontalPosition
< 0) || menuHorizontalPosition
>= NUM_SWITCHES
)) {
647 lcdDrawFilledRect(MODEL_SETUP_2ND_COLUMN
-1, y
-1, 8*(2*FW
+1), 1+FH
*((current
+4)/5));
651 lcdDrawTextAlignedLeft(y
, STR_SWITCHWARNING
);
652 swarnstate_t states
= g_model
.switchWarningState
;
658 CASE_EVT_ROTARY_BREAK
659 case EVT_KEY_BREAK(KEY_ENTER
):
661 g_model
.switchWarningEnable
^= (1 << menuHorizontalPosition
);
662 storageDirty(EE_MODEL
);
664 if (menuHorizontalPosition
< NUM_SWITCHES
-1) {
665 g_model
.switchWarningEnable
^= (1 << menuHorizontalPosition
);
666 storageDirty(EE_MODEL
);
671 case EVT_KEY_LONG(KEY_ENTER
):
674 g_model
.switchWarningState
= switches_states
;
676 storageDirty(EE_MODEL
);
678 if (menuHorizontalPosition
== NUM_SWITCHES
-1) {
679 START_NO_HIGHLIGHT();
681 g_model
.switchWarningState
= switches_states
;
683 storageDirty(EE_MODEL
);
691 LcdFlags line
= attr
;
693 for (uint8_t i
=0; i
<NUM_SWITCHES
-1/*not on TRN switch*/; i
++) {
694 uint8_t swactive
= !(g_model
.switchWarningEnable
& 1 << i
);
698 c
= '0'+(states
& 0x03);
702 if ((states
& 0x01) && swactive
)
704 c
= pgm_read_byte(STR_VSWITCHES
- 2 + 9 + (3*(i
+1)));
707 if (line
&& (menuHorizontalPosition
== i
)) {
708 attr
= BLINK
| INVERS
;
711 lcdDrawChar(MODEL_SETUP_2ND_COLUMN
+i
*FW
, y
, (swactive
) ? c
: '-', attr
);
713 lcdDrawChar(MODEL_SETUP_2ND_COLUMN
+i
*FW
, y
, (swactive
|| (attr
& BLINK
)) ? c
: '-', attr
);
716 lcdDrawText(MODEL_SETUP_2ND_COLUMN
+(NUM_SWITCHES
*FW
), y
, PSTR("<]"), (menuHorizontalPosition
== NUM_SWITCHES
-1 && !NO_HIGHLIGHT()) ? line
: 0);
722 #if defined(PCBTARANIS)
723 case ITEM_MODEL_POTS_WARNING
:
724 lcdDrawTextAlignedLeft(y
, STR_POTWARNING
);
725 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
, y
, PSTR("\004""OFF\0""Man\0""Auto"), g_model
.potsWarnMode
, (menuHorizontalPosition
== 0) ? attr
: 0);
726 if (attr
&& (menuHorizontalPosition
== 0)) {
727 CHECK_INCDEC_MODELVAR(event
, g_model
.potsWarnMode
, POTS_WARN_OFF
, POTS_WARN_AUTO
);
728 storageDirty(EE_MODEL
);
732 if (menuHorizontalPosition
> 0) s_editMode
= 0;
733 if (!READ_ONLY() && menuHorizontalPosition
> 0) {
735 case EVT_KEY_LONG(KEY_ENTER
):
737 if (g_model
.potsWarnMode
== POTS_WARN_MANUAL
) {
738 SAVE_POT_POSITION(menuHorizontalPosition
-1);
740 storageDirty(EE_MODEL
);
743 case EVT_KEY_BREAK(KEY_ENTER
):
744 g_model
.potsWarnEnabled
^= (1 << (menuHorizontalPosition
-1));
745 storageDirty(EE_MODEL
);
750 if (g_model
.potsWarnMode
) {
751 coord_t x
= MODEL_SETUP_2ND_COLUMN
+28;
752 for (int i
=0; i
<NUM_POTS
+NUM_SLIDERS
; ++i
) {
753 if (i
<NUM_XPOTS
&& !IS_POT_SLIDER_AVAILABLE(POT1
+i
)) {
754 if (attr
&& (menuHorizontalPosition
==i
+1)) REPEAT_LAST_CURSOR_MOVE();
757 LcdFlags flags
= ((menuHorizontalPosition
==i
+1) && attr
) ? BLINK
: 0;
758 if ((!attr
|| menuHorizontalPosition
>= 0) && !(g_model
.potsWarnEnabled
& (1 << i
))) {
762 // TODO add a new function
763 lcdDrawSizedText(x
, y
, STR_VSRCRAW
+2+STR_VSRCRAW
[0]*(NUM_STICKS
+1+i
), STR_VSRCRAW
[0]-1, flags
& ~ZCHAR
);
771 case ITEM_MODEL_BEEP_CENTER
:
772 lcdDrawTextAlignedLeft(y
, STR_BEEPCTR
);
773 for (uint8_t i
=0; i
<NUM_STICKS
+NUM_POTS
+NUM_SLIDERS
+NUM_ROTARY_ENCODERS
; i
++) {
774 // TODO flash saving, \001 not needed in STR_RETA123
775 coord_t x
= MODEL_SETUP_2ND_COLUMN
+i
*FW
;
776 lcdDrawTextAtIndex(x
, y
, STR_RETA123
, i
, ((menuHorizontalPosition
==i
) && attr
) ? BLINK
|INVERS
: (((g_model
.beepANACenter
& ((BeepANACenter
)1<<i
)) || (attr
&& CURSOR_ON_LINE())) ? INVERS
: 0 ) );
778 if (attr
&& CURSOR_ON_CELL
) {
779 if (event
==EVT_KEY_BREAK(KEY_ENTER
) || p1valdiff
) {
780 if (READ_ONLY_UNLOCKED()) {
782 g_model
.beepANACenter
^= ((BeepANACenter
)1<<menuHorizontalPosition
);
783 storageDirty(EE_MODEL
);
790 case ITEM_MODEL_USE_GLOBAL_FUNCTIONS
:
791 lcdDrawTextAlignedLeft(y
, STR_USE_GLOBAL_FUNCS
);
792 drawCheckBox(MODEL_SETUP_2ND_COLUMN
, y
, !g_model
.noGlobalFunctions
, attr
);
793 if (attr
) g_model
.noGlobalFunctions
= !checkIncDecModel(event
, !g_model
.noGlobalFunctions
, 0, 1);
797 #if defined(PCBTARANIS)
798 case ITEM_MODEL_INTERNAL_MODULE_LABEL
:
799 lcdDrawTextAlignedLeft(y
, TR_INTERNALRF
);
802 case ITEM_MODEL_INTERNAL_MODULE_MODE
:
803 lcdDrawTextAlignedLeft(y
, STR_MODE
);
804 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
, y
, STR_XJT_PROTOCOLS
, 1+g_model
.moduleData
[0].rfProtocol
, attr
);
806 g_model
.moduleData
[INTERNAL_MODULE
].rfProtocol
= checkIncDec(event
, g_model
.moduleData
[INTERNAL_MODULE
].rfProtocol
, RF_PROTO_OFF
, RF_PROTO_LAST
, EE_MODEL
, isRfProtocolAvailable
);
807 if (checkIncDec_Ret
) {
808 g_model
.moduleData
[0].type
= MODULE_TYPE_XJT
;
809 g_model
.moduleData
[0].channelsStart
= 0;
810 g_model
.moduleData
[0].channelsCount
= DEFAULT_CHANNELS(INTERNAL_MODULE
);
811 if (g_model
.moduleData
[INTERNAL_MODULE
].rfProtocol
== RF_PROTO_OFF
)
812 g_model
.moduleData
[INTERNAL_MODULE
].type
= MODULE_TYPE_NONE
;
818 #if defined(PCBSKY9X)
819 case ITEM_MODEL_EXTRA_MODULE_LABEL
:
820 lcdDrawTextAlignedLeft(y
, "RF Port 2 (PPM)");
825 case ITEM_MODEL_EXTERNAL_MODULE_LABEL
:
826 lcdDrawTextAlignedLeft(y
, TR_EXTERNALRF
);
829 case ITEM_MODEL_EXTERNAL_MODULE_MODE
:
830 lcdDrawTextAlignedLeft(y
, STR_MODE
);
831 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
, y
, STR_TARANIS_PROTOCOLS
, g_model
.moduleData
[EXTERNAL_MODULE
].type
, menuHorizontalPosition
==0 ? attr
: 0);
832 if (IS_MODULE_XJT(EXTERNAL_MODULE
))
833 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
+5*FW
, y
, STR_XJT_PROTOCOLS
, 1+g_model
.moduleData
[EXTERNAL_MODULE
].rfProtocol
, menuHorizontalPosition
==1 ? attr
: 0);
834 else if (IS_MODULE_DSM2(EXTERNAL_MODULE
))
835 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
+5*FW
, y
, STR_DSM_PROTOCOLS
, g_model
.moduleData
[EXTERNAL_MODULE
].rfProtocol
, menuHorizontalPosition
==1 ? attr
: 0);
836 else if (IS_MODULE_R9M(EXTERNAL_MODULE
))
837 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
+5*FW
, y
, STR_R9M_REGION
, g_model
.moduleData
[EXTERNAL_MODULE
].subType
, (menuHorizontalPosition
==1 ? attr
: 0));
838 #if defined(MULTIMODULE)
839 else if (IS_MODULE_MULTIMODULE(EXTERNAL_MODULE
)) {
840 int multi_rfProto
= g_model
.moduleData
[EXTERNAL_MODULE
].getMultiProtocol(false);
841 if (g_model
.moduleData
[EXTERNAL_MODULE
].multi
.customProto
)
842 lcdDrawText(MODEL_SETUP_2ND_COLUMN
+5*FW
, y
, STR_MULTI_CUSTOM
, menuHorizontalPosition
==1 ? attr
: 0);
844 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
+5*FW
, y
, STR_MULTI_PROTOCOLS
, multi_rfProto
, menuHorizontalPosition
==1 ? attr
: 0);
847 if (attr
&& (editMode
>0 || p1valdiff
)) {
848 switch (menuHorizontalPosition
) {
850 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
);
851 if (checkIncDec_Ret
) {
852 g_model
.moduleData
[EXTERNAL_MODULE
].rfProtocol
= 0;
853 g_model
.moduleData
[EXTERNAL_MODULE
].channelsStart
= 0;
854 g_model
.moduleData
[EXTERNAL_MODULE
].channelsCount
= DEFAULT_CHANNELS(EXTERNAL_MODULE
);
855 if (IS_MODULE_SBUS(EXTERNAL_MODULE
))
856 g_model
.moduleData
[EXTERNAL_MODULE
].sbus
.refreshRate
= -31;
860 if (IS_MODULE_DSM2(EXTERNAL_MODULE
))
861 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[EXTERNAL_MODULE
].rfProtocol
, DSM2_PROTO_LP45
, DSM2_PROTO_DSMX
);
862 else if (IS_MODULE_R9M(EXTERNAL_MODULE
))
863 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[EXTERNAL_MODULE
].subType
, MODULE_SUBTYPE_R9M_FCC
, MODULE_SUBTYPE_R9M_LBT
);
864 #if defined(MULTIMODULE)
865 else if (IS_MODULE_MULTIMODULE(EXTERNAL_MODULE
)) {
866 int multiRfProto
= g_model
.moduleData
[EXTERNAL_MODULE
].multi
.customProto
== 1 ? MM_RF_PROTO_CUSTOM
: g_model
.moduleData
[EXTERNAL_MODULE
].getMultiProtocol(false);
867 CHECK_INCDEC_MODELVAR(event
, multiRfProto
, MM_RF_PROTO_FIRST
, MM_RF_PROTO_LAST
);
868 if (checkIncDec_Ret
) {
869 g_model
.moduleData
[EXTERNAL_MODULE
].multi
.customProto
= (multiRfProto
== MM_RF_PROTO_CUSTOM
);
870 if (!g_model
.moduleData
[EXTERNAL_MODULE
].multi
.customProto
)
871 g_model
.moduleData
[EXTERNAL_MODULE
].setMultiProtocol(multiRfProto
);
872 g_model
.moduleData
[EXTERNAL_MODULE
].subType
= 0;
873 // Sensible default for DSM2 (same as for ppm): 7ch@22ms + Autodetect settings enabled
874 if (g_model
.moduleData
[EXTERNAL_MODULE
].getMultiProtocol(true) == MM_RF_PROTO_DSM2
) {
875 g_model
.moduleData
[EXTERNAL_MODULE
].multi
.autoBindMode
= 1;
878 g_model
.moduleData
[EXTERNAL_MODULE
].multi
.autoBindMode
= 0;
880 g_model
.moduleData
[EXTERNAL_MODULE
].multi
.optionValue
= 0;
885 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[EXTERNAL_MODULE
].rfProtocol
, RF_PROTO_X16
, RF_PROTO_LAST
);
887 if (checkIncDec_Ret
) {
888 g_model
.moduleData
[EXTERNAL_MODULE
].channelsStart
= 0;
889 g_model
.moduleData
[EXTERNAL_MODULE
].channelsCount
= DEFAULT_CHANNELS(EXTERNAL_MODULE
);
896 #if defined(MULTIMODULE)
897 case ITEM_MODEL_EXTERNAL_MODULE_SUBTYPE
:
899 lcdDrawTextAlignedLeft(y
, STR_SUBTYPE
);
900 uint8_t multi_rfProto
= g_model
.moduleData
[EXTERNAL_MODULE
].getMultiProtocol(true);
901 const mm_protocol_definition
* pdef
= getMultiProtocolDefinition(multi_rfProto
);
903 if (multi_rfProto
== MM_RF_CUSTOM_SELECTED
) {
904 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN
+ 3 * FW
, y
, g_model
.moduleData
[EXTERNAL_MODULE
].getMultiProtocol(false), RIGHT
| (menuHorizontalPosition
== 0 ? attr
: 0), 2);
905 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN
+ 5 * FW
, y
, g_model
.moduleData
[EXTERNAL_MODULE
].subType
, RIGHT
| (menuHorizontalPosition
== 1 ? attr
: 0), 2);
908 if (pdef
->subTypeString
!= nullptr)
909 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
, y
, pdef
->subTypeString
, g_model
.moduleData
[EXTERNAL_MODULE
].subType
, attr
);
911 if (attr
&& (editMode
> 0 || p1valdiff
)) {
912 switch (menuHorizontalPosition
) {
914 if (multi_rfProto
== MM_RF_CUSTOM_SELECTED
)
915 g_model
.moduleData
[EXTERNAL_MODULE
].setMultiProtocol(checkIncDec(event
, g_model
.moduleData
[EXTERNAL_MODULE
].getMultiProtocol(false), 0, 63, EE_MODEL
));
916 else if (pdef
->maxSubtype
> 0)
917 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[EXTERNAL_MODULE
].subType
, 0, pdef
->maxSubtype
);
920 // Custom protocol, third column is subtype
921 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[EXTERNAL_MODULE
].subType
, 0, 7);
929 #if defined(PCBTARANIS)
930 case ITEM_MODEL_TRAINER_LABEL
:
931 lcdDrawTextAlignedLeft(y
, STR_TRAINER
);
934 case ITEM_MODEL_TRAINER_MODE
:
935 lcdDrawTextAlignedLeft(y
, STR_MODE
);
936 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
, y
, STR_VTRAINERMODES
, g_model
.trainerMode
, attr
);
938 g_model
.trainerMode
= checkIncDec(event
, g_model
.trainerMode
, 0, TRAINER_MODE_MAX(), EE_MODEL
, isTrainerModeAvailable
);
939 #if defined(BLUETOOTH)
940 if (checkIncDec_Ret
) {
941 bluetoothState
= BLUETOOTH_STATE_OFF
;
942 bluetoothDistantAddr
[0] = 0;
949 #if defined(PCBTARANIS) && defined(BLUETOOTH)
950 case ITEM_MODEL_TRAINER_BLUETOOTH
:
951 if (g_model
.trainerMode
== TRAINER_MODE_MASTER_BLUETOOTH
) {
955 if (bluetoothDistantAddr
[0]) {
956 lcdDrawText(INDENT_WIDTH
, y
+1, bluetoothDistantAddr
, TINSIZE
);
957 if (bluetoothState
!= BLUETOOTH_STATE_CONNECTED
) {
958 lcdDrawText(MODEL_SETUP_2ND_COLUMN
, y
, BUTTON("Bind"), menuHorizontalPosition
== 0 ? attr
: 0);
959 lcdDrawText(MODEL_SETUP_2ND_COLUMN
+5*FW
, y
, BUTTON("Clear"), menuHorizontalPosition
== 1 ? attr
: 0);
962 lcdDrawText(MODEL_SETUP_2ND_COLUMN
, y
, BUTTON("Clear"), attr
);
964 if (attr
&& event
== EVT_KEY_FIRST(KEY_ENTER
)) {
965 if (bluetoothState
== BLUETOOTH_STATE_CONNECTED
|| menuHorizontalPosition
== 1) {
966 bluetoothState
= BLUETOOTH_STATE_OFF
;
967 bluetoothDistantAddr
[0] = 0;
970 bluetoothState
= BLUETOOTH_STATE_BIND_REQUESTED
;
975 lcdDrawText(INDENT_WIDTH
, y
, "---");
976 if (bluetoothState
< BLUETOOTH_STATE_IDLE
)
977 lcdDrawText(MODEL_SETUP_2ND_COLUMN
, y
, BUTTON("Init"), attr
);
979 lcdDrawText(MODEL_SETUP_2ND_COLUMN
, y
, BUTTON("Discover"), attr
);
980 if (attr
&& event
== EVT_KEY_FIRST(KEY_ENTER
)) {
981 if (bluetoothState
< BLUETOOTH_STATE_IDLE
)
982 bluetoothState
= BLUETOOTH_STATE_OFF
;
984 bluetoothState
= BLUETOOTH_STATE_DISCOVER_REQUESTED
;
989 if (bluetoothDistantAddr
[0])
990 lcdDrawText(INDENT_WIDTH
, y
+1, bluetoothDistantAddr
, TINSIZE
);
992 lcdDrawText(INDENT_WIDTH
, y
, "---");
993 lcdDrawText(MODEL_SETUP_2ND_COLUMN
, y
, bluetoothState
== BLUETOOTH_STATE_CONNECTED
? "Connected" : "!Connected");
998 #if defined(PCBTARANIS)
999 case ITEM_MODEL_TRAINER_CHANNELS
:
1000 case ITEM_MODEL_INTERNAL_MODULE_CHANNELS
:
1002 #if defined(PCBSKY9X)
1003 case ITEM_MODEL_EXTRA_MODULE_CHANNELS
:
1006 case ITEM_MODEL_EXTERNAL_MODULE_CHANNELS
:
1008 uint8_t moduleIdx
= CURRENT_MODULE_EDITED(k
);
1009 ModuleData
& moduleData
= g_model
.moduleData
[moduleIdx
];
1010 lcdDrawTextAlignedLeft(y
, STR_CHANNELRANGE
);
1011 if ((int8_t)PORT_CHANNELS_ROWS(moduleIdx
) >= 0) {
1012 lcdDrawText(MODEL_SETUP_2ND_COLUMN
, y
, STR_CH
, menuHorizontalPosition
==0 ? attr
: 0);
1013 lcdDrawNumber(lcdLastRightPos
, y
, moduleData
.channelsStart
+1, LEFT
| (menuHorizontalPosition
==0 ? attr
: 0));
1014 lcdDrawChar(lcdLastRightPos
, y
, '-');
1015 lcdDrawNumber(lcdLastRightPos
+ FW
+1, y
, moduleData
.channelsStart
+NUM_CHANNELS(moduleIdx
), LEFT
| (menuHorizontalPosition
==1 ? attr
: 0));
1016 if (attr
&& (editMode
>0 || p1valdiff
)) {
1017 switch (menuHorizontalPosition
) {
1019 CHECK_INCDEC_MODELVAR_ZERO(event
, moduleData
.channelsStart
, 32-8-moduleData
.channelsCount
);
1022 CHECK_INCDEC_MODELVAR(event
, moduleData
.channelsCount
, -4, min
<int8_t>(MAX_CHANNELS(moduleIdx
), 32-8-moduleData
.channelsStart
));
1023 if ((k
== ITEM_MODEL_EXTERNAL_MODULE_CHANNELS
&& g_model
.moduleData
[EXTERNAL_MODULE
].type
== MODULE_TYPE_PPM
)) {
1024 SET_DEFAULT_PPM_FRAME_LENGTH(moduleIdx
);
1035 case ITEM_MODEL_TRAINER_PARAMS
:
1037 #if defined(PCBTARANIS)
1038 case ITEM_MODEL_INTERNAL_MODULE_BIND
:
1040 #if defined(PCBSKY9X)
1041 case ITEM_MODEL_EXTRA_MODULE_BIND
:
1044 case ITEM_MODEL_EXTERNAL_MODULE_BIND
:
1046 uint8_t moduleIdx
= CURRENT_MODULE_EDITED(k
);
1047 ModuleData
& moduleData
= g_model
.moduleData
[moduleIdx
];
1048 if (IS_MODULE_PPM(moduleIdx
)) {
1049 lcdDrawTextAlignedLeft(y
, STR_PPMFRAME
);
1050 lcdDrawText(MODEL_SETUP_2ND_COLUMN
+3*FW
, y
, STR_MS
);
1051 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN
, y
, (int16_t)moduleData
.ppm
.frameLength
*5 + 225, (menuHorizontalPosition
<=0 ? attr
: 0) | PREC1
|LEFT
);
1052 lcdDrawChar(MODEL_SETUP_2ND_COLUMN
+8*FW
+2, y
, 'u');
1053 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN
+8*FW
+2, y
, (moduleData
.ppm
.delay
*50)+300, RIGHT
| ((CURSOR_ON_LINE() || menuHorizontalPosition
==1) ? attr
: 0));
1054 lcdDrawChar(MODEL_SETUP_2ND_COLUMN
+10*FW
, y
, moduleData
.ppm
.pulsePol
? '+' : '-', (CURSOR_ON_LINE() || menuHorizontalPosition
==2) ? attr
: 0);
1055 if (attr
&& (editMode
>0 || p1valdiff
)) {
1056 switch (menuHorizontalPosition
) {
1058 CHECK_INCDEC_MODELVAR(event
, moduleData
.ppm
.frameLength
, -20, 35);
1061 CHECK_INCDEC_MODELVAR(event
, moduleData
.ppm
.delay
, -4, 10);
1064 CHECK_INCDEC_MODELVAR_ZERO(event
, moduleData
.ppm
.pulsePol
, 1);
1069 else if (IS_MODULE_SBUS(moduleIdx
)) {
1070 lcdDrawTextAlignedLeft(y
, STR_REFRESHRATE
);
1071 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN
, y
, (int16_t)moduleData
.ppm
.frameLength
*5 + 225, (menuHorizontalPosition
<=0 ? attr
: 0) | PREC1
|LEFT
);
1072 lcdDrawText(lcdLastRightPos
, y
, STR_MS
);
1073 lcdDrawText(MODEL_SETUP_2ND_COLUMN
+5*FW
+2, y
, moduleData
.sbus
.noninverted
? "no inv" : "normal", (CURSOR_ON_LINE() || menuHorizontalPosition
==1) ? attr
: 0);
1075 if (attr
&& s_editMode
>0) {
1076 switch (menuHorizontalPosition
) {
1078 CHECK_INCDEC_MODELVAR(event
, moduleData
.ppm
.frameLength
, -33, 35);
1081 CHECK_INCDEC_MODELVAR_ZERO(event
, moduleData
.sbus
.noninverted
, 1);
1087 horzpos_t l_posHorz
= menuHorizontalPosition
;
1088 coord_t xOffsetBind
= MODEL_SETUP_BIND_OFS
;
1089 if (IS_MODULE_XJT(moduleIdx
) && IS_D8_RX(moduleIdx
)) {
1091 lcdDrawTextAlignedLeft(y
, STR_RECEIVER
);
1092 if (attr
) l_posHorz
+= 1;
1095 lcdDrawTextAlignedLeft(y
, STR_RECEIVER_NUM
);
1097 if (IS_MODULE_PXX(moduleIdx
) || IS_MODULE_DSM2(moduleIdx
) || IS_MODULE_MULTIMODULE(moduleIdx
)) {
1099 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN
, y
, g_model
.header
.modelId
[moduleIdx
], (l_posHorz
==0 ? attr
: 0) | LEADING0
|LEFT
, 2);
1100 if (attr
&& l_posHorz
== 0) {
1101 if (editMode
>0 || p1valdiff
) {
1102 CHECK_INCDEC_MODELVAR_ZERO(event
, g_model
.header
.modelId
[moduleIdx
], MAX_RX_NUM(moduleIdx
));
1103 if (checkIncDec_Ret
) {
1104 modelHeaders
[g_eeGeneral
.currModel
].modelId
[moduleIdx
] = g_model
.header
.modelId
[moduleIdx
];
1107 if (editMode
==0 && event
==EVT_KEY_BREAK(KEY_ENTER
)) {
1108 checkModelIdUnique(g_eeGeneral
.currModel
, moduleIdx
);
1111 lcdDrawText(MODEL_SETUP_2ND_COLUMN
+xOffsetBind
, y
, STR_MODULE_BIND
, l_posHorz
==1 ? attr
: 0);
1112 lcdDrawText(MODEL_SETUP_2ND_COLUMN
+MODEL_SETUP_RANGE_OFS
+xOffsetBind
, y
, STR_MODULE_RANGE
, l_posHorz
==2 ? attr
: 0);
1113 uint8_t newFlag
= 0;
1114 #if defined(MULTIMODULE)
1115 if (multiBindStatus
== MULTI_BIND_FINISHED
) {
1116 multiBindStatus
= MULTI_NORMAL_OPERATION
;
1120 #if defined(PCBTARANIS)
1121 if (attr
&& l_posHorz
> 0) {
1122 if (s_editMode
> 0) {
1123 if (l_posHorz
== 1) {
1124 if (IS_MODULE_R9M(moduleIdx
) || (IS_MODULE_XJT(moduleIdx
) && g_model
.moduleData
[moduleIdx
].rfProtocol
== RF_PROTO_X16
)) {
1125 #if defined(PCBXLITE)
1126 if (EVT_KEY_MASK(event
) == KEY_ENTER
) {
1128 if (event
== EVT_KEY_BREAK(KEY_ENTER
)) {
1131 uint8_t default_selection
= 0; // R9M_LBT should default to 0 as available options are variables
1132 if (IS_MODULE_R9M_LBT(moduleIdx
)) {
1133 if (BIND_TELEM_ALLOWED(moduleIdx
))
1134 POPUP_MENU_ADD_ITEM(STR_BINDING_1_8_TELEM_ON
);
1135 POPUP_MENU_ADD_ITEM(STR_BINDING_1_8_TELEM_OFF
);
1136 if (BIND_TELEM_ALLOWED(moduleIdx
) && BIND_CH9TO16_ALLOWED(moduleIdx
))
1137 POPUP_MENU_ADD_ITEM(STR_BINDING_9_16_TELEM_ON
);
1138 if (BIND_CH9TO16_ALLOWED(moduleIdx
))
1139 POPUP_MENU_ADD_ITEM(STR_BINDING_9_16_TELEM_OFF
);
1142 if (BIND_TELEM_ALLOWED(moduleIdx
))
1143 POPUP_MENU_ADD_ITEM(STR_BINDING_1_8_TELEM_ON
);
1144 POPUP_MENU_ADD_ITEM(STR_BINDING_1_8_TELEM_OFF
);
1145 if (BIND_TELEM_ALLOWED(moduleIdx
))
1146 POPUP_MENU_ADD_ITEM(STR_BINDING_9_16_TELEM_ON
);
1147 POPUP_MENU_ADD_ITEM(STR_BINDING_9_16_TELEM_OFF
);
1148 default_selection
= g_model
.moduleData
[INTERNAL_MODULE
].pxx
.receiver_telem_off
+ (g_model
.moduleData
[INTERNAL_MODULE
].pxx
.receiver_channel_9_16
<< 1);
1150 POPUP_MENU_SELECT_ITEM(default_selection
);
1151 POPUP_MENU_START(onBindMenu
);
1154 if (moduleFlag
[moduleIdx
] == MODULE_BIND
) {
1155 newFlag
= MODULE_BIND
;
1158 if (!popupMenuNoItems
) {
1159 s_editMode
= 0; // this is when popup is exited before a choice is made
1164 newFlag
= MODULE_BIND
;
1167 else if (l_posHorz
== 2) {
1168 newFlag
= MODULE_RANGECHECK
;
1173 if (attr
&& l_posHorz
>0 && s_editMode
>0) {
1175 newFlag
= MODULE_BIND
;
1176 else if (l_posHorz
== 2)
1177 newFlag
= MODULE_RANGECHECK
;
1180 moduleFlag
[moduleIdx
] = newFlag
;
1182 #if defined(MULTIMODULE)
1183 if (newFlag
== MODULE_BIND
) {
1184 multiBindStatus
= MULTI_BIND_INITIATED
;
1194 #if defined(PCBSKY9X) && defined(REVX)
1195 case ITEM_MODEL_EXTERNAL_MODULE_OUTPUT_TYPE
:
1197 uint8_t moduleIdx
= CURRENT_MODULE_EDITED(k
);
1198 ModuleData
& moduleData
= g_model
.moduleData
[moduleIdx
];
1199 moduleData
.ppm
.outputType
= editChoice(MODEL_SETUP_2ND_COLUMN
, y
, STR_OUTPUT_TYPE
, STR_VOUTPUT_TYPE
, moduleData
.ppm
.outputType
, 0, 1, attr
, event
);
1204 #if defined(PCBTARANIS)
1205 case ITEM_MODEL_INTERNAL_MODULE_FAILSAFE
:
1208 case ITEM_MODEL_EXTERNAL_MODULE_FAILSAFE
: {
1209 uint8_t moduleIdx
= CURRENT_MODULE_EDITED(k
);
1210 ModuleData
&moduleData
= g_model
.moduleData
[moduleIdx
];
1211 lcdDrawTextAlignedLeft(y
, STR_FAILSAFE
);
1212 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
, y
, STR_VFAILSAFE
, moduleData
.failsafeMode
, menuHorizontalPosition
== 0 ? attr
: 0);
1213 if (moduleData
.failsafeMode
== FAILSAFE_CUSTOM
)
1214 lcdDrawText(MODEL_SETUP_2ND_COLUMN
+ MODEL_SETUP_SET_FAILSAFE_OFS
, y
, STR_SET
, menuHorizontalPosition
== 1 ? attr
: 0);
1216 if (moduleData
.failsafeMode
!= FAILSAFE_CUSTOM
)
1217 menuHorizontalPosition
= 0;
1218 if (menuHorizontalPosition
== 0) {
1219 if (editMode
> 0 || p1valdiff
) {
1220 CHECK_INCDEC_MODELVAR_ZERO(event
, moduleData
.failsafeMode
, FAILSAFE_LAST
);
1221 if (checkIncDec_Ret
) SEND_FAILSAFE_NOW(moduleIdx
);
1223 } else if (menuHorizontalPosition
== 1) {
1225 if (moduleData
.failsafeMode
== FAILSAFE_CUSTOM
) {
1226 if (event
== EVT_KEY_LONG(KEY_ENTER
)) {
1228 setCustomFailsafe(moduleIdx
);
1229 storageDirty(EE_MODEL
);
1231 SEND_FAILSAFE_NOW(moduleIdx
);
1233 else if (event
== EVT_KEY_BREAK(KEY_ENTER
)) {
1234 g_moduleIdx
= moduleIdx
;
1235 pushMenu(menuModelFailsafe
);
1239 lcdDrawSolidFilledRect(MODEL_SETUP_2ND_COLUMN
, y
, LCD_W
- MODEL_SETUP_2ND_COLUMN
, 8);
1245 #if defined(PCBXLITE)
1246 case ITEM_MODEL_INTERNAL_MODULE_ANTENNA
:
1248 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
);
1249 if (newAntennaSel
!= g_model
.moduleData
[INTERNAL_MODULE
].pxx
.external_antenna
&& newAntennaSel
== XJT_EXTERNAL_ANTENNA
) {
1250 POPUP_CONFIRMATION(STR_ANTENNACONFIRM1
);
1251 const char * w
= STR_ANTENNACONFIRM2
;
1252 SET_WARNING_INFO(w
, strlen(w
), 0);
1255 g_model
.moduleData
[INTERNAL_MODULE
].pxx
.external_antenna
= newAntennaSel
;
1260 case ITEM_MODEL_EXTERNAL_MODULE_OPTIONS
:
1262 uint8_t moduleIdx
= CURRENT_MODULE_EDITED(k
);
1263 #if defined(MULTIMODULE)
1264 if (IS_MODULE_MULTIMODULE(moduleIdx
)) {
1265 int optionValue
= g_model
.moduleData
[moduleIdx
].multi
.optionValue
;
1267 const uint8_t multi_proto
= g_model
.moduleData
[EXTERNAL_MODULE
].getMultiProtocol(true);
1268 const mm_protocol_definition
* pdef
= getMultiProtocolDefinition(multi_proto
);
1269 if (pdef
->optionsstr
)
1270 lcdDrawTextAlignedLeft(y
, pdef
->optionsstr
);
1272 if (multi_proto
== MM_RF_PROTO_FS_AFHDS2A
)
1273 optionValue
= 50 + 5 * optionValue
;
1275 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN
, y
, optionValue
, LEFT
| attr
);
1277 if (multi_proto
== MM_RF_PROTO_FS_AFHDS2A
) {
1278 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[moduleIdx
].multi
.optionValue
, 0, 70);
1280 else if (multi_proto
== MM_RF_PROTO_OLRS
) {
1281 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[moduleIdx
].multi
.optionValue
, -1, 7);
1284 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[moduleIdx
].multi
.optionValue
, -128, 127);
1289 if (IS_MODULE_R9M(moduleIdx
)) {
1290 lcdDrawTextAlignedLeft(y
, STR_MODULE_TELEMETRY
);
1291 if (IS_TELEMETRY_INTERNAL_MODULE()) {
1292 lcdDrawText(MODEL_SETUP_2ND_COLUMN
, y
, STR_DISABLE_INTERNAL
);
1295 lcdDrawText(MODEL_SETUP_2ND_COLUMN
, y
, STR_MODULE_TELEM_ON
);
1298 else if (IS_MODULE_SBUS(moduleIdx
)) {
1299 lcdDrawTextAlignedLeft(y
, STR_WARN_BATTVOLTAGE
);
1300 putsVolts(lcdLastRightPos
, y
, getBatteryVoltage(), attr
| PREC2
| LEFT
);
1305 case ITEM_MODEL_EXTERNAL_MODULE_POWER
:
1307 uint8_t moduleIdx
= CURRENT_MODULE_EDITED(k
);
1308 if (IS_MODULE_R9M(moduleIdx
)) {
1309 lcdDrawTextAlignedLeft(y
, TR_MULTI_RFPOWER
);
1310 if(IS_MODULE_R9M_FCC(moduleIdx
)) {
1311 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
1312 #if defined(MODULE_R9M_FULLSIZE)
1313 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
, y
, STR_R9M_FCC_POWER_VALUES
, g_model
.moduleData
[moduleIdx
].pxx
.power
, LEFT
| attr
);
1315 CHECK_INCDEC_MODELVAR_ZERO(event
, g_model
.moduleData
[moduleIdx
].pxx
.power
, R9M_FCC_POWER_MAX
);
1317 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
, y
, STR_R9M_FCC_POWER_VALUES
, g_model
.moduleData
[moduleIdx
].pxx
.power
, LEFT
);
1319 REPEAT_LAST_CURSOR_MOVE();
1323 g_model
.moduleData
[moduleIdx
].pxx
.power
= min((uint8_t)g_model
.moduleData
[moduleIdx
].pxx
.power
, (uint8_t)R9M_LBT_POWER_MAX
);
1324 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
, y
, STR_R9M_LBT_POWER_VALUES
, selectedPxxPower
, LEFT
| attr
);
1326 CHECK_INCDEC_MODELVAR_ZERO(event
, selectedPxxPower
, R9M_LBT_POWER_MAX
);
1328 if (attr
&& editMode
== 0 && selectedPxxPower
!= g_model
.moduleData
[moduleIdx
].pxx
.power
) {
1329 if((selectedPxxPower
+ g_model
.moduleData
[moduleIdx
].pxx
.power
) < 5) //switching between mode 2 and 3 does not require rebind
1330 POPUP_WARNING(STR_REBIND
);
1331 g_model
.moduleData
[moduleIdx
].pxx
.power
= selectedPxxPower
;
1335 #if defined(MULTIMODULE)
1336 else if (IS_MODULE_MULTIMODULE(moduleIdx
)) {
1337 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
);
1344 #if defined(MULTIMODULE)
1345 case ITEM_MODEL_EXTERNAL_MODULE_AUTOBIND
:
1346 if (g_model
.moduleData
[EXTERNAL_MODULE
].getMultiProtocol(true) == MM_RF_PROTO_DSM2
)
1347 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
);
1349 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
);
1352 case ITEM_MODEL_EXTERNAL_MODULE_STATUS
: {
1353 lcdDrawTextAlignedLeft(y
, STR_MODULE_STATUS
);
1355 char statusText
[64];
1356 multiModuleStatus
.getStatusString(statusText
);
1357 lcdDrawText(MODEL_SETUP_2ND_COLUMN
, y
, statusText
);
1361 case ITEM_MODEL_EXTERNAL_MODULE_SYNCSTATUS
: {
1362 lcdDrawTextAlignedLeft(y
, STR_MODULE_SYNC
);
1364 char statusText
[64];
1365 multiSyncStatus
.getRefreshString(statusText
);
1366 lcdDrawText(MODEL_SETUP_2ND_COLUMN
, y
, statusText
);
1372 #if !defined(CPUARM)
1373 case ITEM_MODEL_PPM1_PROTOCOL
:
1374 lcdDrawTextAlignedLeft(y
, NO_INDENT(STR_PROTO
));
1375 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
, y
, STR_VPROTOS
, protocol
, menuHorizontalPosition
<=0 ? attr
: 0);
1376 if (IS_PPM_PROTOCOL(protocol
)) {
1377 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
+7*FW
, y
, STR_NCHANNELS
, g_model
.ppmNCH
+2, menuHorizontalPosition
!=0 ? attr
: 0);
1379 else if (menuHorizontalPosition
>0 && attr
) {
1380 MOVE_CURSOR_FROM_HERE();
1382 if (attr
&& (editMode
>0 || p1valdiff
|| (!IS_PPM_PROTOCOL(protocol
) && !IS_DSM2_PROTOCOL(protocol
)))) {
1383 switch (menuHorizontalPosition
) {
1385 CHECK_INCDEC_MODELVAR_ZERO(event
, g_model
.protocol
, PROTO_MAX
-1);
1388 CHECK_INCDEC_MODELVAR(event
, g_model
.ppmNCH
, -2, 4);
1389 g_model
.ppmFrameLength
= g_model
.ppmNCH
* 8;
1397 case ITEM_MODEL_PPM2_PROTOCOL
:
1398 lcdDrawTextAlignedLeft(y
, PSTR("Port2"));
1399 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
, y
, STR_VPROTOS
, 0, 0);
1400 lcdDrawText(MODEL_SETUP_2ND_COLUMN
+4*FW
+3, y
, STR_CH
, menuHorizontalPosition
<=0 ? attr
: 0);
1401 lcdDrawNumber(lcdLastRightPos
, y
, g_model
.moduleData
[1].channelsStart
+1, LEFT
| (menuHorizontalPosition
<=0 ? attr
: 0));
1402 lcdDrawChar(lcdLastRightPos
, y
, '-');
1403 lcdDrawNumber(lcdLastRightPos
+ FW
+1, y
, g_model
.moduleData
[1].channelsStart
+8+g_model
.moduleData
[1].channelsCount
, LEFT
| (menuHorizontalPosition
!=0 ? attr
: 0));
1404 if (attr
&& (editMode
>0 || p1valdiff
)) {
1405 switch (menuHorizontalPosition
) {
1407 CHECK_INCDEC_MODELVAR_ZERO(event
, g_model
.moduleData
[1].channelsStart
, 32-8-g_model
.moduleData
[1].channelsCount
);
1408 SET_DEFAULT_PPM_FRAME_LENGTH(1);
1411 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[1].channelsCount
, -4, min
<int8_t>(8, 32-8-g_model
.moduleData
[1].channelsStart
));
1412 SET_DEFAULT_PPM_FRAME_LENGTH(1);
1418 case ITEM_MODEL_PPM2_PARAMS
:
1419 lcdDrawTextAlignedLeft(y
, STR_PPMFRAME
);
1420 lcdDrawText(MODEL_SETUP_2ND_COLUMN
+3*FW
, y
, STR_MS
);
1421 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN
, y
, (int16_t)g_model
.moduleData
[1].ppmFrameLength
*5 + 225, (menuHorizontalPosition
<=0 ? attr
: 0) | PREC1
| LEFT
);
1422 lcdDrawChar(MODEL_SETUP_2ND_COLUMN
+8*FW
+2, y
, 'u');
1423 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN
+8*FW
+2, y
, (g_model
.moduleData
[1].ppmDelay
*50)+300, RIGHT
| ((menuHorizontalPosition
< 0 || menuHorizontalPosition
==1) ? attr
: 0));
1424 lcdDrawChar(MODEL_SETUP_2ND_COLUMN
+10*FW
, y
, g_model
.moduleData
[1].ppmPulsePol
? '+' : '-', (menuHorizontalPosition
< 0 || menuHorizontalPosition
==2) ? attr
: 0);
1425 if (attr
&& (editMode
>0 || p1valdiff
)) {
1426 switch (menuHorizontalPosition
) {
1428 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[1].ppmFrameLength
, -20, 35);
1431 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[1].ppmDelay
, -4, 10);
1434 CHECK_INCDEC_MODELVAR_ZERO(event
, g_model
.moduleData
[1].ppmPulsePol
, 1);
1441 #if !defined(CPUARM)
1442 case ITEM_MODEL_PPM1_PARAMS
:
1443 if (IS_PPM_PROTOCOL(protocol
)) {
1444 lcdDrawTextAlignedLeft(y
, STR_PPMFRAME
);
1445 lcdDrawText(MODEL_SETUP_2ND_COLUMN
+3*FW
, y
, STR_MS
);
1446 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN
, y
, (int16_t)g_model
.ppmFrameLength
*5 + 225, (menuHorizontalPosition
<=0 ? attr
: 0) | PREC1
|LEFT
);
1447 lcdDrawChar(MODEL_SETUP_2ND_COLUMN
+8*FW
+2, y
, 'u');
1448 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN
+8*FW
+2, y
, (g_model
.ppmDelay
*50)+300, (CURSOR_ON_LINE() || menuHorizontalPosition
==1) ? attr
: 0);
1449 lcdDrawChar(MODEL_SETUP_2ND_COLUMN
+10*FW
, y
, g_model
.pulsePol
? '+' : '-', (CURSOR_ON_LINE() || menuHorizontalPosition
==2) ? attr
: 0);
1450 if (attr
&& (editMode
>0 || p1valdiff
)) {
1451 switch (menuHorizontalPosition
) {
1453 CHECK_INCDEC_MODELVAR(event
, g_model
.ppmFrameLength
, -20, 35);
1456 CHECK_INCDEC_MODELVAR(event
, g_model
.ppmDelay
, -4, 10);
1459 CHECK_INCDEC_MODELVAR_ZERO(event
, g_model
.pulsePol
, 1);
1464 #if defined(DSM2) || defined(PXX)
1465 else if (IS_DSM2_PROTOCOL(protocol
) || IS_PXX_PROTOCOL(protocol
)) {
1466 if (attr
&& menuHorizontalPosition
> 1) {
1467 REPEAT_LAST_CURSOR_MOVE(); // limit 3 column row to 2 colums (Rx_Num and RANGE fields)
1469 lcdDrawTextAlignedLeft(y
, STR_RECEIVER_NUM
);
1470 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN
, y
, g_model
.header
.modelId
[0], (menuHorizontalPosition
<=0 ? attr
: 0) | LEADING0
|LEFT
, 2);
1471 if (attr
&& (menuHorizontalPosition
==0 && (editMode
>0 || p1valdiff
))) {
1472 CHECK_INCDEC_MODELVAR_ZERO(event
, g_model
.header
.modelId
[0], 99);
1475 if (protocol
== PROTO_PXX
) {
1476 lcdDrawText(MODEL_SETUP_2ND_COLUMN
+4*FW
, y
, STR_SYNCMENU
, menuHorizontalPosition
!=0 ? attr
: 0);
1477 uint8_t newFlag
= 0;
1478 if (attr
&& menuHorizontalPosition
>0 && editMode
>0) {
1480 newFlag
= MODULE_BIND
;
1482 moduleFlag
[0] = newFlag
;
1486 if (IS_DSM2_PROTOCOL(protocol
)) {
1487 lcdDrawText(MODEL_SETUP_2ND_COLUMN
+4*FW
, y
, STR_MODULE_RANGE
, menuHorizontalPosition
!=0 ? attr
: 0);
1488 moduleFlag
[0] = (attr
&& menuHorizontalPosition
>0 && editMode
>0) ? MODULE_RANGECHECK
: 0; // [MENU] key toggles range check mode
1498 #if defined(CPUARM) && defined(PXX)
1499 if (IS_RANGECHECK_ENABLE()) {
1500 showMessageBox("RSSI: ");
1501 lcdDrawNumber(16+4*FW
, 5*FH
, TELEMETRY_RSSI(), BOLD
);
1507 void menuModelFailsafe(event_t event
)
1509 const uint8_t channelStart
= g_model
.moduleData
[g_moduleIdx
].channelsStart
;
1510 const int lim
= (g_model
.extendedLimits
? (512 * LIMIT_EXT_PERCENT
/ 100) : 512) * 2;
1511 uint8_t wbar
= LCD_W
- FW
* 4 - FWNUM
* 4;
1512 #if defined(PPM_UNIT_PERCENT_PREC1)
1516 if (event
== EVT_KEY_LONG(KEY_ENTER
)) {
1520 if (menuVerticalPosition
< NUM_CHANNELS(g_moduleIdx
)) {
1522 g_model
.moduleData
[g_moduleIdx
].failsafeChannels
[menuVerticalPosition
] = channelOutputs
[menuVerticalPosition
+channelStart
];
1526 int16_t & failsafe
= g_model
.moduleData
[g_moduleIdx
].failsafeChannels
[menuVerticalPosition
];
1527 if (failsafe
< FAILSAFE_CHANNEL_HOLD
)
1528 failsafe
= FAILSAFE_CHANNEL_HOLD
;
1529 else if (failsafe
== FAILSAFE_CHANNEL_HOLD
)
1530 failsafe
= FAILSAFE_CHANNEL_NOPULSE
;
1536 // "Outputs => Failsafe" menu item
1537 setCustomFailsafe(g_moduleIdx
);
1540 storageDirty(EE_MODEL
);
1542 SEND_FAILSAFE_NOW(g_moduleIdx
);
1545 SIMPLE_SUBMENU_NOTITLE(NUM_CHANNELS(g_moduleIdx
) + 1);
1547 lcdDrawTextAlignedCenter(0, FAILSAFESET
);
1550 const coord_t x
= 1;
1552 uint8_t line
= (menuVerticalPosition
>= NUM_CHANNELS(g_moduleIdx
) ? 2 : 0);
1553 uint8_t ch
= (menuVerticalPosition
>= 8 ? 8 : 0) + line
;
1556 for (; line
< 8; line
++) {
1557 const int32_t channelValue
= channelOutputs
[ch
+channelStart
];
1558 int32_t failsafeValue
= g_model
.moduleData
[g_moduleIdx
].failsafeChannels
[ch
];
1561 putsChn(x
+1, y
, ch
+1, SMLSIZE
);
1564 LcdFlags flags
= TINSIZE
;
1565 if (menuVerticalPosition
== ch
) {
1568 if (failsafeValue
== FAILSAFE_CHANNEL_HOLD
|| failsafeValue
== FAILSAFE_CHANNEL_NOPULSE
) {
1573 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[g_moduleIdx
].failsafeChannels
[ch
], -lim
, +lim
);
1578 uint8_t xValue
= x
+LCD_W
-4-wbar
;
1579 if (failsafeValue
== FAILSAFE_CHANNEL_HOLD
) {
1580 lcdDrawText(xValue
, y
, STR_HOLD
, RIGHT
|flags
);
1583 else if (failsafeValue
== FAILSAFE_CHANNEL_NOPULSE
) {
1584 lcdDrawText(xValue
, y
, STR_NONE
, RIGHT
|flags
);
1588 #if defined(PPM_UNIT_US)
1589 lcdDrawNumber(xValue
, y
, PPM_CH_CENTER(ch
)+failsafeValue
/2, RIGHT
|flags
);
1590 #elif defined(PPM_UNIT_PERCENT_PREC1)
1591 lcdDrawNumber(xValue
, y
, calcRESXto1000(failsafeValue
), RIGHT
|PREC1
|flags
);
1593 lcdDrawNumber(xValue
, y
, calcRESXto1000(failsafeValue
)/10, RIGHT
|flags
);
1598 #if !defined(PCBX7) // X7 LCD doesn't like too many horizontal lines
1599 lcdDrawRect(x
+LCD_W
-3-wbar
, y
, wbar
+1, 6);
1601 const uint8_t lenChannel
= limit
<uint8_t>(1, (abs(channelValue
) * wbar
/2 + lim
/2) / lim
, wbar
/2);
1602 const uint8_t lenFailsafe
= limit
<uint8_t>(1, (abs(failsafeValue
) * wbar
/2 + lim
/2) / lim
, wbar
/2);
1603 const coord_t xChannel
= (channelValue
>0) ? x
+LCD_W
-3-wbar
/2 : x
+LCD_W
-2-wbar
/2-lenChannel
;
1604 const coord_t xFailsafe
= (failsafeValue
>0) ? x
+LCD_W
-3-wbar
/2 : x
+LCD_W
-2-wbar
/2-lenFailsafe
;
1605 lcdDrawHorizontalLine(xChannel
, y
+1, lenChannel
, DOTTED
, 0);
1606 lcdDrawHorizontalLine(xChannel
, y
+2, lenChannel
, DOTTED
, 0);
1607 lcdDrawSolidHorizontalLine(xFailsafe
, y
+3, lenFailsafe
);
1608 lcdDrawSolidHorizontalLine(xFailsafe
, y
+4, lenFailsafe
);
1612 if (++ch
>= NUM_CHANNELS(g_moduleIdx
))
1616 if (menuVerticalPosition
>= NUM_CHANNELS(g_moduleIdx
)) {
1617 // Outputs => Failsafe
1618 lcdDrawText(CENTER_OFS
, LCD_H
- (FH
+ 1), STR_OUTPUTS2FAILSAFE
, INVERS
);