Better support for R9M EU (#5487)
[opentx.git] / radio / src / gui / 128x64 / model_setup.cpp
blobb3cdb62650b80ace8b977e6b7c3b1132f2d45f51
1 /*
2 * Copyright (C) OpenTX
4 * Based on code named
5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
21 #include "opentx.h"
23 #if defined(CPUARM)
24 uint8_t g_moduleIdx;
25 void menuModelFailsafe(event_t event);
26 #endif
28 enum MenuModelSetupItems {
29 ITEM_MODEL_NAME,
30 ITEM_MODEL_TIMER1,
31 CASE_CPUARM(ITEM_MODEL_TIMER1_NAME)
32 CASE_PERSISTENT_TIMERS(ITEM_MODEL_TIMER1_PERSISTENT)
33 ITEM_MODEL_TIMER1_MINUTE_BEEP,
34 ITEM_MODEL_TIMER1_COUNTDOWN_BEEP,
35 ITEM_MODEL_TIMER2,
36 CASE_CPUARM(ITEM_MODEL_TIMER2_NAME)
37 CASE_PERSISTENT_TIMERS(ITEM_MODEL_TIMER2_PERSISTENT)
38 ITEM_MODEL_TIMER2_MINUTE_BEEP,
39 ITEM_MODEL_TIMER2_COUNTDOWN_BEEP,
40 CASE_CPUARM(ITEM_MODEL_TIMER3)
41 CASE_CPUARM(ITEM_MODEL_TIMER3_NAME)
42 CASE_CPUARM(ITEM_MODEL_TIMER3_PERSISTENT)
43 CASE_CPUARM(ITEM_MODEL_TIMER3_MINUTE_BEEP)
44 CASE_CPUARM(ITEM_MODEL_TIMER3_COUNTDOWN_BEEP)
45 ITEM_MODEL_EXTENDED_LIMITS,
46 ITEM_MODEL_EXTENDED_TRIMS,
47 CASE_CPUARM(ITEM_MODEL_DISPLAY_TRIMS)
48 ITEM_MODEL_TRIM_INC,
49 ITEM_MODEL_THROTTLE_REVERSED,
50 ITEM_MODEL_THROTTLE_TRACE,
51 ITEM_MODEL_THROTTLE_TRIM,
52 CASE_CPUARM(ITEM_MODEL_PREFLIGHT_LABEL)
53 CASE_CPUARM(ITEM_MODEL_CHECKLIST_DISPLAY)
54 ITEM_MODEL_THROTTLE_WARNING,
55 ITEM_MODEL_SWITCHES_WARNING,
56 #if defined(PCBX7)
57 ITEM_MODEL_POTS_WARNING,
58 #endif
59 ITEM_MODEL_BEEP_CENTER,
60 CASE_CPUARM(ITEM_MODEL_USE_GLOBAL_FUNCTIONS)
61 #if defined(PCBX7)
62 ITEM_MODEL_INTERNAL_MODULE_LABEL,
63 ITEM_MODEL_INTERNAL_MODULE_MODE,
64 ITEM_MODEL_INTERNAL_MODULE_CHANNELS,
65 ITEM_MODEL_INTERNAL_MODULE_BIND,
66 ITEM_MODEL_INTERNAL_MODULE_FAILSAFE,
67 #endif
68 #if defined(CPUARM)
69 ITEM_MODEL_EXTERNAL_MODULE_LABEL,
70 ITEM_MODEL_EXTERNAL_MODULE_MODE,
71 #if defined(MULTIMODULE)
72 ITEM_MODEL_EXTERNAL_MODULE_SUBTYPE,
73 ITEM_MODEL_EXTERNAL_MODULE_STATUS,
74 ITEM_MODEL_EXTERNAL_MODULE_SYNCSTATUS,
75 #endif
76 ITEM_MODEL_EXTERNAL_MODULE_CHANNELS,
77 ITEM_MODEL_EXTERNAL_MODULE_BIND,
78 #if defined(PCBSKY9X) && defined(REVX)
79 ITEM_MODEL_EXTERNAL_MODULE_OUTPUT_TYPE,
80 #endif
81 ITEM_MODEL_EXTERNAL_MODULE_FAILSAFE,
82 ITEM_MODEL_EXTERNAL_MODULE_OPTIONS,
83 #if defined(MULTIMODULE)
84 ITEM_MODEL_EXTERNAL_MODULE_AUTOBIND,
85 #endif
86 ITEM_MODEL_EXTERNAL_MODULE_POWER,
87 #if defined(PCBSKY9X) && !defined(REVA)
88 ITEM_MODEL_EXTRA_MODULE_LABEL,
89 ITEM_MODEL_EXTRA_MODULE_CHANNELS,
90 ITEM_MODEL_EXTRA_MODULE_BIND,
91 #endif
92 #else
93 ITEM_MODEL_PPM1_PROTOCOL,
94 ITEM_MODEL_PPM1_PARAMS,
95 #endif
96 #if defined(PCBX7)
97 ITEM_MODEL_TRAINER_LABEL,
98 ITEM_MODEL_TRAINER_MODE,
99 #if defined(BLUETOOTH)
100 ITEM_MODEL_TRAINER_BLUETOOTH,
101 #endif
102 ITEM_MODEL_TRAINER_CHANNELS,
103 ITEM_MODEL_TRAINER_PARAMS,
104 #endif
105 ITEM_MODEL_SETUP_MAX
108 #if defined(PCBSKY9X)
109 #define FIELD_PROTOCOL_MAX 2
110 #else
111 #define FIELD_PROTOCOL_MAX 1
112 #endif
114 #define MODEL_SETUP_2ND_COLUMN (LCD_W-11*FW)
115 #define MODEL_SETUP_BIND_OFS 2*FW+1
116 #define MODEL_SETUP_RANGE_OFS 4*FW+3
117 #define MODEL_SETUP_SET_FAILSAFE_OFS 7*FW-2
119 #if defined(PCBX7)
120 #define CURRENT_MODULE_EDITED(k) (k>=ITEM_MODEL_TRAINER_LABEL ? TRAINER_MODULE : (k>=ITEM_MODEL_EXTERNAL_MODULE_LABEL ? EXTERNAL_MODULE : INTERNAL_MODULE))
121 #elif defined(PCBSKY9X) && !defined(REVA)
122 #define CURRENT_MODULE_EDITED(k) (k>=ITEM_MODEL_EXTRA_MODULE_LABEL ? EXTRA_MODULE : EXTERNAL_MODULE)
123 #else
124 #define CURRENT_MODULE_EDITED(k) (EXTERNAL_MODULE)
125 #endif
127 #if defined(CPUARM)
129 #if !defined(TARANIS_INTERNAL_PPM)
130 #define INTERNAL_MODULE_MODE_ROWS 0 // (OFF / RF protocols)
131 #else
132 #define INTERNAL_MODULE_MODE_ROWS (IS_MODULE_XJT(INTERNAL_MODULE) ? (uint8_t)1 : (uint8_t)0) // Module type + RF protocols
133 #endif
134 #define IF_INTERNAL_MODULE_ON(x) (IS_INTERNAL_MODULE_ENABLED()? (uint8_t)(x) : HIDDEN_ROW )
135 #define IF_EXTERNAL_MODULE_ON(x) (IS_EXTERNAL_MODULE_ENABLED()? (uint8_t)(x) : HIDDEN_ROW)
136 #define INTERNAL_MODULE_CHANNELS_ROWS IF_INTERNAL_MODULE_ON(1)
137 #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
139 #if defined(PCBSKY9X) && defined(REVX)
140 #define OUTPUT_TYPE_ROWS() (IS_MODULE_PPM(EXTERNAL_MODULE) ? (uint8_t)0 : HIDDEN_ROW) ,
141 #else
142 #define OUTPUT_TYPE_ROWS()
143 #endif
144 #define PORT_CHANNELS_ROWS(x) (x==EXTERNAL_MODULE ? EXTERNAL_MODULE_CHANNELS_ROWS : 0)
146 #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
148 #define CURSOR_ON_CELL (true)
149 #define MODEL_SETUP_MAX_LINES (HEADER_LINE+ITEM_MODEL_SETUP_MAX)
150 #define POT_WARN_ITEMS() ((g_model.nPotsToWarn >> 6) ? (uint8_t)NUM_POTS+NUM_SLIDERS : (uint8_t)0)
151 #define TIMER_ROWS 2, 0, CASE_PERSISTENT_TIMERS(0) 0, 0
152 #if defined(PCBSKY9X) && !defined(REVA)
153 #define EXTRA_MODULE_ROWS LABEL(ExtraModule), 1, 2,
154 #else
155 #define EXTRA_MODULE_ROWS
156 #endif
158 #if defined(PCBX7)
159 #if defined(BLUETOOTH)
160 #define TRAINER_BLUETOOTH_M_ROW ((bluetoothDistantAddr[0] == '\0' || bluetoothState == BLUETOOTH_STATE_CONNECTED) ? (uint8_t)0 : (uint8_t)1)
161 #define TRAINER_BLUETOOTH_S_ROW (bluetoothDistantAddr[0] == '\0' ? HIDDEN_ROW : LABEL())
162 #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)),
163 #else
164 #define TRAINER_BLUETOOTH_ROW
165 #endif
166 #define TRAINER_CHANNELS_ROW (IS_SLAVE_TRAINER() ? (uint8_t)1 : HIDDEN_ROW)
167 #define TRAINER_PARAMS_ROW (IS_SLAVE_TRAINER() ? (uint8_t)2 : HIDDEN_ROW)
168 #define TRAINER_ROWS LABEL(Trainer), 0, TRAINER_BLUETOOTH_ROW TRAINER_CHANNELS_ROW, TRAINER_PARAMS_ROW
169 #else
170 #define TRAINER_ROWS
171 #endif
173 #elif defined(CPUM64)
174 #define CURSOR_ON_CELL (true)
175 #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)
176 #else
177 #define CURSOR_ON_CELL (true)
178 #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)
179 #endif
181 #if defined(PCBTARANIS)
182 void onBindMenu(const char * result)
184 uint8_t moduleIdx = CURRENT_MODULE_EDITED(menuVerticalPosition);
186 if (result == STR_BINDING_25MW_CH1_8_TELEM_OFF) {
187 g_model.moduleData[moduleIdx].pxx.power = R9M_LBT_POWER_25;
188 g_model.moduleData[moduleIdx].pxx.receiver_telem_off = true;
189 g_model.moduleData[moduleIdx].pxx.receiver_channel_9_16 = false;
191 else if (result == STR_BINDING_25MW_CH1_8_TELEM_ON) {
192 g_model.moduleData[moduleIdx].pxx.power = R9M_LBT_POWER_25;
193 g_model.moduleData[moduleIdx].pxx.receiver_telem_off = false;
194 g_model.moduleData[moduleIdx].pxx.receiver_channel_9_16 = false;
196 else if (result == STR_BINDING_500MW_CH1_8_TELEM_OFF) {
197 g_model.moduleData[moduleIdx].pxx.power = R9M_LBT_POWER_500;
198 g_model.moduleData[moduleIdx].pxx.receiver_telem_off = true;
199 g_model.moduleData[moduleIdx].pxx.receiver_channel_9_16 = false;
201 else if (result == STR_BINDING_500MW_CH9_16_TELEM_OFF) {
202 g_model.moduleData[moduleIdx].pxx.power = R9M_LBT_POWER_500;
203 g_model.moduleData[moduleIdx].pxx.receiver_telem_off = true;
204 g_model.moduleData[moduleIdx].pxx.receiver_channel_9_16 = true;
206 else if (result == STR_BINDING_1_8_TELEM_ON) {
207 g_model.moduleData[moduleIdx].pxx.receiver_telem_off = false;
208 g_model.moduleData[moduleIdx].pxx.receiver_channel_9_16 = false;
210 else if (result == STR_BINDING_1_8_TELEM_OFF) {
211 g_model.moduleData[moduleIdx].pxx.receiver_telem_off = true;
212 g_model.moduleData[moduleIdx].pxx.receiver_channel_9_16 = false;
214 else if (result == STR_BINDING_9_16_TELEM_ON) {
215 g_model.moduleData[moduleIdx].pxx.receiver_telem_off = false;
216 g_model.moduleData[moduleIdx].pxx.receiver_channel_9_16 = true;
218 else if (result == STR_BINDING_9_16_TELEM_OFF) {
219 g_model.moduleData[moduleIdx].pxx.receiver_telem_off = true;
220 g_model.moduleData[moduleIdx].pxx.receiver_channel_9_16 = true;
222 else {
223 return;
226 moduleFlag[moduleIdx] = MODULE_BIND;
228 #endif
231 void menuModelSetup(event_t event)
233 #if defined(PCBX7)
234 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_POTS, NUM_STICKS+NUM_POTS+NUM_SLIDERS+NUM_ROTARY_ENCODERS-1, 0,
235 LABEL(InternalModule),
236 INTERNAL_MODULE_MODE_ROWS,
237 INTERNAL_MODULE_CHANNELS_ROWS,
238 IF_INTERNAL_MODULE_ON(HAS_RF_PROTOCOL_MODELINDEX(g_model.moduleData[INTERNAL_MODULE].rfProtocol) ? (uint8_t)2 : (uint8_t)1),
239 IF_INTERNAL_MODULE_ON(FAILSAFE_ROWS(INTERNAL_MODULE)),
240 LABEL(ExternalModule),
241 EXTERNAL_MODULE_MODE_ROWS,
242 MULTIMODULE_SUBTYPE_ROWS(EXTERNAL_MODULE)
243 MULTIMODULE_STATUS_ROWS
244 EXTERNAL_MODULE_CHANNELS_ROWS,
245 EXTERNAL_MODULE_BIND_ROWS(),
246 OUTPUT_TYPE_ROWS()
247 FAILSAFE_ROWS(EXTERNAL_MODULE),
248 EXTERNAL_MODULE_OPTION_ROW,
249 MULTIMODULE_MODULE_ROWS
250 EXTERNAL_MODULE_POWER_ROW,
251 EXTRA_MODULE_ROWS
252 TRAINER_ROWS });
253 #elif defined(CPUARM)
254 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,
255 LABEL(ExternalModule),
256 EXTERNAL_MODULE_MODE_ROWS,
257 MULTIMODULE_SUBTYPE_ROWS(EXTERNAL_MODULE)
258 MULTIMODULE_STATUS_ROWS
259 EXTERNAL_MODULE_CHANNELS_ROWS,
260 EXTERNAL_MODULE_BIND_ROWS(),
261 OUTPUT_TYPE_ROWS()
262 FAILSAFE_ROWS(EXTERNAL_MODULE),
263 EXTERNAL_MODULE_OPTION_ROW,
264 MULTIMODULE_MODULE_ROWS
265 EXTERNAL_MODULE_POWER_ROW,
266 EXTRA_MODULE_ROWS
267 TRAINER_ROWS });
268 #elif defined(CPUM64)
269 uint8_t protocol = g_model.protocol;
270 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 });
271 #else
272 uint8_t protocol = g_model.protocol;
273 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) });
274 #endif
276 MENU_CHECK(menuTabModel, MENU_MODEL_SETUP, MODEL_SETUP_MAX_LINES);
278 #if defined(CPUARM) && (defined(DSM2) || defined(PXX))
279 if (menuEvent) {
280 moduleFlag[0] = 0;
281 #if NUM_MODULES > 1
282 moduleFlag[1] = 0;
283 #endif
285 #endif
287 TITLE(STR_MENUSETUP);
289 uint8_t sub = menuVerticalPosition - HEADER_LINE;
290 int8_t editMode = s_editMode;
292 for (uint8_t i=0; i<NUM_BODY_LINES; ++i) {
293 coord_t y = MENU_HEADER_HEIGHT + 1 + i*FH;
294 uint8_t k = i+menuVerticalOffset;
295 #if defined(CPUARM)
296 for (int j=0; j<=k; j++) {
297 if (mstate_tab[j+HEADER_LINE] == HIDDEN_ROW) {
298 if (++k >= (int)DIM(mstate_tab)) {
299 return;
303 #endif
305 LcdFlags blink = ((editMode>0) ? BLINK|INVERS : INVERS);
306 LcdFlags attr = (sub == k ? blink : 0);
308 switch (k) {
309 case ITEM_MODEL_NAME:
310 editSingleName(MODEL_SETUP_2ND_COLUMN, y, STR_MODELNAME, g_model.header.name, sizeof(g_model.header.name), event, attr);
311 #if defined(CPUARM)
312 memcpy(modelHeaders[g_eeGeneral.currModel].name, g_model.header.name, sizeof(g_model.header.name));
313 #endif
314 break;
316 #if defined(CPUARM)
317 case ITEM_MODEL_TIMER1:
318 case ITEM_MODEL_TIMER2:
319 case ITEM_MODEL_TIMER3:
321 unsigned int timerIdx = (k>=ITEM_MODEL_TIMER3 ? 2 : (k>=ITEM_MODEL_TIMER2 ? 1 : 0));
322 TimerData * timer = &g_model.timers[timerIdx];
323 drawStringWithIndex(0*FW, y, STR_TIMER, timerIdx+1);
324 drawTimerMode(MODEL_SETUP_2ND_COLUMN, y, timer->mode, menuHorizontalPosition==0 ? attr : 0);
325 drawTimer(MODEL_SETUP_2ND_COLUMN+5*FW-2+5*FWNUM+1, y, timer->start, RIGHT | (menuHorizontalPosition==1 ? attr : 0), menuHorizontalPosition==2 ? attr : 0);
326 if (attr && (editMode>0 || p1valdiff)) {
327 div_t qr = div(timer->start, 60);
328 switch (menuHorizontalPosition) {
329 case 0:
331 int8_t timerMode = timer->mode;
332 if (timerMode < 0) timerMode -= TMRMODE_COUNT-1;
333 CHECK_INCDEC_MODELVAR_CHECK(event, timerMode, -TMRMODE_COUNT-SWSRC_LAST+1, TMRMODE_COUNT+SWSRC_LAST-1, isSwitchAvailableInTimers);
334 if (timerMode < 0) timerMode += TMRMODE_COUNT-1;
335 timer->mode = timerMode;
336 #if defined(AUTOSWITCH)
337 if (s_editMode>0) {
338 int8_t val = timer->mode - (TMRMODE_COUNT-1);
339 int8_t switchVal = checkIncDecMovedSwitch(val);
340 if (val != switchVal) {
341 timer->mode = switchVal + (TMRMODE_COUNT-1);
342 storageDirty(EE_MODEL);
345 #endif
346 break;
348 case 1:
349 CHECK_INCDEC_MODELVAR_ZERO(event, qr.quot, 539); // 8:59
350 timer->start = qr.rem + qr.quot*60;
351 break;
352 case 2:
353 qr.rem -= checkIncDecModel(event, qr.rem+2, 1, 62)-2;
354 timer->start -= qr.rem ;
355 if ((int16_t)timer->start < 0) timer->start=0;
356 if ((int16_t)timer->start > 5999) timer->start=32399; // 8:59:59
357 break;
360 break;
363 case ITEM_MODEL_TIMER1_NAME:
364 case ITEM_MODEL_TIMER2_NAME:
365 case ITEM_MODEL_TIMER3_NAME:
367 TimerData * timer = &g_model.timers[k>=ITEM_MODEL_TIMER3 ? 2 : (k>=ITEM_MODEL_TIMER2 ? 1 : 0)];
368 editSingleName(MODEL_SETUP_2ND_COLUMN, y, STR_TIMER_NAME, timer->name, sizeof(timer->name), event, attr);
369 break;
372 case ITEM_MODEL_TIMER1_MINUTE_BEEP:
373 case ITEM_MODEL_TIMER2_MINUTE_BEEP:
374 case ITEM_MODEL_TIMER3_MINUTE_BEEP:
376 TimerData * timer = &g_model.timers[k>=ITEM_MODEL_TIMER3 ? 2 : (k>=ITEM_MODEL_TIMER2 ? 1 : 0)];
377 timer->minuteBeep = editCheckBox(timer->minuteBeep, MODEL_SETUP_2ND_COLUMN, y, STR_MINUTEBEEP, attr, event);
378 break;
381 case ITEM_MODEL_TIMER1_COUNTDOWN_BEEP:
382 case ITEM_MODEL_TIMER2_COUNTDOWN_BEEP:
383 case ITEM_MODEL_TIMER3_COUNTDOWN_BEEP:
385 TimerData * timer = &g_model.timers[k>=ITEM_MODEL_TIMER3 ? 2 : (k>=ITEM_MODEL_TIMER2 ? 1 : 0)];
386 timer->countdownBeep = editChoice(MODEL_SETUP_2ND_COLUMN, y, STR_BEEPCOUNTDOWN, STR_VBEEPCOUNTDOWN, timer->countdownBeep, COUNTDOWN_SILENT, COUNTDOWN_COUNT-1, attr, event);
387 break;
390 case ITEM_MODEL_TIMER1_PERSISTENT:
391 case ITEM_MODEL_TIMER2_PERSISTENT:
392 case ITEM_MODEL_TIMER3_PERSISTENT:
394 TimerData * timer = &g_model.timers[k>=ITEM_MODEL_TIMER3 ? 2 : (k>=ITEM_MODEL_TIMER2 ? 1 : 0)];
395 timer->persistent = editChoice(MODEL_SETUP_2ND_COLUMN, y, STR_PERSISTENT, STR_VPERSISTENT, timer->persistent, 0, 2, attr, event);
396 break;
398 #else // AVR
399 case ITEM_MODEL_TIMER1:
400 case ITEM_MODEL_TIMER2:
401 case ITEM_MODEL_TIMER1_MINUTE_BEEP:
402 case ITEM_MODEL_TIMER2_MINUTE_BEEP:
403 case ITEM_MODEL_TIMER1_COUNTDOWN_BEEP:
404 case ITEM_MODEL_TIMER2_COUNTDOWN_BEEP:
406 TimerData *timer = &g_model.timers[k>=ITEM_MODEL_TIMER2 ? 1 : 0];
407 if (k==ITEM_MODEL_TIMER1_MINUTE_BEEP || k==ITEM_MODEL_TIMER2_MINUTE_BEEP) {
408 timer->minuteBeep = editCheckBox(timer->minuteBeep, MODEL_SETUP_2ND_COLUMN, y, STR_MINUTEBEEP, attr, event);
410 else if (k==ITEM_MODEL_TIMER1_COUNTDOWN_BEEP || k==ITEM_MODEL_TIMER2_COUNTDOWN_BEEP) {
411 timer->countdownBeep = editCheckBox(timer->countdownBeep, MODEL_SETUP_2ND_COLUMN, y, STR_BEEPCOUNTDOWN, attr, event);
413 else {
414 drawStringWithIndex(0*FW, y, STR_TIMER, k>=ITEM_MODEL_TIMER2 ? 2 : 1);
415 drawTimerMode(MODEL_SETUP_2ND_COLUMN, y, timer->mode, menuHorizontalPosition==0 ? attr : 0);
416 drawTimer(MODEL_SETUP_2ND_COLUMN+5*FW-2+5*FWNUM+1, y, timer->start, menuHorizontalPosition==1 ? attr : 0, menuHorizontalPosition==2 ? attr : 0);
417 if (attr && (editMode>0 || p1valdiff)) {
418 div_t qr = div(timer->start, 60);
419 switch (menuHorizontalPosition) {
420 case 0:
421 CHECK_INCDEC_MODELVAR_CHECK(event, timer->mode, SWSRC_FIRST, TMRMODE_COUNT+SWSRC_LAST-1/*SWSRC_None removed*/, isSwitchAvailableInTimers);
422 break;
423 case 1:
424 CHECK_INCDEC_MODELVAR_ZERO(event, qr.quot, 59);
425 timer->start = qr.rem + qr.quot*60;
426 break;
427 case 2:
428 qr.rem -= checkIncDecModel(event, qr.rem+2, 1, 62) - 2;
429 if ((int16_t)timer->start >= qr.rem) {
430 timer->start -= qr.rem ;
432 if ((int16_t)timer->start > 3599) {
433 timer->start = 3599; // 59:59
435 break;
439 break;
442 #if defined(CPUM2560)
443 case ITEM_MODEL_TIMER1_PERSISTENT:
444 case ITEM_MODEL_TIMER2_PERSISTENT:
446 TimerData &timer = g_model.timers[k==ITEM_MODEL_TIMER2_PERSISTENT];
447 timer.persistent = editChoice(MODEL_SETUP_2ND_COLUMN, y, STR_PERSISTENT, STR_VPERSISTENT, timer.persistent, 0, 2, attr, event);
448 break;
450 #endif
451 #endif
453 case ITEM_MODEL_EXTENDED_LIMITS:
454 ON_OFF_MENU_ITEM(g_model.extendedLimits, MODEL_SETUP_2ND_COLUMN, y, STR_ELIMITS, attr, event);
455 break;
457 case ITEM_MODEL_EXTENDED_TRIMS:
458 #if defined(CPUM64)
459 ON_OFF_MENU_ITEM(g_model.extendedTrims, MODEL_SETUP_2ND_COLUMN, y, STR_ETRIMS, attr, event);
460 #else
461 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);
462 lcdDrawText(MODEL_SETUP_2ND_COLUMN+4*FW, y, STR_RESET_BTN, (menuHorizontalPosition>0 && !NO_HIGHLIGHT()) ? attr : 0);
463 if (attr && menuHorizontalPosition>0) {
464 s_editMode = 0;
465 if (event==EVT_KEY_LONG(KEY_ENTER)) {
466 START_NO_HIGHLIGHT();
467 for (uint8_t i=0; i<MAX_FLIGHT_MODES; i++) {
468 memclear(&g_model.flightModeData[i], TRIMS_ARRAY_SIZE);
470 storageDirty(EE_MODEL);
471 AUDIO_WARNING1();
474 #endif
475 break;
477 #if defined(CPUARM)
478 case ITEM_MODEL_DISPLAY_TRIMS:
479 g_model.displayTrims = editChoice(MODEL_SETUP_2ND_COLUMN, y, STR_DISPLAY_TRIMS, STR_VDISPLAYTRIMS, g_model.displayTrims, 0, 2, attr, event);
480 break;
481 #endif
483 case ITEM_MODEL_TRIM_INC:
484 g_model.trimInc = editChoice(MODEL_SETUP_2ND_COLUMN, y, STR_TRIMINC, STR_VTRIMINC, g_model.trimInc, -2, 2, attr, event);
485 break;
487 case ITEM_MODEL_THROTTLE_REVERSED:
488 ON_OFF_MENU_ITEM(g_model.throttleReversed, MODEL_SETUP_2ND_COLUMN, y, STR_THROTTLEREVERSE, attr, event ) ;
489 break;
491 case ITEM_MODEL_THROTTLE_TRACE:
493 lcdDrawTextAlignedLeft(y, STR_TTRACE);
494 if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, g_model.thrTraceSrc, NUM_POTS+NUM_SLIDERS+MAX_OUTPUT_CHANNELS);
495 uint8_t idx = g_model.thrTraceSrc + MIXSRC_Thr;
496 if (idx > MIXSRC_Thr)
497 idx += 1;
498 if (idx >= MIXSRC_FIRST_POT+NUM_POTS+NUM_SLIDERS)
499 idx += MIXSRC_CH1 - MIXSRC_FIRST_POT - NUM_POTS - NUM_SLIDERS;
500 drawSource(MODEL_SETUP_2ND_COLUMN, y, idx, attr);
501 break;
504 case ITEM_MODEL_THROTTLE_TRIM:
505 ON_OFF_MENU_ITEM(g_model.thrTrim, MODEL_SETUP_2ND_COLUMN, y, STR_TTRIM, attr, event);
506 break;
508 #if defined(CPUARM)
509 case ITEM_MODEL_PREFLIGHT_LABEL:
510 lcdDrawTextAlignedLeft(y, STR_PREFLIGHT);
511 break;
513 case ITEM_MODEL_CHECKLIST_DISPLAY:
514 ON_OFF_MENU_ITEM(g_model.displayChecklist, MODEL_SETUP_2ND_COLUMN, y, STR_CHECKLIST, attr, event);
515 break;
516 #endif
518 case ITEM_MODEL_THROTTLE_WARNING:
519 g_model.disableThrottleWarning = !editCheckBox(!g_model.disableThrottleWarning, MODEL_SETUP_2ND_COLUMN, y, STR_THROTTLEWARNING, attr, event);
520 break;
522 case ITEM_MODEL_SWITCHES_WARNING:
524 lcdDrawTextAlignedLeft(y, STR_SWITCHWARNING);
525 swarnstate_t states = g_model.switchWarningState;
526 char c;
527 if (attr) {
528 s_editMode = 0;
529 if (!READ_ONLY()) {
530 switch (event) {
531 CASE_EVT_ROTARY_BREAK
532 case EVT_KEY_BREAK(KEY_ENTER):
533 #if defined(CPUM64)
534 g_model.switchWarningEnable ^= (1 << menuHorizontalPosition);
535 storageDirty(EE_MODEL);
536 #else
537 #if defined(PCBX7)
538 if (menuHorizontalPosition < NUM_SWITCHES) {
539 g_model.switchWarningEnable ^= (1 << (menuHorizontalPosition-1));
540 #else
541 if (menuHorizontalPosition < NUM_SWITCHES-1) {
542 g_model.switchWarningEnable ^= (1 << menuHorizontalPosition);
543 #endif
544 storageDirty(EE_MODEL);
546 #endif
547 break;
549 case EVT_KEY_LONG(KEY_ENTER):
550 #if defined(CPUM64)
551 getMovedSwitch();
552 g_model.switchWarningState = switches_states;
553 AUDIO_WARNING1();
554 storageDirty(EE_MODEL);
555 #elif defined(PCBX7)
556 if (attr && menuHorizontalPosition == 0) {
557 getMovedSwitch();
558 g_model.switchWarningState = switches_states;
559 AUDIO_WARNING1();
560 storageDirty(EE_MODEL);
562 #else
563 if (menuHorizontalPosition == NUM_SWITCHES-1) {
564 START_NO_HIGHLIGHT();
565 getMovedSwitch();
566 g_model.switchWarningState = switches_states;
567 AUDIO_WARNING1();
568 storageDirty(EE_MODEL);
570 #endif
571 killEvents(event);
572 break;
576 LcdFlags line = attr;
577 #if defined(PCBX7)
578 int current = 0;
579 for (int i=0; i<NUM_SWITCHES-1; i++) {
580 if (SWITCH_WARNING_ALLOWED(i)) {
581 div_t qr = div(current, 8);
582 uint8_t swactive = !(g_model.switchWarningEnable & (1<<i));
583 c = "\300-\301"[states & 0x03];
584 lcdDrawChar(MODEL_SETUP_2ND_COLUMN+qr.rem*(2*FW), y, (i < 4 ? 'A'+i : 'B'+i), line && (menuHorizontalPosition-1==current) ? INVERS : 0);
585 if (swactive) lcdDrawChar(lcdNextPos, y, c);
586 ++current;
588 states >>= 2;
590 if (attr && menuHorizontalPosition == 0) {
591 lcdDrawFilledRect(MODEL_SETUP_2ND_COLUMN-1, y-1, (NUM_SWITCHES-1)*(2*FW), 1+FH*((current+7)/8));
593 #else // PCBX7
594 for (uint8_t i=0; i<NUM_SWITCHES-1/*not on TRN switch*/; i++) {
595 uint8_t swactive = !(g_model.switchWarningEnable & 1 << i);
596 attr = 0;
598 if (IS_3POS(i)) {
599 c = '0'+(states & 0x03);
600 states >>= 2;
602 else {
603 if ((states & 0x01) && swactive)
604 attr = INVERS;
605 c = pgm_read_byte(STR_VSWITCHES - 2 + 9 + (3*(i+1)));
606 states >>= 1;
608 if (line && (menuHorizontalPosition == i)) {
609 attr = BLINK | INVERS;
611 #if defined(CPUARM)
612 lcdDrawChar(MODEL_SETUP_2ND_COLUMN+i*FW, y, (swactive) ? c : '-', attr);
613 #else
614 lcdDrawChar(MODEL_SETUP_2ND_COLUMN+i*FW, y, (swactive || (attr & BLINK)) ? c : '-', attr);
615 #endif
616 #if !defined(CPUM64)
617 lcdDrawText(MODEL_SETUP_2ND_COLUMN+(NUM_SWITCHES*FW), y, PSTR("<]"), (menuHorizontalPosition == NUM_SWITCHES-1 && !NO_HIGHLIGHT()) ? line : 0);
618 #endif
620 #endif // PCBX7
621 break;
623 #if defined(PCBX7)
624 case ITEM_MODEL_POTS_WARNING:
625 lcdDrawTextAlignedLeft(y, STR_POTWARNING);
626 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, PSTR("\004""OFF\0""Man\0""Auto"), g_model.potsWarnMode, (menuHorizontalPosition == 0) ? attr : 0);
627 if (attr && (menuHorizontalPosition == 0)) {
628 CHECK_INCDEC_MODELVAR(event, g_model.potsWarnMode, POTS_WARN_OFF, POTS_WARN_AUTO);
629 storageDirty(EE_MODEL);
632 if (attr) {
633 if (menuHorizontalPosition > 0) s_editMode = 0;
634 if (!READ_ONLY() && menuHorizontalPosition > 0) {
635 switch (event) {
636 case EVT_KEY_LONG(KEY_ENTER):
637 killEvents(event);
638 if (g_model.potsWarnMode == POTS_WARN_MANUAL) {
639 SAVE_POT_POSITION(menuHorizontalPosition-1);
640 AUDIO_WARNING1();
641 storageDirty(EE_MODEL);
643 break;
644 case EVT_KEY_BREAK(KEY_ENTER):
645 g_model.potsWarnEnabled ^= (1 << (menuHorizontalPosition-1));
646 storageDirty(EE_MODEL);
647 break;
651 if (g_model.potsWarnMode) {
652 coord_t x = MODEL_SETUP_2ND_COLUMN+28;
653 for (int i=0; i<NUM_POTS+NUM_SLIDERS; ++i) {
654 if (i<NUM_XPOTS && !IS_POT_SLIDER_AVAILABLE(POT1+i)) {
655 if (attr && (menuHorizontalPosition==i+1)) REPEAT_LAST_CURSOR_MOVE();
657 else {
658 LcdFlags flags = ((menuHorizontalPosition==i+1) && attr) ? BLINK : 0;
659 if ((!attr || menuHorizontalPosition >= 0) && !(g_model.potsWarnEnabled & (1 << i))) {
660 flags |= INVERS;
663 // TODO add a new function
664 lcdDrawSizedText(x, y, STR_VSRCRAW+2+STR_VSRCRAW[0]*(NUM_STICKS+1+i), STR_VSRCRAW[0]-1, flags & ~ZCHAR);
665 x = lcdNextPos+3;
669 break;
670 #endif // PCBX7
672 case ITEM_MODEL_BEEP_CENTER:
673 lcdDrawTextAlignedLeft(y, STR_BEEPCTR);
674 for (uint8_t i=0; i<NUM_STICKS+NUM_POTS+NUM_SLIDERS+NUM_ROTARY_ENCODERS; i++) {
675 // TODO flash saving, \001 not needed in STR_RETA123
676 coord_t x = MODEL_SETUP_2ND_COLUMN+i*FW;
677 lcdDrawTextAtIndex(x, y, STR_RETA123, i, ((menuHorizontalPosition==i) && attr) ? BLINK|INVERS : (((g_model.beepANACenter & ((BeepANACenter)1<<i)) || (attr && CURSOR_ON_LINE())) ? INVERS : 0 ) );
679 if (attr && CURSOR_ON_CELL) {
680 if (event==EVT_KEY_BREAK(KEY_ENTER) || p1valdiff) {
681 if (READ_ONLY_UNLOCKED()) {
682 s_editMode = 0;
683 g_model.beepANACenter ^= ((BeepANACenter)1<<menuHorizontalPosition);
684 storageDirty(EE_MODEL);
688 break;
690 #if defined(CPUARM)
691 case ITEM_MODEL_USE_GLOBAL_FUNCTIONS:
692 lcdDrawTextAlignedLeft(y, STR_USE_GLOBAL_FUNCS);
693 drawCheckBox(MODEL_SETUP_2ND_COLUMN, y, !g_model.noGlobalFunctions, attr);
694 if (attr) g_model.noGlobalFunctions = !checkIncDecModel(event, !g_model.noGlobalFunctions, 0, 1);
695 break;
696 #endif
698 #if defined(PCBX7)
699 case ITEM_MODEL_INTERNAL_MODULE_LABEL:
700 lcdDrawTextAlignedLeft(y, TR_INTERNALRF);
701 break;
703 case ITEM_MODEL_INTERNAL_MODULE_MODE:
704 lcdDrawTextAlignedLeft(y, STR_MODE);
705 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_XJT_PROTOCOLS, 1+g_model.moduleData[0].rfProtocol, attr);
706 if (attr) {
707 g_model.moduleData[INTERNAL_MODULE].rfProtocol = checkIncDec(event, g_model.moduleData[INTERNAL_MODULE].rfProtocol, RF_PROTO_OFF, RF_PROTO_LAST, EE_MODEL, isRfProtocolAvailable);
708 if (checkIncDec_Ret) {
709 g_model.moduleData[0].type = MODULE_TYPE_XJT;
710 g_model.moduleData[0].channelsStart = 0;
711 g_model.moduleData[0].channelsCount = DEFAULT_CHANNELS(INTERNAL_MODULE);
712 if (g_model.moduleData[INTERNAL_MODULE].rfProtocol == RF_PROTO_OFF)
713 g_model.moduleData[INTERNAL_MODULE].type = MODULE_TYPE_NONE;
716 break;
717 #endif
719 #if defined(PCBSKY9X)
720 case ITEM_MODEL_EXTRA_MODULE_LABEL:
721 lcdDrawTextAlignedLeft(y, "RF Port 2 (PPM)");
722 break;
723 #endif
725 #if defined(CPUARM)
726 case ITEM_MODEL_EXTERNAL_MODULE_LABEL:
727 lcdDrawTextAlignedLeft(y, TR_EXTERNALRF);
728 break;
730 case ITEM_MODEL_EXTERNAL_MODULE_MODE:
731 lcdDrawTextAlignedLeft(y, STR_MODE);
732 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_TARANIS_PROTOCOLS, g_model.moduleData[EXTERNAL_MODULE].type, menuHorizontalPosition==0 ? attr : 0);
733 if (IS_MODULE_XJT(EXTERNAL_MODULE))
734 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN+5*FW, y, STR_XJT_PROTOCOLS, 1+g_model.moduleData[EXTERNAL_MODULE].rfProtocol, menuHorizontalPosition==1 ? attr : 0);
735 else if (IS_MODULE_DSM2(EXTERNAL_MODULE))
736 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN+5*FW, y, STR_DSM_PROTOCOLS, g_model.moduleData[EXTERNAL_MODULE].rfProtocol, menuHorizontalPosition==1 ? attr : 0);
737 else if (IS_MODULE_R9M(EXTERNAL_MODULE))
738 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN+5*FW, y, STR_R9M_MODES, g_model.moduleData[EXTERNAL_MODULE].subType, (menuHorizontalPosition==1 ? attr : 0));
739 #if defined(MULTIMODULE)
740 else if (IS_MODULE_MULTIMODULE(EXTERNAL_MODULE)) {
741 int multi_rfProto = g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(false);
742 if (g_model.moduleData[EXTERNAL_MODULE].multi.customProto)
743 lcdDrawText(MODEL_SETUP_2ND_COLUMN+5*FW, y, STR_MULTI_CUSTOM, menuHorizontalPosition==1 ? attr : 0);
744 else
745 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN+5*FW, y, STR_MULTI_PROTOCOLS, multi_rfProto, menuHorizontalPosition==1 ? attr : 0);
747 #endif
748 if (attr && (editMode>0 || p1valdiff)) {
749 switch (menuHorizontalPosition) {
750 case 0:
751 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);
752 if (checkIncDec_Ret) {
753 g_model.moduleData[EXTERNAL_MODULE].rfProtocol = 0;
754 g_model.moduleData[EXTERNAL_MODULE].channelsStart = 0;
755 g_model.moduleData[EXTERNAL_MODULE].channelsCount = DEFAULT_CHANNELS(EXTERNAL_MODULE);
756 if (IS_MODULE_SBUS(EXTERNAL_MODULE))
757 g_model.moduleData[EXTERNAL_MODULE].sbus.refreshRate = -31;
759 break;
760 case 1:
761 if (IS_MODULE_DSM2(EXTERNAL_MODULE))
762 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[EXTERNAL_MODULE].rfProtocol, DSM2_PROTO_LP45, DSM2_PROTO_DSMX);
763 else if (IS_MODULE_R9M(EXTERNAL_MODULE))
764 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[EXTERNAL_MODULE].subType, MODULE_SUBTYPE_R9M_FCC, MODULE_SUBTYPE_R9M_LBT);
765 #if defined(MULTIMODULE)
766 else if (IS_MODULE_MULTIMODULE(EXTERNAL_MODULE)) {
767 int multiRfProto = g_model.moduleData[EXTERNAL_MODULE].multi.customProto == 1 ? MM_RF_PROTO_CUSTOM : g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(false);
768 CHECK_INCDEC_MODELVAR(event, multiRfProto, MM_RF_PROTO_FIRST, MM_RF_PROTO_LAST);
769 if (checkIncDec_Ret) {
770 g_model.moduleData[EXTERNAL_MODULE].multi.customProto = (multiRfProto == MM_RF_PROTO_CUSTOM);
771 if (!g_model.moduleData[EXTERNAL_MODULE].multi.customProto)
772 g_model.moduleData[EXTERNAL_MODULE].setMultiProtocol(multiRfProto);
773 g_model.moduleData[EXTERNAL_MODULE].subType = 0;
774 // Sensible default for DSM2 (same as for ppm): 7ch@22ms + Autodetect settings enabled
775 if (g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(true) == MM_RF_PROTO_DSM2) {
776 g_model.moduleData[EXTERNAL_MODULE].multi.autoBindMode = 1;
778 else {
779 g_model.moduleData[EXTERNAL_MODULE].multi.autoBindMode = 0;
781 g_model.moduleData[EXTERNAL_MODULE].multi.optionValue = 0;
784 #endif
785 else {
786 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[EXTERNAL_MODULE].rfProtocol, RF_PROTO_X16, RF_PROTO_LAST);
788 if (checkIncDec_Ret) {
789 g_model.moduleData[EXTERNAL_MODULE].channelsStart = 0;
790 g_model.moduleData[EXTERNAL_MODULE].channelsCount = DEFAULT_CHANNELS(EXTERNAL_MODULE);
794 break;
795 #endif
797 #if defined(MULTIMODULE)
798 case ITEM_MODEL_EXTERNAL_MODULE_SUBTYPE:
800 lcdDrawTextAlignedLeft(y, STR_SUBTYPE);
801 uint8_t multi_rfProto = g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(true);
802 const mm_protocol_definition * pdef = getMultiProtocolDefinition(multi_rfProto);
804 if (multi_rfProto == MM_RF_CUSTOM_SELECTED) {
805 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN + 3 * FW, y, g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(false), RIGHT | (menuHorizontalPosition == 0 ? attr : 0), 2);
806 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN + 5 * FW, y, g_model.moduleData[EXTERNAL_MODULE].subType, RIGHT | (menuHorizontalPosition == 1 ? attr : 0), 2);
808 else {
809 if (pdef->subTypeString != nullptr)
810 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, pdef->subTypeString, g_model.moduleData[EXTERNAL_MODULE].subType, attr);
812 if (attr && (editMode > 0 || p1valdiff)) {
813 switch (menuHorizontalPosition) {
814 case 0:
815 if (multi_rfProto == MM_RF_CUSTOM_SELECTED)
816 g_model.moduleData[EXTERNAL_MODULE].setMultiProtocol(checkIncDec(event, g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(false), 0, 63, EE_MODEL));
817 else if (pdef->maxSubtype > 0)
818 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[EXTERNAL_MODULE].subType, 0, pdef->maxSubtype);
819 break;
820 case 1:
821 // Custom protocol, third column is subtype
822 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[EXTERNAL_MODULE].subType, 0, 7);
823 break;
827 break;
828 #endif
830 #if defined(PCBX7)
831 case ITEM_MODEL_TRAINER_LABEL:
832 lcdDrawTextAlignedLeft(y, STR_TRAINER);
833 break;
835 case ITEM_MODEL_TRAINER_MODE:
836 lcdDrawTextAlignedLeft(y, STR_MODE);
837 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_VTRAINERMODES, g_model.trainerMode, attr);
838 if (attr) {
839 g_model.trainerMode = checkIncDec(event, g_model.trainerMode, 0, TRAINER_MODE_MAX(), EE_MODEL, isTrainerModeAvailable);
840 #if defined(BLUETOOTH)
841 if (checkIncDec_Ret) {
842 bluetoothState = BLUETOOTH_STATE_OFF;
843 bluetoothDistantAddr[0] = 0;
845 #endif
847 break;
848 #endif
850 #if defined(PCBX7) && defined(BLUETOOTH)
851 case ITEM_MODEL_TRAINER_BLUETOOTH:
852 if (g_model.trainerMode == TRAINER_MODE_MASTER_BLUETOOTH) {
853 if (attr) {
854 s_editMode = 0;
856 if (bluetoothDistantAddr[0]) {
857 lcdDrawText(INDENT_WIDTH, y+1, bluetoothDistantAddr, TINSIZE);
858 if (bluetoothState != BLUETOOTH_STATE_CONNECTED) {
859 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, BUTTON("Bind"), menuHorizontalPosition == 0 ? attr : 0);
860 lcdDrawText(MODEL_SETUP_2ND_COLUMN+5*FW, y, BUTTON("Clear"), menuHorizontalPosition == 1 ? attr : 0);
862 else {
863 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, BUTTON("Clear"), attr);
865 if (attr && event == EVT_KEY_FIRST(KEY_ENTER)) {
866 if (bluetoothState == BLUETOOTH_STATE_CONNECTED || menuHorizontalPosition == 1) {
867 bluetoothState = BLUETOOTH_STATE_OFF;
868 bluetoothDistantAddr[0] = 0;
870 else {
871 bluetoothState = BLUETOOTH_STATE_BIND_REQUESTED;
875 else {
876 lcdDrawText(INDENT_WIDTH, y, "---");
877 if (bluetoothState < BLUETOOTH_STATE_IDLE)
878 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, BUTTON("Init"), attr);
879 else
880 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, BUTTON("Discover"), attr);
881 if (attr && event == EVT_KEY_FIRST(KEY_ENTER)) {
882 if (bluetoothState < BLUETOOTH_STATE_IDLE)
883 bluetoothState = BLUETOOTH_STATE_OFF;
884 else
885 bluetoothState = BLUETOOTH_STATE_DISCOVER_REQUESTED;
889 else {
890 if (bluetoothDistantAddr[0])
891 lcdDrawText(INDENT_WIDTH, y+1, bluetoothDistantAddr, TINSIZE);
892 else
893 lcdDrawText(INDENT_WIDTH, y, "---");
894 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, bluetoothState == BLUETOOTH_STATE_CONNECTED ? "Connected" : "!Connected");
896 break;
897 #endif
899 #if defined(PCBX7)
900 case ITEM_MODEL_TRAINER_CHANNELS:
901 case ITEM_MODEL_INTERNAL_MODULE_CHANNELS:
902 #endif
903 #if defined(PCBSKY9X)
904 case ITEM_MODEL_EXTRA_MODULE_CHANNELS:
905 #endif
906 #if defined(CPUARM)
907 case ITEM_MODEL_EXTERNAL_MODULE_CHANNELS:
909 uint8_t moduleIdx = CURRENT_MODULE_EDITED(k);
910 ModuleData & moduleData = g_model.moduleData[moduleIdx];
911 lcdDrawTextAlignedLeft(y, STR_CHANNELRANGE);
912 if ((int8_t)PORT_CHANNELS_ROWS(moduleIdx) >= 0) {
913 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_CH, menuHorizontalPosition==0 ? attr : 0);
914 lcdDrawNumber(lcdLastRightPos, y, moduleData.channelsStart+1, LEFT | (menuHorizontalPosition==0 ? attr : 0));
915 lcdDrawChar(lcdLastRightPos, y, '-');
916 lcdDrawNumber(lcdLastRightPos + FW+1, y, moduleData.channelsStart+NUM_CHANNELS(moduleIdx), LEFT | (menuHorizontalPosition==1 ? attr : 0));
917 if (attr && (editMode>0 || p1valdiff)) {
918 switch (menuHorizontalPosition) {
919 case 0:
920 CHECK_INCDEC_MODELVAR_ZERO(event, moduleData.channelsStart, 32-8-moduleData.channelsCount);
921 break;
922 case 1:
923 CHECK_INCDEC_MODELVAR(event, moduleData.channelsCount, -4, min<int8_t>(MAX_CHANNELS(moduleIdx), 32-8-moduleData.channelsStart));
924 if ((k == ITEM_MODEL_EXTERNAL_MODULE_CHANNELS && g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_PPM)) {
925 SET_DEFAULT_PPM_FRAME_LENGTH(moduleIdx);
927 break;
931 break;
933 #endif
935 #if defined(PCBX7)
936 case ITEM_MODEL_TRAINER_PARAMS:
937 case ITEM_MODEL_INTERNAL_MODULE_BIND:
938 #endif
939 #if defined(PCBSKY9X)
940 case ITEM_MODEL_EXTRA_MODULE_BIND:
941 #endif
942 #if defined(CPUARM)
943 case ITEM_MODEL_EXTERNAL_MODULE_BIND:
945 uint8_t moduleIdx = CURRENT_MODULE_EDITED(k);
946 ModuleData & moduleData = g_model.moduleData[moduleIdx];
947 if (IS_MODULE_PPM(moduleIdx)) {
948 lcdDrawTextAlignedLeft(y, STR_PPMFRAME);
949 lcdDrawText(MODEL_SETUP_2ND_COLUMN+3*FW, y, STR_MS);
950 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, (int16_t)moduleData.ppm.frameLength*5 + 225, (menuHorizontalPosition<=0 ? attr : 0) | PREC1|LEFT);
951 lcdDrawChar(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, 'u');
952 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, (moduleData.ppm.delay*50)+300, RIGHT | ((CURSOR_ON_LINE() || menuHorizontalPosition==1) ? attr : 0));
953 lcdDrawChar(MODEL_SETUP_2ND_COLUMN+10*FW, y, moduleData.ppm.pulsePol ? '+' : '-', (CURSOR_ON_LINE() || menuHorizontalPosition==2) ? attr : 0);
954 if (attr && (editMode>0 || p1valdiff)) {
955 switch (menuHorizontalPosition) {
956 case 0:
957 CHECK_INCDEC_MODELVAR(event, moduleData.ppm.frameLength, -20, 35);
958 break;
959 case 1:
960 CHECK_INCDEC_MODELVAR(event, moduleData.ppm.delay, -4, 10);
961 break;
962 case 2:
963 CHECK_INCDEC_MODELVAR_ZERO(event, moduleData.ppm.pulsePol, 1);
964 break;
968 else if (IS_MODULE_SBUS(moduleIdx)) {
969 lcdDrawTextAlignedLeft(y, STR_REFRESHRATE);
970 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, (int16_t)moduleData.ppm.frameLength*5 + 225, (menuHorizontalPosition<=0 ? attr : 0) | PREC1|LEFT);
971 lcdDrawText(lcdLastRightPos, y, STR_MS);
972 lcdDrawText(MODEL_SETUP_2ND_COLUMN+5*FW+2, y, moduleData.sbus.noninverted ? "no inv" : "normal", (CURSOR_ON_LINE() || menuHorizontalPosition==1) ? attr : 0);
974 if (attr && s_editMode>0) {
975 switch (menuHorizontalPosition) {
976 case 0:
977 CHECK_INCDEC_MODELVAR(event, moduleData.ppm.frameLength, -33, 35);
978 break;
979 case 1:
980 CHECK_INCDEC_MODELVAR_ZERO(event, moduleData.sbus.noninverted, 1);
981 break;
985 else {
986 horzpos_t l_posHorz = menuHorizontalPosition;
987 coord_t xOffsetBind = MODEL_SETUP_BIND_OFS;
988 if (IS_MODULE_XJT(moduleIdx) && IS_D8_RX(moduleIdx)) {
989 xOffsetBind = 0;
990 lcdDrawTextAlignedLeft(y, STR_RECEIVER);
991 if (attr) l_posHorz += 1;
993 else {
994 lcdDrawTextAlignedLeft(y, STR_RECEIVER_NUM);
996 if (IS_MODULE_PXX(moduleIdx) || IS_MODULE_DSM2(moduleIdx) || IS_MODULE_MULTIMODULE(moduleIdx)) {
997 if (xOffsetBind) lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, g_model.header.modelId[moduleIdx], (l_posHorz==0 ? attr : 0) | LEADING0|LEFT, 2);
998 if (attr && l_posHorz==0) {
999 if (editMode>0 || p1valdiff) {
1000 CHECK_INCDEC_MODELVAR_ZERO(event, g_model.header.modelId[moduleIdx], MAX_RX_NUM(moduleIdx));
1001 if (checkIncDec_Ret) {
1002 modelHeaders[g_eeGeneral.currModel].modelId[moduleIdx] = g_model.header.modelId[moduleIdx];
1005 if (editMode==0 && event==EVT_KEY_BREAK(KEY_ENTER)) {
1006 checkModelIdUnique(g_eeGeneral.currModel, moduleIdx);
1009 lcdDrawText(MODEL_SETUP_2ND_COLUMN+xOffsetBind, y, STR_MODULE_BIND, l_posHorz==1 ? attr : 0);
1010 lcdDrawText(MODEL_SETUP_2ND_COLUMN+MODEL_SETUP_RANGE_OFS+xOffsetBind, y, STR_MODULE_RANGE, l_posHorz==2 ? attr : 0);
1011 uint8_t newFlag = 0;
1012 #if defined(MULTIMODULE)
1013 if (multiBindStatus == MULTI_BIND_FINISHED) {
1014 multiBindStatus = MULTI_NORMAL_OPERATION;
1015 s_editMode=0;
1017 #endif
1018 #if defined(PCBTARANIS)
1019 if (attr && l_posHorz > 0) {
1020 if (s_editMode > 0) {
1021 if (l_posHorz == 1) {
1022 if (IS_MODULE_R9M(moduleIdx) || (IS_MODULE_XJT(moduleIdx) && g_model.moduleData[moduleIdx].rfProtocol== RF_PROTO_X16)) {
1023 if (event == EVT_KEY_BREAK(KEY_ENTER)) {
1024 uint8_t default_selection;
1025 if (IS_MODULE_R9M_LBT(moduleIdx)) {
1026 POPUP_MENU_ADD_ITEM(STR_BINDING_25MW_CH1_8_TELEM_OFF);
1027 POPUP_MENU_ADD_ITEM(STR_BINDING_25MW_CH1_8_TELEM_ON);
1028 POPUP_MENU_ADD_ITEM(STR_BINDING_500MW_CH1_8_TELEM_OFF);
1029 POPUP_MENU_ADD_ITEM(STR_BINDING_500MW_CH9_16_TELEM_OFF);
1030 default_selection = 2;
1032 else {
1033 POPUP_MENU_ADD_ITEM(STR_BINDING_1_8_TELEM_ON);
1034 POPUP_MENU_ADD_ITEM(STR_BINDING_1_8_TELEM_OFF);
1035 POPUP_MENU_ADD_ITEM(STR_BINDING_9_16_TELEM_ON);
1036 POPUP_MENU_ADD_ITEM(STR_BINDING_9_16_TELEM_OFF);
1037 default_selection = g_model.moduleData[INTERNAL_MODULE].pxx.receiver_telem_off + (g_model.moduleData[INTERNAL_MODULE].pxx.receiver_channel_9_16 << 1);
1039 POPUP_MENU_SELECT_ITEM(default_selection);
1040 POPUP_MENU_START(onBindMenu);
1041 continue;
1043 if (moduleFlag[moduleIdx] == MODULE_BIND) {
1044 newFlag = MODULE_BIND;
1046 else {
1047 if (!popupMenuNoItems) {
1048 s_editMode = 0; // this is when popup is exited before a choice is made
1052 else {
1053 newFlag = MODULE_BIND;
1056 else if (l_posHorz == 2) {
1057 newFlag = MODULE_RANGECHECK;
1061 #else
1062 if (attr && l_posHorz>0 && s_editMode>0) {
1063 if (l_posHorz == 1)
1064 newFlag = MODULE_BIND;
1065 else if (l_posHorz == 2)
1066 newFlag = MODULE_RANGECHECK;
1068 #endif
1069 moduleFlag[moduleIdx] = newFlag;
1071 #if defined(MULTIMODULE)
1072 if (newFlag == MODULE_BIND) {
1073 multiBindStatus = MULTI_BIND_INITIATED;
1075 #endif
1079 break;
1081 #endif
1083 #if defined(PCBSKY9X) && defined(REVX)
1084 case ITEM_MODEL_EXTERNAL_MODULE_OUTPUT_TYPE:
1086 uint8_t moduleIdx = CURRENT_MODULE_EDITED(k);
1087 ModuleData & moduleData = g_model.moduleData[moduleIdx];
1088 moduleData.ppm.outputType = editChoice(MODEL_SETUP_2ND_COLUMN, y, STR_OUTPUT_TYPE, STR_VOUTPUT_TYPE, moduleData.ppm.outputType, 0, 1, attr, event);
1089 break;
1091 #endif
1093 #if defined(PCBX7)
1094 case ITEM_MODEL_INTERNAL_MODULE_FAILSAFE:
1095 #endif
1096 #if defined(CPUARM)
1097 case ITEM_MODEL_EXTERNAL_MODULE_FAILSAFE: {
1098 uint8_t moduleIdx = CURRENT_MODULE_EDITED(k);
1099 ModuleData &moduleData = g_model.moduleData[moduleIdx];
1100 lcdDrawTextAlignedLeft(y, STR_FAILSAFE);
1101 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_VFAILSAFE, moduleData.failsafeMode, menuHorizontalPosition == 0 ? attr : 0);
1102 if (moduleData.failsafeMode == FAILSAFE_CUSTOM)
1103 lcdDrawText(MODEL_SETUP_2ND_COLUMN + MODEL_SETUP_SET_FAILSAFE_OFS, y, STR_SET, menuHorizontalPosition == 1 ? attr : 0);
1104 if (attr) {
1105 if (moduleData.failsafeMode != FAILSAFE_CUSTOM)
1106 menuHorizontalPosition = 0;
1107 if (menuHorizontalPosition == 0) {
1108 if (editMode > 0 || p1valdiff) {
1109 CHECK_INCDEC_MODELVAR_ZERO(event, moduleData.failsafeMode, FAILSAFE_LAST);
1110 if (checkIncDec_Ret) SEND_FAILSAFE_NOW(moduleIdx);
1112 } else if (menuHorizontalPosition == 1) {
1113 s_editMode = 0;
1114 if (moduleData.failsafeMode == FAILSAFE_CUSTOM && event == EVT_KEY_FIRST(KEY_ENTER)) {
1115 g_moduleIdx = moduleIdx;
1116 pushMenu(menuModelFailsafe);
1118 } else {
1119 lcdDrawSolidFilledRect(MODEL_SETUP_2ND_COLUMN, y, LCD_W - MODEL_SETUP_2ND_COLUMN, 8);
1123 break;
1125 case ITEM_MODEL_EXTERNAL_MODULE_OPTIONS:
1127 uint8_t moduleIdx = CURRENT_MODULE_EDITED(k);
1128 #if defined(MULTIMODULE)
1129 if (IS_MODULE_MULTIMODULE(moduleIdx)) {
1130 int optionValue = g_model.moduleData[moduleIdx].multi.optionValue;
1132 const uint8_t multi_proto = g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(true);
1133 const mm_protocol_definition * pdef = getMultiProtocolDefinition(multi_proto);
1134 if (pdef->optionsstr)
1135 lcdDrawTextAlignedLeft(y, pdef->optionsstr);
1137 if (multi_proto == MM_RF_PROTO_FS_AFHDS2A)
1138 optionValue = 50 + 5 * optionValue;
1140 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, optionValue, LEFT | attr);
1141 if (attr) {
1142 if (multi_proto == MM_RF_PROTO_FS_AFHDS2A) {
1143 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].multi.optionValue, 0, 70);
1145 else if (multi_proto == MM_RF_PROTO_OLRS) {
1146 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].multi.optionValue, -1, 7);
1148 else {
1149 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].multi.optionValue, -128, 127);
1153 #endif
1154 if (IS_MODULE_R9M_FCC(moduleIdx)) {
1155 if (IS_TELEMETRY_INTERNAL_MODULE()) {
1156 lcdDrawTextAlignedLeft(y, STR_MODULE_TELEMETRY);
1157 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_DISABLE_INTERNAL);
1159 else {
1160 g_model.moduleData[moduleIdx].pxx.sport_out = editCheckBox(g_model.moduleData[EXTERNAL_MODULE].pxx.sport_out, MODEL_SETUP_2ND_COLUMN, y, STR_MODULE_TELEMETRY, attr, event);
1163 else if (IS_MODULE_R9M_LBT(moduleIdx)) {
1164 if (IS_TELEMETRY_INTERNAL_MODULE()) {
1165 lcdDrawTextAlignedLeft(y, STR_MODULE_TELEMETRY);
1166 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_DISABLE_INTERNAL);
1168 else {
1169 lcdDrawTextAlignedLeft(y, STR_MODULE_TELEMETRY);
1170 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_BINDING_OPTION);
1173 else if (IS_MODULE_SBUS(moduleIdx)) {
1174 lcdDrawTextAlignedLeft(y, STR_WARN_BATTVOLTAGE);
1175 putsVolts(lcdLastRightPos, y, getBatteryVoltage(), attr | PREC2 | LEFT);
1177 break;
1180 case ITEM_MODEL_EXTERNAL_MODULE_POWER:
1182 uint8_t moduleIdx = CURRENT_MODULE_EDITED(k);
1183 if (IS_MODULE_R9M_FCC(moduleIdx)) {
1184 // Power selection is only available on R9M FCC
1185 lcdDrawTextAlignedLeft(y, TR_MULTI_RFPOWER);
1186 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_R9M_FCC_POWER_VALUES, g_model.moduleData[moduleIdx].pxx.power, LEFT | attr);
1187 if (attr)
1188 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].pxx.power, 0, R9M_FCC_POWER_MAX);
1190 #if defined(MULTIMODULE)
1191 else if (IS_MODULE_MULTIMODULE(moduleIdx)) {
1192 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);
1194 #endif
1197 break;
1199 #if defined(MULTIMODULE)
1200 case ITEM_MODEL_EXTERNAL_MODULE_AUTOBIND:
1201 if (g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(true) == MM_RF_PROTO_DSM2)
1202 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);
1203 else
1204 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);
1205 break;
1207 case ITEM_MODEL_EXTERNAL_MODULE_STATUS: {
1208 lcdDrawTextAlignedLeft(y, STR_MODULE_STATUS);
1210 char statusText[64];
1211 multiModuleStatus.getStatusString(statusText);
1212 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, statusText);
1213 break;
1216 case ITEM_MODEL_EXTERNAL_MODULE_SYNCSTATUS: {
1217 lcdDrawTextAlignedLeft(y, STR_MODULE_SYNC);
1219 char statusText[64];
1220 multiSyncStatus.getRefreshString(statusText);
1221 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, statusText);
1222 break;
1224 #endif
1225 #endif
1227 #if !defined(CPUARM)
1228 case ITEM_MODEL_PPM1_PROTOCOL:
1229 lcdDrawTextAlignedLeft(y, NO_INDENT(STR_PROTO));
1230 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_VPROTOS, protocol, menuHorizontalPosition<=0 ? attr : 0);
1231 if (IS_PPM_PROTOCOL(protocol)) {
1232 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN+7*FW, y, STR_NCHANNELS, g_model.ppmNCH+2, menuHorizontalPosition!=0 ? attr : 0);
1234 else if (menuHorizontalPosition>0 && attr) {
1235 MOVE_CURSOR_FROM_HERE();
1237 if (attr && (editMode>0 || p1valdiff || (!IS_PPM_PROTOCOL(protocol) && !IS_DSM2_PROTOCOL(protocol)))) {
1238 switch (menuHorizontalPosition) {
1239 case 0:
1240 CHECK_INCDEC_MODELVAR_ZERO(event, g_model.protocol, PROTO_MAX-1);
1241 break;
1242 case 1:
1243 CHECK_INCDEC_MODELVAR(event, g_model.ppmNCH, -2, 4);
1244 g_model.ppmFrameLength = g_model.ppmNCH * 8;
1245 break;
1248 break;
1249 #endif
1251 #if 0
1252 case ITEM_MODEL_PPM2_PROTOCOL:
1253 lcdDrawTextAlignedLeft(y, PSTR("Port2"));
1254 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_VPROTOS, 0, 0);
1255 lcdDrawText(MODEL_SETUP_2ND_COLUMN+4*FW+3, y, STR_CH, menuHorizontalPosition<=0 ? attr : 0);
1256 lcdDrawNumber(lcdLastRightPos, y, g_model.moduleData[1].channelsStart+1, LEFT | (menuHorizontalPosition<=0 ? attr : 0));
1257 lcdDrawChar(lcdLastRightPos, y, '-');
1258 lcdDrawNumber(lcdLastRightPos + FW+1, y, g_model.moduleData[1].channelsStart+8+g_model.moduleData[1].channelsCount, LEFT | (menuHorizontalPosition!=0 ? attr : 0));
1259 if (attr && (editMode>0 || p1valdiff)) {
1260 switch (menuHorizontalPosition) {
1261 case 0:
1262 CHECK_INCDEC_MODELVAR_ZERO(event, g_model.moduleData[1].channelsStart, 32-8-g_model.moduleData[1].channelsCount);
1263 SET_DEFAULT_PPM_FRAME_LENGTH(1);
1264 break;
1265 case 1:
1266 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[1].channelsCount, -4, min<int8_t>(8, 32-8-g_model.moduleData[1].channelsStart));
1267 SET_DEFAULT_PPM_FRAME_LENGTH(1);
1268 break;
1271 break;
1273 case ITEM_MODEL_PPM2_PARAMS:
1274 lcdDrawTextAlignedLeft(y, STR_PPMFRAME);
1275 lcdDrawText(MODEL_SETUP_2ND_COLUMN+3*FW, y, STR_MS);
1276 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, (int16_t)g_model.moduleData[1].ppmFrameLength*5 + 225, (menuHorizontalPosition<=0 ? attr : 0) | PREC1 | LEFT);
1277 lcdDrawChar(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, 'u');
1278 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, (g_model.moduleData[1].ppmDelay*50)+300, RIGHT | ((menuHorizontalPosition < 0 || menuHorizontalPosition==1) ? attr : 0));
1279 lcdDrawChar(MODEL_SETUP_2ND_COLUMN+10*FW, y, g_model.moduleData[1].ppmPulsePol ? '+' : '-', (menuHorizontalPosition < 0 || menuHorizontalPosition==2) ? attr : 0);
1280 if (attr && (editMode>0 || p1valdiff)) {
1281 switch (menuHorizontalPosition) {
1282 case 0:
1283 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[1].ppmFrameLength, -20, 35);
1284 break;
1285 case 1:
1286 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[1].ppmDelay, -4, 10);
1287 break;
1288 case 2:
1289 CHECK_INCDEC_MODELVAR_ZERO(event, g_model.moduleData[1].ppmPulsePol, 1);
1290 break;
1293 break;
1294 #endif
1296 #if !defined(CPUARM)
1297 case ITEM_MODEL_PPM1_PARAMS:
1298 if (IS_PPM_PROTOCOL(protocol)) {
1299 lcdDrawTextAlignedLeft(y, STR_PPMFRAME);
1300 lcdDrawText(MODEL_SETUP_2ND_COLUMN+3*FW, y, STR_MS);
1301 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, (int16_t)g_model.ppmFrameLength*5 + 225, (menuHorizontalPosition<=0 ? attr : 0) | PREC1|LEFT);
1302 lcdDrawChar(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, 'u');
1303 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, (g_model.ppmDelay*50)+300, (CURSOR_ON_LINE() || menuHorizontalPosition==1) ? attr : 0);
1304 lcdDrawChar(MODEL_SETUP_2ND_COLUMN+10*FW, y, g_model.pulsePol ? '+' : '-', (CURSOR_ON_LINE() || menuHorizontalPosition==2) ? attr : 0);
1305 if (attr && (editMode>0 || p1valdiff)) {
1306 switch (menuHorizontalPosition) {
1307 case 0:
1308 CHECK_INCDEC_MODELVAR(event, g_model.ppmFrameLength, -20, 35);
1309 break;
1310 case 1:
1311 CHECK_INCDEC_MODELVAR(event, g_model.ppmDelay, -4, 10);
1312 break;
1313 case 2:
1314 CHECK_INCDEC_MODELVAR_ZERO(event, g_model.pulsePol, 1);
1315 break;
1319 #if defined(DSM2) || defined(PXX)
1320 else if (IS_DSM2_PROTOCOL(protocol) || IS_PXX_PROTOCOL(protocol)) {
1321 if (attr && menuHorizontalPosition > 1) {
1322 REPEAT_LAST_CURSOR_MOVE(); // limit 3 column row to 2 colums (Rx_Num and RANGE fields)
1324 lcdDrawTextAlignedLeft(y, STR_RECEIVER_NUM);
1325 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, g_model.header.modelId[0], (menuHorizontalPosition<=0 ? attr : 0) | LEADING0|LEFT, 2);
1326 if (attr && (menuHorizontalPosition==0 && (editMode>0 || p1valdiff))) {
1327 CHECK_INCDEC_MODELVAR_ZERO(event, g_model.header.modelId[0], 99);
1329 #if defined(PXX)
1330 if (protocol == PROTO_PXX) {
1331 lcdDrawText(MODEL_SETUP_2ND_COLUMN+4*FW, y, STR_SYNCMENU, menuHorizontalPosition!=0 ? attr : 0);
1332 uint8_t newFlag = 0;
1333 if (attr && menuHorizontalPosition>0 && editMode>0) {
1334 // send reset code
1335 newFlag = MODULE_BIND;
1337 moduleFlag[0] = newFlag;
1339 #endif
1340 #if defined(DSM2)
1341 if (IS_DSM2_PROTOCOL(protocol)) {
1342 lcdDrawText(MODEL_SETUP_2ND_COLUMN+4*FW, y, STR_MODULE_RANGE, menuHorizontalPosition!=0 ? attr : 0);
1343 moduleFlag[0] = (attr && menuHorizontalPosition>0 && editMode>0) ? MODULE_RANGECHECK : 0; // [MENU] key toggles range check mode
1345 #endif
1347 #endif
1348 break;
1349 #endif
1353 #if defined(CPUARM) && defined(PXX)
1354 if (IS_RANGECHECK_ENABLE()) {
1355 showMessageBox("RSSI: ");
1356 lcdDrawNumber(16+4*FW, 5*FH, TELEMETRY_RSSI(), BOLD);
1358 #endif
1361 #if defined(CPUARM)
1362 void menuModelFailsafe(event_t event)
1364 uint8_t ch = 8 * (menuVerticalPosition / 8);
1365 const uint8_t channelStart = g_model.moduleData[g_moduleIdx].channelsStart;
1366 const int lim = (g_model.extendedLimits ? (512 * LIMIT_EXT_PERCENT / 100) : 512) * 2;
1367 uint8_t wbar = LCD_W - FW * 4 - FWNUM * 4;
1368 #if defined(PPM_UNIT_PERCENT_PREC1)
1369 wbar -= 6;
1370 #endif
1372 if (event == EVT_KEY_LONG(KEY_ENTER)) {
1373 killEvents(event);
1374 event = 0;
1375 if (s_editMode) {
1376 g_model.moduleData[g_moduleIdx].failsafeChannels[menuVerticalPosition] = channelOutputs[menuVerticalPosition+channelStart];
1377 storageDirty(EE_MODEL);
1378 AUDIO_WARNING1();
1379 s_editMode = 0;
1380 SEND_FAILSAFE_NOW(g_moduleIdx);
1382 else {
1383 int16_t & failsafe = g_model.moduleData[g_moduleIdx].failsafeChannels[menuVerticalPosition];
1384 if (failsafe < FAILSAFE_CHANNEL_HOLD)
1385 failsafe = FAILSAFE_CHANNEL_HOLD;
1386 else if (failsafe == FAILSAFE_CHANNEL_HOLD)
1387 failsafe = FAILSAFE_CHANNEL_NOPULSE;
1388 else
1389 failsafe = 0;
1390 storageDirty(EE_MODEL);
1391 AUDIO_WARNING1();
1392 SEND_FAILSAFE_NOW(g_moduleIdx);
1396 SIMPLE_SUBMENU_NOTITLE(NUM_CHANNELS(g_moduleIdx));
1398 lcdDrawTextAlignedCenter(0, FAILSAFESET);
1399 lcdInvertLine(0);
1401 const coord_t x = 1;
1403 // Channels
1404 for (uint8_t line=0; line<8; line++) {
1405 const coord_t y = 9+line*7;
1406 const int32_t channelValue = channelOutputs[ch+channelStart];
1407 int32_t failsafeValue = g_model.moduleData[g_moduleIdx].failsafeChannels[ch];
1409 //Channel
1410 putsChn(x+1, y, ch+1, SMLSIZE);
1412 // Value
1413 LcdFlags flags = TINSIZE;
1414 if (menuVerticalPosition == ch) {
1415 flags |= INVERS;
1416 if (s_editMode) {
1417 if (failsafeValue == FAILSAFE_CHANNEL_HOLD || failsafeValue == FAILSAFE_CHANNEL_NOPULSE) {
1418 s_editMode = 0;
1420 else {
1421 flags |= BLINK;
1422 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[g_moduleIdx].failsafeChannels[ch], -lim, +lim);
1427 uint8_t xValue = x+LCD_W-4-wbar;
1428 if (failsafeValue == FAILSAFE_CHANNEL_HOLD) {
1429 lcdDrawText(xValue, y, STR_HOLD, RIGHT|flags);
1430 failsafeValue = 0;
1432 else if (failsafeValue == FAILSAFE_CHANNEL_NOPULSE) {
1433 lcdDrawText(xValue, y, STR_NONE, RIGHT|flags);
1434 failsafeValue = 0;
1436 else {
1437 #if defined(PPM_UNIT_US)
1438 lcdDrawNumber(xValue, y, PPM_CH_CENTER(ch)+failsafeValue/2, RIGHT|flags);
1439 #elif defined(PPM_UNIT_PERCENT_PREC1)
1440 lcdDrawNumber(xValue, y, calcRESXto1000(failsafeValue), RIGHT|PREC1|flags);
1441 #else
1442 lcdDrawNumber(xValue, y, calcRESXto1000(failsafeValue)/10, RIGHT|flags);
1443 #endif
1446 // Gauge
1447 #if !defined(PCBX7) // X7 LCD doesn't like too many horizontal lines
1448 lcdDrawRect(x+LCD_W-3-wbar, y, wbar+1, 6);
1449 #endif
1450 const uint8_t lenChannel = limit<uint8_t>(1, (abs(channelValue) * wbar/2 + lim/2) / lim, wbar/2);
1451 const uint8_t lenFailsafe = limit<uint8_t>(1, (abs(failsafeValue) * wbar/2 + lim/2) / lim, wbar/2);
1452 const coord_t xChannel = (channelValue>0) ? x+LCD_W-3-wbar/2 : x+LCD_W-2-wbar/2-lenChannel;
1453 const coord_t xFailsafe = (failsafeValue>0) ? x+LCD_W-3-wbar/2 : x+LCD_W-2-wbar/2-lenFailsafe;
1454 lcdDrawHorizontalLine(xChannel, y+1, lenChannel, DOTTED, 0);
1455 lcdDrawHorizontalLine(xChannel, y+2, lenChannel, DOTTED, 0);
1456 lcdDrawSolidHorizontalLine(xFailsafe, y+3, lenFailsafe);
1457 lcdDrawSolidHorizontalLine(xFailsafe, y+4, lenFailsafe);
1459 if (++ch >= NUM_CHANNELS(g_moduleIdx))
1460 break;
1464 #endif