Start sky9x fixes
[opentx.git] / radio / src / gui / 128x64 / model_setup.cpp
blob8890a49036fd47caead9ba2616c949d124e3f988
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 // TODO find why we need this (for REGISTER at least)
24 #if defined(PCBXLITE)
25 #define EVT_BUTTON_PRESSED() EVT_KEY_FIRST(KEY_ENTER)
26 #else
27 #define EVT_BUTTON_PRESSED() EVT_KEY_BREAK(KEY_ENTER)
28 #endif
30 uint8_t g_moduleIdx;
32 #if defined(PCBTARANIS)
33 uint8_t getSwitchWarningsCount()
35 uint8_t count = 0;
36 for (int i=0; i<NUM_SWITCHES; ++i) {
37 if (SWITCH_WARNING_ALLOWED(i)) {
38 ++count;
41 return count;
43 #endif
45 enum MenuModelSetupItems {
46 ITEM_MODEL_SETUP_NAME,
47 ITEM_MODEL_SETUP_TIMER1,
48 ITEM_MODEL_SETUP_TIMER1_NAME,
49 ITEM_MODEL_SETUP_TIMER1_PERSISTENT,
50 ITEM_MODEL_SETUP_TIMER1_MINUTE_BEEP,
51 ITEM_MODEL_SETUP_TIMER1_COUNTDOWN_BEEP,
52 ITEM_MODEL_SETUP_TIMER2,
53 ITEM_MODEL_SETUP_TIMER2_NAME,
54 ITEM_MODEL_SETUP_TIMER2_PERSISTENT,
55 ITEM_MODEL_SETUP_TIMER2_MINUTE_BEEP,
56 ITEM_MODEL_SETUP_TIMER2_COUNTDOWN_BEEP,
57 ITEM_MODEL_SETUP_TIMER3,
58 ITEM_MODEL_SETUP_TIMER3_NAME,
59 ITEM_MODEL_SETUP_TIMER3_PERSISTENT,
60 ITEM_MODEL_SETUP_TIMER3_MINUTE_BEEP,
61 ITEM_MODEL_SETUP_TIMER3_COUNTDOWN_BEEP,
62 ITEM_MODEL_SETUP_EXTENDED_LIMITS,
63 ITEM_MODEL_SETUP_EXTENDED_TRIMS,
64 ITEM_MODEL_SETUP_DISPLAY_TRIMS,
65 ITEM_MODEL_SETUP_TRIM_INC,
66 ITEM_MODEL_SETUP_THROTTLE_REVERSED,
67 ITEM_MODEL_SETUP_THROTTLE_TRACE,
68 ITEM_MODEL_SETUP_THROTTLE_TRIM,
69 ITEM_MODEL_SETUP_PREFLIGHT_LABEL,
70 ITEM_MODEL_SETUP_CHECKLIST_DISPLAY,
71 ITEM_MODEL_SETUP_THROTTLE_WARNING,
72 ITEM_MODEL_SETUP_SWITCHES_WARNING1,
73 #if defined(PCBTARANIS)
74 ITEM_MODEL_SETUP_SWITCHES_WARNING2,
75 ITEM_MODEL_SETUP_POTS_WARNING,
76 #endif
77 ITEM_MODEL_SETUP_BEEP_CENTER,
78 ITEM_MODEL_SETUP_USE_GLOBAL_FUNCTIONS,
80 #if defined(PXX2)
81 ITEM_MODEL_SETUP_REGISTRATION_ID,
82 #endif
84 #if defined(HARDWARE_INTERNAL_MODULE)
85 ITEM_MODEL_SETUP_INTERNAL_MODULE_LABEL,
86 ITEM_MODEL_SETUP_INTERNAL_MODULE_TYPE,
87 ITEM_MODEL_SETUP_INTERNAL_MODULE_CHANNELS,
88 ITEM_MODEL_SETUP_INTERNAL_MODULE_NOT_ACCESS_BIND,
89 ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_MODEL_NUM,
90 #if defined(INTERNAL_MODULE_PXX1) && defined(EXTERNAL_ANTENNA)
91 ITEM_MODEL_SETUP_INTERNAL_MODULE_ANTENNA,
92 #endif
93 ITEM_MODEL_SETUP_INTERNAL_MODULE_FAILSAFE,
94 ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_REGISTER_RANGE,
95 ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_OPTIONS,
96 ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_1,
97 ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_2,
98 ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_3,
99 #endif
101 ITEM_MODEL_SETUP_EXTERNAL_MODULE_LABEL,
102 ITEM_MODEL_SETUP_EXTERNAL_MODULE_TYPE,
103 #if defined(MULTIMODULE)
104 ITEM_MODEL_SETUP_EXTERNAL_MODULE_SUBTYPE,
105 #endif
106 #if defined(MULTIMODULE)
107 ITEM_MODEL_SETUP_EXTERNAL_MODULE_STATUS,
108 ITEM_MODEL_SETUP_EXTERNAL_MODULE_SYNCSTATUS,
109 #endif
110 ITEM_MODEL_SETUP_EXTERNAL_MODULE_CHANNELS,
111 ITEM_MODEL_SETUP_EXTERNAL_MODULE_NOT_ACCESS_BIND,
112 ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_MODEL_NUM,
113 #if defined(PCBSKY9X) && defined(REVX)
114 ITEM_MODEL_SETUP_EXTERNAL_MODULE_OUTPUT_TYPE,
115 #endif
116 ITEM_MODEL_SETUP_EXTERNAL_MODULE_POWER,
117 ITEM_MODEL_SETUP_EXTERNAL_MODULE_OPTIONS,
118 #if defined(MULTIMODULE)
119 ITEM_MODEL_SETUP_EXTERNAL_MODULE_AUTOBIND,
120 ITEM_MODEL_SETUP_EXTERNAL_MODULE_DISABLE_TELEM,
121 ITEM_MODEL_SETUP_EXTERNAL_MODULE_DISABLE_MAPPING,
122 #endif
123 ITEM_MODEL_SETUP_EXTERNAL_MODULE_FAILSAFE,
124 ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_REGISTER_RANGE,
125 ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_OPTIONS,
126 ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_1,
127 ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_2,
128 ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_3,
130 #if defined(PCBSKY9X)
131 ITEM_MODEL_SETUP_EXTRA_MODULE_LABEL,
132 ITEM_MODEL_SETUP_EXTRA_MODULE_CHANNELS,
133 ITEM_MODEL_SETUP_EXTRA_MODULE_BIND,
134 #endif
136 #if defined(PCBTARANIS)
137 ITEM_MODEL_SETUP_TRAINER_LABEL,
138 ITEM_MODEL_SETUP_TRAINER_MODE,
139 #if defined(BLUETOOTH)
140 ITEM_MODEL_SETUP_TRAINER_BLUETOOTH,
141 #endif
142 ITEM_MODEL_SETUP_TRAINER_CHANNELS,
143 ITEM_MODEL_SETUP_TRAINER_PPM_PARAMS,
144 #endif
145 ITEM_MODEL_SETUP_LINES_COUNT
148 #define MODEL_SETUP_2ND_COLUMN (LCD_W-11*FW)
149 #define MODEL_SETUP_BIND_OFS 2*FW+1
150 #define MODEL_SETUP_RANGE_OFS 4*FW+3
151 #define MODEL_SETUP_SET_FAILSAFE_OFS 7*FW-2
153 #define IF_PXX2_MODULE(module, xxx) (isModulePXX2(module) ? (uint8_t)(xxx) : HIDDEN_ROW)
154 #define IF_NOT_PXX2_MODULE(module, xxx) (isModulePXX2(module) ? HIDDEN_ROW : (uint8_t)(xxx))
155 #define IF_ACCESS_MODULE_RF(module, xxx) (isModuleRFAccess(module) ? (uint8_t)(xxx) : HIDDEN_ROW)
156 #define IF_NOT_ACCESS_MODULE_RF(module, xxx) (isModuleRFAccess(module) ? HIDDEN_ROW : (uint8_t)(xxx))
158 #if defined(PXX2)
159 #define REGISTRATION_ID_ROWS uint8_t((isDefaultModelRegistrationID() || (warningText && popupFunc == runPopupRegister)) ? HIDDEN_ROW : READONLY_ROW),
160 #else
161 #define REGISTRATION_ID_ROWS
162 #endif
164 #if defined(HARDWARE_INTERNAL_MODULE)
165 #define CURRENT_MODULE_EDITED(k) (k >= ITEM_MODEL_SETUP_EXTERNAL_MODULE_LABEL ? EXTERNAL_MODULE : INTERNAL_MODULE)
166 #define CURRENT_RECEIVER_EDITED(k) (k - (k >= ITEM_MODEL_SETUP_EXTERNAL_MODULE_LABEL ? ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_1 : ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_1))
167 #elif defined(PCBSKY9X)
168 #define CURRENT_MODULE_EDITED(k) (k >= ITEM_MODEL_SETUP_EXTRA_MODULE_LABEL ? EXTRA_MODULE : EXTERNAL_MODULE)
169 #else
170 #define CURRENT_MODULE_EDITED(k) (EXTERNAL_MODULE)
171 #define CURRENT_RECEIVER_EDITED(k) (k - ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_1)
172 #endif
174 #define MAX_SWITCH_PER_LINE (getSwitchWarningsCount() > 5 ? 4 : 5)
175 #if defined(PCBXLITE)
176 #define SW_WARN_ROWS uint8_t(NAVIGATION_LINE_BY_LINE|getSwitchWarningsCount()), uint8_t(getSwitchWarningsCount() > 4 ? TITLE_ROW : HIDDEN_ROW) // X-Lite needs an additional column for full line selection (<])
177 #else
178 #define SW_WARN_ROWS uint8_t(NAVIGATION_LINE_BY_LINE|(getSwitchWarningsCount()-1)), uint8_t(getSwitchWarningsCount() > MAX_SWITCH_PER_LINE ? TITLE_ROW : HIDDEN_ROW)
179 #endif
181 #if defined(INTERNAL_MODULE_PXX1)
182 #define INTERNAL_MODULE_TYPE_ROWS ((isModuleXJT(INTERNAL_MODULE) || isModulePXX2(INTERNAL_MODULE)) ? (uint8_t)1 : (uint8_t)0) // Module type + RF protocols
183 #else
184 #define INTERNAL_MODULE_TYPE_ROWS (0) // Module type + RF protocols
185 #endif
187 #if defined(PCBSKY9X) && defined(REVX)
188 #define OUTPUT_TYPE_ROW (isModulePPM(EXTERNAL_MODULE) ? (uint8_t)0 : HIDDEN_ROW),
189 #elif defined(PCBSKY9X)
190 #define OUTPUT_TYPE_ROW
191 #endif
193 inline uint8_t EXTERNAL_MODULE_TYPE_ROW()
195 if (isModuleXJT(EXTERNAL_MODULE) || isModuleR9MNonAccess(EXTERNAL_MODULE) || isModuleDSM2(EXTERNAL_MODULE))
196 return 1;
197 #if defined(MULTIMODULE)
198 else if (isModuleMultimodule(EXTERNAL_MODULE)) {
199 return 1;
201 #endif
202 else
203 return 0;
206 #define POT_WARN_ROWS ((g_model.potsWarnMode) ? (uint8_t)(NUM_POTS+NUM_SLIDERS) : (uint8_t)0)
207 #define TIMER_ROWS 2, 0, 0, 0, 0
209 #if defined(PCBSKY9X)
210 #define EXTRA_MODULE_ROWS LABEL(ExtraModule), 1, 2,
211 #else
212 #define EXTRA_MODULE_ROWS
213 #endif
215 #define TRAINER_CHANNELS_ROW (IS_SLAVE_TRAINER() ? (IS_BLUETOOTH_TRAINER() ? (uint8_t)0 : (uint8_t)1) : HIDDEN_ROW)
216 #define TRAINER_PPM_PARAMS_ROW (g_model.trainerData.mode == TRAINER_MODE_SLAVE ? (uint8_t)2 : HIDDEN_ROW)
217 #define TRAINER_BLUETOOTH_M_ROW ((bluetooth.distantAddr[0] == '\0' || bluetooth.state == BLUETOOTH_STATE_CONNECTED) ? (uint8_t)0 : (uint8_t)1)
218 #define TRAINER_BLUETOOTH_S_ROW (bluetooth.distantAddr[0] == '\0' ? HIDDEN_ROW : LABEL())
219 #define IF_BT_TRAINER_ON(x) (g_eeGeneral.bluetoothMode == BLUETOOTH_TRAINER ? (uint8_t)(x) : HIDDEN_ROW)
221 #if defined(INTERNAL_MODULE_PXX1) && defined(EXTERNAL_ANTENNA)
222 #define EXTERNAL_ANTENNA_ROW ((isModuleXJT(INTERNAL_MODULE) && g_eeGeneral.antennaMode == ANTENNA_MODE_PER_MODEL) ? (uint8_t)0 : HIDDEN_ROW),
223 void onModelAntennaSwitchConfirm(const char * result)
225 if (result == STR_OK) {
226 // Switch to external antenna confirmation
227 g_model.moduleData[INTERNAL_MODULE].pxx.antennaMode = ANTENNA_MODE_EXTERNAL;
228 globalData.externalAntennaEnabled = true;
229 storageDirty(EE_MODEL);
231 else {
232 reusableBuffer.moduleSetup.antennaMode = g_model.moduleData[INTERNAL_MODULE].pxx.antennaMode;
235 #else
236 #define EXTERNAL_ANTENNA_ROW
237 #endif
239 #if defined(PCBX7) || defined(PCBX9LITE)
240 #if defined(BLUETOOTH)
241 #define TRAINER_BLUETOOTH_ROW (g_model.trainerData.mode == TRAINER_MODE_MASTER_BLUETOOTH ? TRAINER_BLUETOOTH_M_ROW : (g_model.trainerData.mode == TRAINER_MODE_SLAVE_BLUETOOTH ? TRAINER_BLUETOOTH_S_ROW : HIDDEN_ROW)),
242 #else
243 #define TRAINER_BLUETOOTH_ROW
244 #endif
245 #define TRAINER_PPM_PARAMS_ROW (g_model.trainerData.mode == TRAINER_MODE_SLAVE ? (uint8_t)2 : HIDDEN_ROW)
246 #define TRAINER_ROWS LABEL(Trainer), 0, TRAINER_BLUETOOTH_ROW TRAINER_CHANNELS_ROW, TRAINER_PPM_PARAMS_ROW
247 #elif defined(PCBXLITES)
248 #define TRAINER_BLUETOOTH_ROW (g_model.trainerData.mode == TRAINER_MODE_MASTER_BLUETOOTH ? TRAINER_BLUETOOTH_M_ROW : (g_model.trainerData.mode == TRAINER_MODE_SLAVE_BLUETOOTH ? TRAINER_BLUETOOTH_S_ROW : HIDDEN_ROW))
249 #define TRAINER_PPM_PARAMS_ROW (g_model.trainerData.mode == TRAINER_MODE_SLAVE ? (uint8_t)2 : HIDDEN_ROW)
250 #define TRAINER_ROWS LABEL(Trainer), 0, IF_BT_TRAINER_ON(TRAINER_BLUETOOTH_ROW), TRAINER_CHANNELS_ROW, TRAINER_PPM_PARAMS_ROW
251 #elif defined(PCBXLITE)
252 #define TRAINER_BLUETOOTH_ROW (g_model.trainerData.mode == TRAINER_MODE_MASTER_BLUETOOTH ? TRAINER_BLUETOOTH_M_ROW : (g_model.trainerData.mode == TRAINER_MODE_SLAVE_BLUETOOTH ? TRAINER_BLUETOOTH_S_ROW : HIDDEN_ROW))
253 #define TRAINER_ROWS IF_BT_TRAINER_ON(LABEL(Trainer)), IF_BT_TRAINER_ON(0), IF_BT_TRAINER_ON(TRAINER_BLUETOOTH_ROW), IF_BT_TRAINER_ON(TRAINER_CHANNELS_ROW), HIDDEN_ROW /* xlite has only BT trainer, so never PPM */
254 #else
255 #define TRAINER_ROWS
256 #endif
258 #if defined(BLUETOOTH)
259 void onBluetoothConnectMenu(const char * result)
261 if (result != STR_EXIT) {
262 uint8_t index = (result - reusableBuffer.moduleSetup.bt.devices[0]) / sizeof(reusableBuffer.moduleSetup.bt.devices[0]);
263 strncpy(bluetooth.distantAddr, reusableBuffer.moduleSetup.bt.devices[index], LEN_BLUETOOTH_ADDR);
264 bluetooth.state = BLUETOOTH_STATE_BIND_REQUESTED;
266 else {
267 reusableBuffer.moduleSetup.bt.devicesCount = 0;
268 bluetooth.state = BLUETOOTH_STATE_DISCOVER_END;
271 #endif
273 #include "common/stdlcd/model_setup_pxx1.cpp"
275 #if defined(PXX2)
276 #include "common/stdlcd/model_setup_pxx2.cpp"
277 #endif
279 #if defined(HARDWARE_INTERNAL_MODULE)
280 #define INTERNAL_MODULE_ROWS \
281 LABEL(InternalModule), \
282 INTERNAL_MODULE_TYPE_ROWS, \
283 MODULE_CHANNELS_ROWS(INTERNAL_MODULE), \
284 IF_NOT_ACCESS_MODULE_RF(INTERNAL_MODULE, IF_INTERNAL_MODULE_ON(isModuleRxNumAvailable(INTERNAL_MODULE) ? (uint8_t)2 : (uint8_t)1)), \
285 IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 0), /* RxNum */ \
286 EXTERNAL_ANTENNA_ROW \
287 IF_INTERNAL_MODULE_ON(FAILSAFE_ROWS(INTERNAL_MODULE)), /* Failsafe */ \
288 IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 1), /* Range check and Register buttons */ \
289 IF_PXX2_MODULE(INTERNAL_MODULE, 0), /* Module options */ \
290 IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 0), /* Receiver 1 */ \
291 IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 0), /* Receiver 2 */ \
292 IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 0), /* Receiver 3 */
293 #else
294 #define INTERNAL_MODULE_ROWS
295 #endif
297 void menuModelSetup(event_t event)
299 int8_t old_editMode = s_editMode;
301 #if defined(PCBTARANIS)
302 int8_t old_posHorz = menuHorizontalPosition;
304 MENU_TAB({
305 HEADER_LINE_COLUMNS
307 TIMER_ROWS,
308 TIMER_ROWS,
309 TIMER_ROWS,
310 0, // Extended limits
311 1, // Extended trims
312 0, // Show trims
313 0, // Trims step
314 0, // Throttle reverse
315 0, // Throttle trace source
316 0, // Throttle trim
318 LABEL(PreflightCheck),
319 0, // Checklist
320 0, // Throttle warning
321 SW_WARN_ROWS, // Switch warning
322 POT_WARN_ROWS, // Pot warning
324 NUM_STICKS + NUM_POTS + NUM_SLIDERS - 1, // Center beeps
325 0, // Global functions
327 REGISTRATION_ID_ROWS
329 INTERNAL_MODULE_ROWS
331 LABEL(ExternalModule),
332 EXTERNAL_MODULE_TYPE_ROW(),
333 MULTIMODULE_SUBTYPE_ROWS(EXTERNAL_MODULE)
334 MULTIMODULE_STATUS_ROWS(EXTERNAL_MODULE)
335 MODULE_CHANNELS_ROWS(EXTERNAL_MODULE),
336 IF_NOT_ACCESS_MODULE_RF(EXTERNAL_MODULE, MODULE_BIND_ROWS(EXTERNAL_MODULE)), // line reused for PPM: PPM settings
337 IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // RxNum
338 MODULE_POWER_ROW(EXTERNAL_MODULE),
339 IF_NOT_PXX2_MODULE(EXTERNAL_MODULE, MODULE_OPTION_ROW(EXTERNAL_MODULE)),
340 MULTIMODULE_MODULE_ROWS(EXTERNAL_MODULE)
341 FAILSAFE_ROWS(EXTERNAL_MODULE),
342 IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 1), // Range check and Register buttons
343 IF_PXX2_MODULE(EXTERNAL_MODULE, 0), // Module options
344 IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // Receiver 1
345 IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // Receiver 2
346 IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // Receiver 3
348 TRAINER_ROWS
350 #else
351 MENU_TAB({
352 HEADER_LINE_COLUMNS
354 TIMER_ROWS,
355 TIMER_ROWS,
356 TIMER_ROWS,
357 0, // Extended limits
358 1, // Extended trims
359 0, // Show trims
360 0, // Trims step
361 0, // Throttle reverse
362 0, // Throttle trace source
363 0, // Throttle trim
365 LABEL(PreflightCheck),
366 0, // Checklist
367 0, // Throttle warning
368 NUM_SWITCHES-1, // Switch warning
370 NUM_STICKS+NUM_POTS+NUM_SLIDERS-1, // Center beeps
371 0, // Global functions
373 LABEL(ExternalModule),
374 EXTERNAL_MODULE_TYPE_ROW(),
375 MULTIMODULE_SUBTYPE_ROWS(EXTERNAL_MODULE)
376 MULTIMODULE_STATUS_ROWS(EXTERNAL_MODULE)
377 MODULE_CHANNELS_ROWS(EXTERNAL_MODULE),
378 IF_NOT_ACCESS_MODULE_RF(EXTERNAL_MODULE, MODULE_BIND_ROWS(EXTERNAL_MODULE)), // line reused for PPM: PPM settings
379 IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // RxNum
380 MODULE_POWER_ROW(EXTERNAL_MODULE),
381 IF_NOT_PXX2_MODULE(EXTERNAL_MODULE, MODULE_OPTION_ROW(EXTERNAL_MODULE)),
382 MULTIMODULE_MODULE_ROWS(EXTERNAL_MODULE)
383 FAILSAFE_ROWS(EXTERNAL_MODULE),
384 IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 1), // Range check and Register buttons
385 IF_PXX2_MODULE(EXTERNAL_MODULE, 0), // Module options
386 IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // Receiver 1
387 IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // Receiver 2
388 IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // Receiver 3
390 EXTRA_MODULE_ROWS
391 TRAINER_ROWS
393 #endif
395 MENU_CHECK(menuTabModel, MENU_MODEL_SETUP, HEADER_LINE + ITEM_MODEL_SETUP_LINES_COUNT);
396 title(STR_MENUSETUP);
398 if (event == EVT_ENTRY || event == EVT_ENTRY_UP) {
399 memclear(&reusableBuffer.moduleSetup, sizeof(reusableBuffer.moduleSetup));
400 reusableBuffer.moduleSetup.r9mPower = g_model.moduleData[EXTERNAL_MODULE].pxx.power;
401 reusableBuffer.moduleSetup.previousType = g_model.moduleData[EXTERNAL_MODULE].type;
402 reusableBuffer.moduleSetup.newType = g_model.moduleData[EXTERNAL_MODULE].type;
403 #if defined(INTERNAL_MODULE_PXX1) && defined(EXTERNAL_ANTENNA)
404 reusableBuffer.moduleSetup.antennaMode = g_model.moduleData[INTERNAL_MODULE].pxx.antennaMode;
405 #endif
408 #if (defined(DSM2) || defined(PXX))
409 if (menuEvent) {
410 moduleState[0].mode = 0;
411 #if NUM_MODULES > 1
412 moduleState[1].mode = 0;
413 #endif
415 #endif
417 uint8_t sub = menuVerticalPosition - HEADER_LINE;
419 for (uint8_t i=0; i<NUM_BODY_LINES; ++i) {
420 coord_t y = MENU_HEADER_HEIGHT + 1 + i*FH;
421 uint8_t k = i + menuVerticalOffset;
422 for (int j=0; j<=k; j++) {
423 if (mstate_tab[j+HEADER_LINE] == HIDDEN_ROW) {
424 if (++k >= (int)DIM(mstate_tab)) {
425 return;
430 LcdFlags blink = ((s_editMode>0) ? BLINK|INVERS : INVERS);
431 LcdFlags attr = (sub == k ? blink : 0);
433 switch (k) {
434 case ITEM_MODEL_SETUP_NAME:
435 editSingleName(MODEL_SETUP_2ND_COLUMN, y, STR_MODELNAME, g_model.header.name, sizeof(g_model.header.name), event, attr);
436 memcpy(modelHeaders[g_eeGeneral.currModel].name, g_model.header.name, sizeof(g_model.header.name));
437 break;
439 case ITEM_MODEL_SETUP_TIMER1:
440 case ITEM_MODEL_SETUP_TIMER2:
441 case ITEM_MODEL_SETUP_TIMER3:
443 unsigned int timerIdx = (k>=ITEM_MODEL_SETUP_TIMER3 ? 2 : (k>=ITEM_MODEL_SETUP_TIMER2 ? 1 : 0));
444 TimerData * timer = &g_model.timers[timerIdx];
445 drawStringWithIndex(0*FW, y, STR_TIMER, timerIdx+1);
446 drawTimerMode(MODEL_SETUP_2ND_COLUMN, y, timer->mode, menuHorizontalPosition==0 ? attr : 0);
447 drawTimer(MODEL_SETUP_2ND_COLUMN+5*FW-2+5*FWNUM+1, y, timer->start, RIGHT | (menuHorizontalPosition==1 ? attr : 0), menuHorizontalPosition==2 ? attr : 0);
448 if (attr && s_editMode > 0) {
449 div_t qr = div(timer->start, 60);
450 switch (menuHorizontalPosition) {
451 case 0:
453 swsrc_t timerMode = timer->mode;
454 if (timerMode < 0)
455 timerMode -= TMRMODE_COUNT-1;
456 CHECK_INCDEC_MODELVAR_CHECK(event, timerMode, -TMRMODE_COUNT-SWSRC_LAST+1, TMRMODE_COUNT+SWSRC_LAST-1, isSwitchAvailableInTimers);
457 if (timerMode < 0)
458 timerMode += TMRMODE_COUNT-1;
459 timer->mode = timerMode;
460 #if defined(AUTOSWITCH)
461 if (s_editMode>0) {
462 int8_t val = timer->mode - (TMRMODE_COUNT-1);
463 int8_t switchVal = checkIncDecMovedSwitch(val);
464 if (val != switchVal) {
465 timer->mode = switchVal + (TMRMODE_COUNT-1);
466 storageDirty(EE_MODEL);
469 #endif
470 break;
472 case 1:
473 CHECK_INCDEC_MODELVAR_ZERO(event, qr.quot, 539); // 8:59
474 timer->start = qr.rem + qr.quot*60;
475 break;
476 case 2:
477 qr.rem -= checkIncDecModel(event, qr.rem+2, 1, 62)-2;
478 timer->start -= qr.rem ;
479 if ((int16_t)timer->start < 0) timer->start=0;
480 if ((int16_t)timer->start > 5999) timer->start=32399; // 8:59:59
481 break;
484 break;
487 case ITEM_MODEL_SETUP_TIMER1_NAME:
488 case ITEM_MODEL_SETUP_TIMER2_NAME:
489 case ITEM_MODEL_SETUP_TIMER3_NAME:
491 TimerData * timer = &g_model.timers[k>=ITEM_MODEL_SETUP_TIMER3 ? 2 : (k>=ITEM_MODEL_SETUP_TIMER2 ? 1 : 0)];
492 editSingleName(MODEL_SETUP_2ND_COLUMN, y, INDENT TR_NAME, timer->name, sizeof(timer->name), event, attr);
493 break;
496 case ITEM_MODEL_SETUP_TIMER1_MINUTE_BEEP:
497 case ITEM_MODEL_SETUP_TIMER2_MINUTE_BEEP:
498 case ITEM_MODEL_SETUP_TIMER3_MINUTE_BEEP:
500 TimerData * timer = &g_model.timers[k>=ITEM_MODEL_SETUP_TIMER3 ? 2 : (k>=ITEM_MODEL_SETUP_TIMER2 ? 1 : 0)];
501 timer->minuteBeep = editCheckBox(timer->minuteBeep, MODEL_SETUP_2ND_COLUMN, y, INDENT TR_MINUTEBEEP, attr, event);
502 break;
505 case ITEM_MODEL_SETUP_TIMER1_COUNTDOWN_BEEP:
506 case ITEM_MODEL_SETUP_TIMER2_COUNTDOWN_BEEP:
507 case ITEM_MODEL_SETUP_TIMER3_COUNTDOWN_BEEP:
509 TimerData * timer = &g_model.timers[k>=ITEM_MODEL_SETUP_TIMER3 ? 2 : (k>=ITEM_MODEL_SETUP_TIMER2 ? 1 : 0)];
510 timer->countdownBeep = editChoice(MODEL_SETUP_2ND_COLUMN, y, STR_BEEPCOUNTDOWN, STR_VBEEPCOUNTDOWN, timer->countdownBeep, COUNTDOWN_SILENT, COUNTDOWN_COUNT-1, attr, event);
511 break;
514 case ITEM_MODEL_SETUP_TIMER1_PERSISTENT:
515 case ITEM_MODEL_SETUP_TIMER2_PERSISTENT:
516 case ITEM_MODEL_SETUP_TIMER3_PERSISTENT:
518 TimerData * timer = &g_model.timers[k>=ITEM_MODEL_SETUP_TIMER3 ? 2 : (k>=ITEM_MODEL_SETUP_TIMER2 ? 1 : 0)];
519 timer->persistent = editChoice(MODEL_SETUP_2ND_COLUMN, y, STR_PERSISTENT, STR_VPERSISTENT, timer->persistent, 0, 2, attr, event);
520 break;
523 case ITEM_MODEL_SETUP_EXTENDED_LIMITS:
524 ON_OFF_MENU_ITEM(g_model.extendedLimits, MODEL_SETUP_2ND_COLUMN, y, STR_ELIMITS, attr, event);
525 break;
527 case ITEM_MODEL_SETUP_EXTENDED_TRIMS:
528 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);
529 lcdDrawText(MODEL_SETUP_2ND_COLUMN+4*FW, y, STR_RESET_BTN, (menuHorizontalPosition>0 && !NO_HIGHLIGHT()) ? attr : 0);
530 if (attr && menuHorizontalPosition>0) {
531 s_editMode = 0;
532 if (event==EVT_KEY_LONG(KEY_ENTER)) {
533 START_NO_HIGHLIGHT();
534 for (uint8_t i=0; i<MAX_FLIGHT_MODES; i++) {
535 memclear(&g_model.flightModeData[i], TRIMS_ARRAY_SIZE);
537 storageDirty(EE_MODEL);
538 AUDIO_WARNING1();
541 break;
543 case ITEM_MODEL_SETUP_DISPLAY_TRIMS:
544 g_model.displayTrims = editChoice(MODEL_SETUP_2ND_COLUMN, y, STR_DISPLAY_TRIMS, STR_VDISPLAYTRIMS, g_model.displayTrims, 0, 2, attr, event);
545 break;
547 case ITEM_MODEL_SETUP_TRIM_INC:
548 g_model.trimInc = editChoice(MODEL_SETUP_2ND_COLUMN, y, STR_TRIMINC, STR_VTRIMINC, g_model.trimInc, -2, 2, attr, event);
549 break;
551 case ITEM_MODEL_SETUP_THROTTLE_REVERSED:
552 ON_OFF_MENU_ITEM(g_model.throttleReversed, MODEL_SETUP_2ND_COLUMN, y, STR_THROTTLEREVERSE, attr, event ) ;
553 break;
555 case ITEM_MODEL_SETUP_THROTTLE_TRACE:
557 lcdDrawTextAlignedLeft(y, STR_TTRACE);
558 if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, g_model.thrTraceSrc, NUM_POTS+NUM_SLIDERS+MAX_OUTPUT_CHANNELS);
559 uint8_t idx = g_model.thrTraceSrc + MIXSRC_Thr;
560 if (idx > MIXSRC_Thr)
561 idx += 1;
562 if (idx >= MIXSRC_FIRST_POT+NUM_POTS+NUM_SLIDERS)
563 idx += MIXSRC_CH1 - MIXSRC_FIRST_POT - NUM_POTS - NUM_SLIDERS;
564 drawSource(MODEL_SETUP_2ND_COLUMN, y, idx, attr);
565 break;
568 case ITEM_MODEL_SETUP_THROTTLE_TRIM:
569 ON_OFF_MENU_ITEM(g_model.thrTrim, MODEL_SETUP_2ND_COLUMN, y, STR_TTRIM, attr, event);
570 break;
572 case ITEM_MODEL_SETUP_PREFLIGHT_LABEL:
573 lcdDrawTextAlignedLeft(y, STR_PREFLIGHT);
574 break;
576 case ITEM_MODEL_SETUP_CHECKLIST_DISPLAY:
577 ON_OFF_MENU_ITEM(g_model.displayChecklist, MODEL_SETUP_2ND_COLUMN, y, STR_CHECKLIST, attr, event);
578 break;
580 case ITEM_MODEL_SETUP_THROTTLE_WARNING:
581 g_model.disableThrottleWarning = !editCheckBox(!g_model.disableThrottleWarning, MODEL_SETUP_2ND_COLUMN, y, STR_THROTTLEWARNING, attr, event);
582 break;
584 #if defined(PCBTARANIS)
585 case ITEM_MODEL_SETUP_SWITCHES_WARNING2:
586 if (i==0) {
587 if (CURSOR_MOVED_LEFT(event))
588 menuVerticalOffset--;
589 else
590 menuVerticalOffset++;
592 break;
593 #endif
595 case ITEM_MODEL_SETUP_SWITCHES_WARNING1:
596 #if defined(PCBTARANIS)
598 #define FIRSTSW_STR STR_VSRCRAW+(MIXSRC_FIRST_SWITCH-MIXSRC_Rud+1)*length
599 uint8_t switchWarningsCount = getSwitchWarningsCount();
600 uint8_t length = STR_VSRCRAW[0];
601 horzpos_t l_posHorz = menuHorizontalPosition;
603 if (i>=NUM_BODY_LINES-2 && getSwitchWarningsCount() > MAX_SWITCH_PER_LINE*(NUM_BODY_LINES-i)) {
604 if (CURSOR_MOVED_LEFT(event))
605 menuVerticalOffset--;
606 else
607 menuVerticalOffset++;
608 break;
611 swarnstate_t states = g_model.switchWarningState;
612 char c;
614 lcdDrawTextAlignedLeft(y, STR_SWITCHWARNING);
615 #if defined(PCBXLITE)
616 lcdDrawText(LCD_W, y, "<]", RIGHT);
617 if (attr) {
618 if (menuHorizontalPosition > switchWarningsCount)
619 menuHorizontalPosition = switchWarningsCount;
621 if (attr && menuHorizontalPosition == switchWarningsCount) {
622 #else
623 if (attr) {
624 #endif
625 s_editMode = 0;
626 if (!READ_ONLY()) {
627 switch (event) {
628 case EVT_KEY_BREAK(KEY_ENTER):
629 break;
631 case EVT_KEY_LONG(KEY_ENTER):
632 if (menuHorizontalPosition < 0 || menuHorizontalPosition >= switchWarningsCount) {
633 START_NO_HIGHLIGHT();
634 getMovedSwitch();
635 g_model.switchWarningState = switches_states;
636 AUDIO_WARNING1();
637 storageDirty(EE_MODEL);
639 killEvents(event);
640 break;
645 int current = 0;
646 for (int i = 0; i < NUM_SWITCHES; i++) {
647 if (SWITCH_WARNING_ALLOWED(i)) {
648 div_t qr = div(current, MAX_SWITCH_PER_LINE);
649 if (!READ_ONLY() && event==EVT_KEY_BREAK(KEY_ENTER) && attr && l_posHorz == current && old_posHorz >= 0) {
650 g_model.switchWarningEnable ^= (1 << i);
651 storageDirty(EE_MODEL);
652 #if defined(PCBXLITE)
653 s_editMode = 0;
654 #endif
656 uint8_t swactive = !(g_model.switchWarningEnable & (1<<i));
657 c = "\300-\301"[states & 0x03];
658 // lcdDrawChar(MODEL_SETUP_2ND_COLUMN+qr.rem*(2*FW+1), y+FH*qr.quot, 'A'+i, attr && (menuHorizontalPosition==current) ? INVERS : 0);
659 lcdDrawSizedText(MODEL_SETUP_2ND_COLUMN + qr.rem*((2*FW)+1), y+FH*qr.quot, FIRSTSW_STR+(i*length)+3, 1, attr && (menuHorizontalPosition==current) ? INVERS : 0);
660 if (swactive) lcdDrawChar(lcdNextPos, y+FH*qr.quot, c);
661 ++current;
663 states >>= 2;
665 if (attr && ((menuHorizontalPosition < 0) || menuHorizontalPosition >= switchWarningsCount)) {
666 lcdDrawFilledRect(MODEL_SETUP_2ND_COLUMN-1, y-1, 8*(2*FW+1), 1+FH*((current+4)/5));
668 #else // PCBTARANIS
670 lcdDrawTextAlignedLeft(y, STR_SWITCHWARNING);
671 swarnstate_t states = g_model.switchWarningState;
672 char c;
673 if (attr) {
674 s_editMode = 0;
675 if (!READ_ONLY()) {
676 switch (event) {
677 case EVT_KEY_BREAK(KEY_ENTER):
678 if (menuHorizontalPosition < NUM_SWITCHES-1) {
679 g_model.switchWarningEnable ^= (1 << menuHorizontalPosition);
680 storageDirty(EE_MODEL);
682 break;
684 case EVT_KEY_LONG(KEY_ENTER):
685 if (menuHorizontalPosition == NUM_SWITCHES-1) {
686 START_NO_HIGHLIGHT();
687 getMovedSwitch();
688 g_model.switchWarningState = switches_states;
689 AUDIO_WARNING1();
690 storageDirty(EE_MODEL);
692 killEvents(event);
693 break;
698 for (uint8_t i=0; i<NUM_SWITCHES-1/*not on TRN switch*/; i++) {
699 uint8_t swactive = !(g_model.switchWarningEnable & 1 << i);
700 attr = 0;
702 if (IS_3POS(i)) {
703 c = '0'+(states & 0x03);
704 states >>= 2;
706 else {
707 if ((states & 0x01) && swactive)
708 attr = INVERS;
709 c = *(STR_VSWITCHES - 2 + 9 + (3*(i+1)));
710 states >>= 1;
712 if (attr && (menuHorizontalPosition == i)) {
713 attr = BLINK | INVERS;
715 lcdDrawChar(MODEL_SETUP_2ND_COLUMN+i*FW, y, (swactive) ? c : '-', attr);
716 lcdDrawText(MODEL_SETUP_2ND_COLUMN+(NUM_SWITCHES*FW), y, "<]", (menuHorizontalPosition == NUM_SWITCHES-1 && !NO_HIGHLIGHT()) ? attr : 0);
718 #endif
719 break;
722 #if defined(PCBTARANIS)
723 case ITEM_MODEL_SETUP_POTS_WARNING:
724 lcdDrawTextAlignedLeft(y, STR_POTWARNING);
725 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, "\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);
731 if (attr) {
732 if (menuHorizontalPosition > 0) s_editMode = 0;
733 if (!READ_ONLY() && menuHorizontalPosition > 0) {
734 switch (event) {
735 case EVT_KEY_LONG(KEY_ENTER):
736 killEvents(event);
737 if (g_model.potsWarnMode == POTS_WARN_MANUAL) {
738 SAVE_POT_POSITION(menuHorizontalPosition-1);
739 AUDIO_WARNING1();
740 storageDirty(EE_MODEL);
742 break;
743 case EVT_KEY_BREAK(KEY_ENTER):
744 g_model.potsWarnEnabled ^= (1 << (menuHorizontalPosition-1));
745 storageDirty(EE_MODEL);
746 break;
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();
756 else {
757 LcdFlags flags = ((menuHorizontalPosition==i+1) && attr) ? BLINK : 0;
758 if ((!attr || menuHorizontalPosition >= 0) && !(g_model.potsWarnEnabled & (1 << i))) {
759 flags |= INVERS;
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);
764 x = lcdNextPos+3;
768 break;
769 #endif
771 case ITEM_MODEL_SETUP_BEEP_CENTER:
772 lcdDrawTextAlignedLeft(y, STR_BEEPCTR);
773 for (uint8_t i=0; i<NUM_STICKS+NUM_POTS+NUM_SLIDERS; 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) {
779 if (event == EVT_KEY_BREAK(KEY_ENTER)) {
780 if (READ_ONLY_UNLOCKED()) {
781 s_editMode = 0;
782 g_model.beepANACenter ^= ((BeepANACenter)1<<menuHorizontalPosition);
783 storageDirty(EE_MODEL);
787 break;
789 case ITEM_MODEL_SETUP_USE_GLOBAL_FUNCTIONS:
790 lcdDrawTextAlignedLeft(y, STR_USE_GLOBAL_FUNCS);
791 drawCheckBox(MODEL_SETUP_2ND_COLUMN, y, !g_model.noGlobalFunctions, attr);
792 if (attr) g_model.noGlobalFunctions = !checkIncDecModel(event, !g_model.noGlobalFunctions, 0, 1);
793 break;
795 #if defined(HARDWARE_INTERNAL_MODULE)
796 case ITEM_MODEL_SETUP_INTERNAL_MODULE_LABEL:
797 lcdDrawTextAlignedLeft(y, STR_INTERNALRF);
798 break;
800 case ITEM_MODEL_SETUP_INTERNAL_MODULE_TYPE:
802 lcdDrawTextAlignedLeft(y, INDENT TR_MODE);
803 #if defined(INTERNAL_MODULE_PXX1)
804 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_INTERNAL_MODULE_PROTOCOLS, g_model.moduleData[INTERNAL_MODULE].type, menuHorizontalPosition==0 ? attr : 0);
805 if (isModuleXJT(INTERNAL_MODULE))
806 lcdDrawTextAtIndex(lcdNextPos + 3, y, STR_XJT_ACCST_RF_PROTOCOLS, 1+g_model.moduleData[INTERNAL_MODULE].subType, menuHorizontalPosition==1 ? attr : 0);
807 else if (isModuleISRM(INTERNAL_MODULE))
808 lcdDrawTextAtIndex(lcdNextPos + 3, y, STR_ISRM_RF_PROTOCOLS, g_model.moduleData[INTERNAL_MODULE].subType, menuHorizontalPosition==1 ? attr : 0);
809 if (attr) {
810 if (menuHorizontalPosition == 0) {
811 uint8_t moduleType = checkIncDec(event, g_model.moduleData[INTERNAL_MODULE].type, MODULE_TYPE_NONE, MODULE_TYPE_MAX, EE_MODEL, isInternalModuleAvailable);
812 if (checkIncDec_Ret) {
813 setModuleType(INTERNAL_MODULE, moduleType);
816 else if (isModuleXJT(INTERNAL_MODULE)) {
817 g_model.moduleData[INTERNAL_MODULE].subType = checkIncDec(event, g_model.moduleData[INTERNAL_MODULE].subType, 0, MODULE_SUBTYPE_PXX1_LAST, EE_MODEL, isRfProtocolAvailable);
818 if (checkIncDec_Ret) {
819 g_model.moduleData[0].type = MODULE_TYPE_XJT_PXX1;
820 g_model.moduleData[0].channelsStart = 0;
821 g_model.moduleData[0].channelsCount = defaultModuleChannels_M8(INTERNAL_MODULE);
824 else if (isModulePXX2(INTERNAL_MODULE)) {
825 g_model.moduleData[INTERNAL_MODULE].subType = checkIncDec(event, g_model.moduleData[INTERNAL_MODULE].subType, 0, MODULE_SUBTYPE_ISRM_PXX2_ACCST_D16, EE_MODEL, isRfProtocolAvailable);
828 #else
829 uint8_t index = 0;
830 if (g_model.moduleData[INTERNAL_MODULE].type == MODULE_TYPE_ISRM_PXX2) {
831 index = 1 + g_model.moduleData[INTERNAL_MODULE].subType;
833 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_ISRM_RF_PROTOCOLS, index, attr);
834 if (attr) {
835 index = checkIncDec(event, index, 0, MODULE_SUBTYPE_ISRM_PXX2_ACCST_D16 + 1 /* because of --- */, EE_MODEL);
836 if (checkIncDec_Ret) {
837 memclear(&g_model.moduleData[INTERNAL_MODULE], sizeof(ModuleData));
838 if (index > 0) {
839 g_model.moduleData[INTERNAL_MODULE].type = MODULE_TYPE_ISRM_PXX2;
840 g_model.moduleData[INTERNAL_MODULE].subType = index - 1;
841 g_model.moduleData[INTERNAL_MODULE].channelsCount = defaultModuleChannels_M8(INTERNAL_MODULE);
845 #endif
846 break;
848 #endif
850 #if defined(PCBSKY9X)
851 case ITEM_MODEL_SETUP_EXTRA_MODULE_LABEL:
852 lcdDrawTextAlignedLeft(y, "RF Port 2 (PPM)");
853 break;
854 #endif
856 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_LABEL:
857 lcdDrawTextAlignedLeft(y, STR_EXTERNALRF);
858 break;
860 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_TYPE:
861 lcdDrawTextAlignedLeft(y, INDENT TR_MODE);
862 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_EXTERNAL_MODULE_PROTOCOLS, reusableBuffer.moduleSetup.newType, menuHorizontalPosition==0 ? attr : 0);
863 if (isModuleXJT(EXTERNAL_MODULE))
864 lcdDrawTextAtIndex(lcdNextPos + 3, y, STR_XJT_ACCST_RF_PROTOCOLS, 1+g_model.moduleData[EXTERNAL_MODULE].subType, menuHorizontalPosition==1 ? attr : 0);
865 else if (isModuleDSM2(EXTERNAL_MODULE))
866 lcdDrawTextAtIndex(lcdNextPos + 3, y, STR_DSM_PROTOCOLS, g_model.moduleData[EXTERNAL_MODULE].rfProtocol, menuHorizontalPosition==1 ? attr : 0);
867 else if (isModuleR9MNonAccess(EXTERNAL_MODULE))
868 lcdDrawTextAtIndex(lcdNextPos + 3, y, STR_R9M_REGION, g_model.moduleData[EXTERNAL_MODULE].subType, (menuHorizontalPosition==1 ? attr : 0));
869 #if defined(MULTIMODULE)
870 else if (isModuleMultimodule(EXTERNAL_MODULE)) {
871 int multi_rfProto = g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol();
872 lcdDrawMultiProtocolString(lcdNextPos + 3, y, EXTERNAL_MODULE, multi_rfProto, menuHorizontalPosition == 1 ? attr : 0);
874 #endif
875 if (attr && menuHorizontalPosition == 0) {
876 if (s_editMode > 0) {
877 g_model.moduleData[EXTERNAL_MODULE].type = MODULE_TYPE_NONE;
879 else if (reusableBuffer.moduleSetup.newType != reusableBuffer.moduleSetup.previousType) {
880 g_model.moduleData[EXTERNAL_MODULE].type = reusableBuffer.moduleSetup.newType;
881 reusableBuffer.moduleSetup.previousType = reusableBuffer.moduleSetup.newType;
882 setModuleType(EXTERNAL_MODULE, g_model.moduleData[EXTERNAL_MODULE].type);
884 else if (g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_NONE) {
885 g_model.moduleData[EXTERNAL_MODULE].type = reusableBuffer.moduleSetup.newType;
888 if (attr) {
889 if (s_editMode > 0) {
890 switch (menuHorizontalPosition) {
891 case 0:
892 reusableBuffer.moduleSetup.newType = checkIncDec(event, reusableBuffer.moduleSetup.newType, MODULE_TYPE_NONE, MODULE_TYPE_MAX, EE_MODEL, isExternalModuleAvailable);
893 break;
895 case 1:
896 if (isModuleDSM2(EXTERNAL_MODULE)) {
897 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[EXTERNAL_MODULE].rfProtocol, DSM2_PROTO_LP45, DSM2_PROTO_DSMX);
899 else if (isModuleR9MNonAccess(EXTERNAL_MODULE)) {
900 g_model.moduleData[EXTERNAL_MODULE].subType = checkIncDec(event,
901 g_model.moduleData[EXTERNAL_MODULE].subType,
902 MODULE_SUBTYPE_R9M_FCC,
903 MODULE_SUBTYPE_R9M_LAST,
904 EE_MODEL,
905 isR9MModeAvailable);
906 if (checkIncDec_Ret) {
907 g_model.moduleData[EXTERNAL_MODULE].pxx.power = 0;
908 g_model.moduleData[EXTERNAL_MODULE].channelsStart = 0;
909 g_model.moduleData[EXTERNAL_MODULE].channelsCount = defaultModuleChannels_M8(EXTERNAL_MODULE);
913 #if defined(MULTIMODULE)
914 else if (isModuleMultimodule(EXTERNAL_MODULE)) {
915 int multiRfProto = g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol();
916 CHECK_INCDEC_MODELVAR_CHECK(event, multiRfProto, MODULE_SUBTYPE_MULTI_FIRST, MULTI_MAX_PROTOCOLS, isMultiProtocolSelectable);
917 if (checkIncDec_Ret) {
918 g_model.moduleData[EXTERNAL_MODULE].setMultiProtocol(multiRfProto);
919 g_model.moduleData[EXTERNAL_MODULE].subType = 0;
920 // Sensible default for DSM2 (same as for ppm): 7ch@22ms + Autodetect settings enabled
921 if (g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol() == MODULE_SUBTYPE_MULTI_DSM2) {
922 g_model.moduleData[EXTERNAL_MODULE].multi.autoBindMode = 1;
924 else {
925 g_model.moduleData[EXTERNAL_MODULE].multi.autoBindMode = 0;
927 g_model.moduleData[EXTERNAL_MODULE].multi.optionValue = 0;
930 #endif
931 else {
932 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[EXTERNAL_MODULE].subType, 0, MODULE_SUBTYPE_PXX1_LAST);
935 if (checkIncDec_Ret) {
936 g_model.moduleData[EXTERNAL_MODULE].channelsStart = 0;
937 g_model.moduleData[EXTERNAL_MODULE].channelsCount = defaultModuleChannels_M8(EXTERNAL_MODULE);
941 #if POPUP_LEVEL > 1
942 else if (old_editMode > 0) {
943 if (isModuleR9MNonAccess(EXTERNAL_MODULE)) {
944 if (g_model.moduleData[EXTERNAL_MODULE].subType > MODULE_SUBTYPE_R9M_EU) {
945 POPUP_WARNING(STR_MODULE_PROTOCOL_FLEX_WARN_LINE1);
946 SET_WARNING_INFO(STR_MODULE_PROTOCOL_WARN_LINE2, sizeof(TR_MODULE_PROTOCOL_WARN_LINE2) - 1, 0);
948 #if POPUP_LEVEL >= 3
949 else if (g_model.moduleData[EXTERNAL_MODULE].subType == MODULE_SUBTYPE_R9M_EU) {
950 POPUP_WARNING(STR_MODULE_PROTOCOL_EU_WARN_LINE1);
951 SET_WARNING_INFO(STR_MODULE_PROTOCOL_WARN_LINE2, sizeof(TR_MODULE_PROTOCOL_WARN_LINE2) - 1, 0);
953 else {
954 POPUP_WARNING(STR_MODULE_PROTOCOL_FCC_WARN_LINE1);
955 SET_WARNING_INFO(STR_MODULE_PROTOCOL_WARN_LINE2, sizeof(TR_MODULE_PROTOCOL_WARN_LINE2) - 1, 0);
957 #endif
960 #endif
962 break;
964 #if defined(MULTIMODULE)
965 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_SUBTYPE:
967 lcdDrawTextAlignedLeft(y, STR_SUBTYPE);
968 lcdDrawMultiSubProtocolString(MODEL_SETUP_2ND_COLUMN, y, EXTERNAL_MODULE, g_model.moduleData[EXTERNAL_MODULE].subType, attr);
969 if (attr && s_editMode > 0) {
970 switch (menuHorizontalPosition) {
971 case 0:{
972 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[EXTERNAL_MODULE].subType, 0, getMaxMultiSubtype(EXTERNAL_MODULE));
973 break;
978 break;
979 #endif
981 #if defined(PCBTARANIS)
982 case ITEM_MODEL_SETUP_TRAINER_LABEL:
983 lcdDrawTextAlignedLeft(y, STR_TRAINER);
984 break;
986 case ITEM_MODEL_SETUP_TRAINER_MODE:
987 lcdDrawTextAlignedLeft(y, INDENT TR_MODE);
988 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_VTRAINERMODES, g_model.trainerData.mode, attr);
989 if (attr) {
990 g_model.trainerData.mode = checkIncDec(event, g_model.trainerData.mode, 0, TRAINER_MODE_MAX(), EE_MODEL, isTrainerModeAvailable);
991 #if defined(BLUETOOTH)
992 if (checkIncDec_Ret) {
993 bluetooth.state = BLUETOOTH_STATE_OFF;
994 bluetooth.distantAddr[0] = '\0';
996 #endif
998 break;
999 #endif
1001 #if defined(PCBTARANIS) && defined(BLUETOOTH)
1002 case ITEM_MODEL_SETUP_TRAINER_BLUETOOTH:
1003 if (g_model.trainerData.mode == TRAINER_MODE_MASTER_BLUETOOTH) {
1004 if (attr) {
1005 s_editMode = 0;
1007 if (bluetooth.distantAddr[0]) {
1008 lcdDrawText(INDENT_WIDTH, y+1, bluetooth.distantAddr, TINSIZE);
1009 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, BUTTON(TR_CLEAR), attr);
1010 if (attr && event == EVT_KEY_BREAK(KEY_ENTER)) {
1011 bluetooth.state = BLUETOOTH_STATE_CLEAR_REQUESTED;
1012 memclear(bluetooth.distantAddr, sizeof(bluetooth.distantAddr));
1015 else {
1016 lcdDrawText(INDENT_WIDTH, y, "---");
1017 if (bluetooth.state < BLUETOOTH_STATE_IDLE)
1018 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_BUTTON_INIT, attr);
1019 else
1020 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, BUTTON(TR_DISCOVER), attr);
1021 if (attr && event == EVT_KEY_BREAK(KEY_ENTER)) {
1022 if (bluetooth.state < BLUETOOTH_STATE_IDLE) {
1023 bluetooth.state = BLUETOOTH_STATE_OFF;
1025 else {
1026 reusableBuffer.moduleSetup.bt.devicesCount = 0;
1027 bluetooth.state = BLUETOOTH_STATE_DISCOVER_REQUESTED;
1031 if (bluetooth.state == BLUETOOTH_STATE_DISCOVER_START && reusableBuffer.moduleSetup.bt.devicesCount > 0) {
1032 popupMenuItemsCount = min<uint8_t>(reusableBuffer.moduleSetup.bt.devicesCount, MAX_BLUETOOTH_DISTANT_ADDR);
1033 for (uint8_t i=0; i<popupMenuItemsCount; i++) {
1034 popupMenuItems[i] = reusableBuffer.moduleSetup.bt.devices[i];
1036 popupMenuTitle = STR_BT_SELECT_DEVICE;
1037 POPUP_MENU_START(onBluetoothConnectMenu);
1041 else {
1042 if (bluetooth.distantAddr[0])
1043 lcdDrawText(INDENT_WIDTH, y+1, bluetooth.distantAddr, TINSIZE);
1044 else
1045 lcdDrawText(INDENT_WIDTH, y, "---");
1046 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, bluetooth.state == BLUETOOTH_STATE_CONNECTED ? STR_CONNECTED : STR_NOT_CONNECTED);
1048 break;
1049 #endif
1051 #if defined(PCBTARANIS)
1052 case ITEM_MODEL_SETUP_TRAINER_CHANNELS:
1053 lcdDrawTextAlignedLeft(y, STR_CHANNELRANGE);
1054 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_CH, menuHorizontalPosition==0 ? attr : 0);
1055 lcdDrawNumber(lcdLastRightPos, y, g_model.trainerData.channelsStart+1, LEFT | (menuHorizontalPosition==0 ? attr : 0));
1056 lcdDrawChar(lcdLastRightPos, y, '-');
1057 lcdDrawNumber(lcdLastRightPos + FW+1, y, g_model.trainerData.channelsStart + 8 + g_model.trainerData.channelsCount, LEFT | (menuHorizontalPosition==1 ? attr : 0));
1058 if (attr && s_editMode > 0) {
1059 switch (menuHorizontalPosition) {
1060 case 0:
1061 CHECK_INCDEC_MODELVAR_ZERO(event, g_model.trainerData.channelsStart, 32-8-g_model.trainerData.channelsCount);
1062 break;
1063 case 1:
1064 CHECK_INCDEC_MODELVAR(event, g_model.trainerData.channelsCount, -4, min<int8_t>(MAX_TRAINER_CHANNELS_M8, 32-8-g_model.trainerData.channelsStart));
1065 break;
1068 break;
1069 #endif
1071 #if defined(HARDWARE_INTERNAL_MODULE)
1072 case ITEM_MODEL_SETUP_INTERNAL_MODULE_CHANNELS:
1073 #endif
1074 #if defined(PCBSKY9X)
1075 case ITEM_MODEL_SETUP_EXTRA_MODULE_CHANNELS:
1076 #endif
1077 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_CHANNELS:
1079 uint8_t moduleIdx = CURRENT_MODULE_EDITED(k);
1080 ModuleData & moduleData = g_model.moduleData[moduleIdx];
1081 lcdDrawTextAlignedLeft(y, STR_CHANNELRANGE);
1082 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_CH, menuHorizontalPosition==0 ? attr : 0);
1083 lcdDrawNumber(lcdLastRightPos, y, moduleData.channelsStart+1, LEFT | (menuHorizontalPosition==0 ? attr : 0));
1084 lcdDrawChar(lcdLastRightPos, y, '-');
1085 lcdDrawNumber(lcdLastRightPos + FW+1, y, moduleData.channelsStart+sentModuleChannels(moduleIdx), LEFT | (menuHorizontalPosition==1 ? attr : 0));
1086 const char * delay = getModuleDelay(moduleIdx);
1087 if (delay)
1088 lcdDrawText(lcdLastRightPos+4, y, delay, SMLSIZE);
1089 if (attr && s_editMode > 0) {
1090 switch (menuHorizontalPosition) {
1091 case 0:
1092 CHECK_INCDEC_MODELVAR_ZERO(event, moduleData.channelsStart, 32-8-moduleData.channelsCount);
1093 break;
1094 case 1:
1095 CHECK_INCDEC_MODELVAR_CHECK(event, moduleData.channelsCount, -4, min<int8_t>(maxModuleChannels_M8(moduleIdx), 32-8-moduleData.channelsStart), moduleData.type == MODULE_TYPE_ISRM_PXX2 ? isPxx2IsrmChannelsCountAllowed : nullptr);
1096 if (checkIncDec_Ret && moduleData.type == MODULE_TYPE_PPM) {
1097 setDefaultPpmFrameLength(moduleIdx);
1099 break;
1102 break;
1105 #if defined(PCBX7) || defined(PCBX9LITE) || defined(PCBXLITE)
1106 case ITEM_MODEL_SETUP_TRAINER_PPM_PARAMS:
1107 lcdDrawTextAlignedLeft(y, STR_PPMFRAME);
1108 lcdDrawText(MODEL_SETUP_2ND_COLUMN+3*FW, y, STR_MS);
1109 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, (int16_t)g_model.trainerData.frameLength*5 + 225, (menuHorizontalPosition<=0 ? attr : 0) | PREC1|LEFT);
1110 lcdDrawChar(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, 'u');
1111 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, (g_model.trainerData.delay*50)+300, RIGHT | ((CURSOR_ON_LINE() || menuHorizontalPosition==1) ? attr : 0));
1112 lcdDrawChar(MODEL_SETUP_2ND_COLUMN+10*FW, y, g_model.trainerData.pulsePol ? '+' : '-', (CURSOR_ON_LINE() || menuHorizontalPosition==2) ? attr : 0);
1113 if (attr && s_editMode > 0) {
1114 switch (menuHorizontalPosition) {
1115 case 0:
1116 CHECK_INCDEC_MODELVAR(event, g_model.trainerData.frameLength, -20, 35);
1117 break;
1118 case 1:
1119 CHECK_INCDEC_MODELVAR(event, g_model.trainerData.delay, -4, 10);
1120 break;
1121 case 2:
1122 CHECK_INCDEC_MODELVAR_ZERO(event, g_model.trainerData.pulsePol, 1);
1123 break;
1126 break;
1127 #endif
1129 #if defined(PXX2)
1130 case ITEM_MODEL_SETUP_REGISTRATION_ID:
1131 lcdDrawTextAlignedLeft(y, STR_REG_ID);
1132 if (isDefaultModelRegistrationID())
1133 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_PXX2_DEFAULT);
1134 else
1135 lcdDrawSizedText(MODEL_SETUP_2ND_COLUMN, y, g_model.modelRegistrationID, PXX2_LEN_REGISTRATION_ID, ZCHAR);
1136 break;
1138 case ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_MODEL_NUM:
1139 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_MODEL_NUM:
1141 uint8_t moduleIdx = CURRENT_MODULE_EDITED(k);
1142 lcdDrawText(INDENT_WIDTH, y, STR_RECEIVER_NUM);
1143 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, g_model.header.modelId[moduleIdx], attr | LEADING0 | LEFT, 2);
1144 if (attr) {
1145 CHECK_INCDEC_MODELVAR_ZERO(event, g_model.header.modelId[moduleIdx], getMaxRxNum(moduleIdx));
1146 if (checkIncDec_Ret) {
1147 modelHeaders[g_eeGeneral.currModel].modelId[moduleIdx] = g_model.header.modelId[moduleIdx];
1151 break;
1153 case ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_REGISTER_RANGE:
1154 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_REGISTER_RANGE:
1156 uint8_t moduleIdx = CURRENT_MODULE_EDITED(k);
1157 lcdDrawTextAlignedLeft(y, INDENT TR_MODULE);
1158 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, BUTTON(TR_REGISTER), (menuHorizontalPosition == 0 ? attr : 0));
1159 lcdDrawText(lcdLastRightPos + 3, y, STR_MODULE_RANGE, (menuHorizontalPosition == 1 ? attr : 0));
1160 if (attr) {
1161 if (moduleState[moduleIdx].mode == MODULE_MODE_NORMAL && s_editMode > 0) {
1162 if (menuHorizontalPosition == 0 && event == EVT_BUTTON_PRESSED()) {
1163 startRegisterDialog(moduleIdx);
1165 else if (menuHorizontalPosition == 1) {
1166 moduleState[moduleIdx].mode = MODULE_MODE_RANGECHECK;
1169 if (s_editMode == 0 && !warningText) {
1170 moduleState[moduleIdx].mode = MODULE_MODE_NORMAL;
1172 if (moduleState[moduleIdx].mode == MODULE_MODE_NORMAL) {
1173 // REGISTER finished
1174 s_editMode = 0;
1178 break;
1180 case ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_OPTIONS:
1181 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_OPTIONS:
1182 lcdDrawText(INDENT_WIDTH, y, STR_OPTIONS);
1183 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_SET, attr);
1184 if (event == EVT_KEY_BREAK(KEY_ENTER) && attr) {
1185 g_moduleIdx = CURRENT_MODULE_EDITED(k);
1186 pushMenu(menuModelModuleOptions);
1188 break;
1190 case ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_1:
1191 case ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_2:
1192 case ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_3:
1193 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_1:
1194 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_2:
1195 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_3:
1196 modelSetupModulePxx2ReceiverLine(CURRENT_MODULE_EDITED(k), CURRENT_RECEIVER_EDITED(k), y, event, attr);
1197 break;
1198 #endif
1200 #if defined(PCBSKY9X)
1201 case ITEM_MODEL_SETUP_EXTRA_MODULE_BIND:
1202 #endif
1203 #if defined(HARDWARE_INTERNAL_MODULE)
1204 case ITEM_MODEL_SETUP_INTERNAL_MODULE_NOT_ACCESS_BIND:
1205 #endif
1206 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_NOT_ACCESS_BIND:
1208 uint8_t moduleIdx = CURRENT_MODULE_EDITED(k);
1209 ModuleData & moduleData = g_model.moduleData[moduleIdx];
1210 if (isModulePPM(moduleIdx)) {
1211 lcdDrawTextAlignedLeft(y, STR_PPMFRAME);
1212 lcdDrawText(MODEL_SETUP_2ND_COLUMN+3*FW, y, STR_MS);
1213 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, (int16_t)moduleData.ppm.frameLength*5 + 225, (menuHorizontalPosition<=0 ? attr : 0) | PREC1|LEFT);
1214 lcdDrawChar(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, 'u');
1215 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, (moduleData.ppm.delay*50)+300, RIGHT | ((CURSOR_ON_LINE() || menuHorizontalPosition==1) ? attr : 0));
1216 lcdDrawChar(MODEL_SETUP_2ND_COLUMN+10*FW, y, moduleData.ppm.pulsePol ? '+' : '-', (CURSOR_ON_LINE() || menuHorizontalPosition==2) ? attr : 0);
1217 if (attr && s_editMode > 0) {
1218 switch (menuHorizontalPosition) {
1219 case 0:
1220 CHECK_INCDEC_MODELVAR(event, moduleData.ppm.frameLength, -20, 35);
1221 break;
1222 case 1:
1223 CHECK_INCDEC_MODELVAR(event, moduleData.ppm.delay, -4, 10);
1224 break;
1225 case 2:
1226 CHECK_INCDEC_MODELVAR_ZERO(event, moduleData.ppm.pulsePol, 1);
1227 break;
1231 else if (isModuleSBUS(moduleIdx)) {
1232 lcdDrawTextAlignedLeft(y, STR_REFRESHRATE);
1233 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, (int16_t)moduleData.ppm.frameLength*5 + 225, (menuHorizontalPosition<=0 ? attr : 0) | PREC1|LEFT);
1234 lcdDrawText(lcdLastRightPos, y, STR_MS);
1235 lcdDrawText(MODEL_SETUP_2ND_COLUMN+5*FW+2, y, moduleData.sbus.noninverted ? STR_NOT_INVERTED : STR_NORMAL, (CURSOR_ON_LINE() || menuHorizontalPosition==1) ? attr : 0);
1237 if (attr && s_editMode>0) {
1238 switch (menuHorizontalPosition) {
1239 case 0:
1240 CHECK_INCDEC_MODELVAR(event, moduleData.ppm.frameLength, -33, 35);
1241 break;
1242 case 1:
1243 CHECK_INCDEC_MODELVAR_ZERO(event, moduleData.sbus.noninverted, 1);
1244 break;
1248 else {
1249 horzpos_t l_posHorz = menuHorizontalPosition;
1250 coord_t xOffsetBind = MODEL_SETUP_BIND_OFS;
1251 if (!isModuleRxNumAvailable(moduleIdx)) {
1252 xOffsetBind = 0;
1253 lcdDrawText(INDENT_WIDTH, y, STR_RECEIVER);
1254 if (attr) l_posHorz += 1;
1256 else {
1257 lcdDrawText(INDENT_WIDTH, y, STR_RECEIVER_NUM);
1259 if (isModuleBindRangeAvailable(moduleIdx)) {
1260 if (xOffsetBind)
1261 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, g_model.header.modelId[moduleIdx], (l_posHorz==0 ? attr : 0) | LEADING0|LEFT, 2);
1262 if (attr && l_posHorz == 0) {
1263 if (s_editMode > 0) {
1264 CHECK_INCDEC_MODELVAR_ZERO(event, g_model.header.modelId[moduleIdx], getMaxRxNum(moduleIdx));
1265 if (checkIncDec_Ret) {
1266 modelHeaders[g_eeGeneral.currModel].modelId[moduleIdx] = g_model.header.modelId[moduleIdx];
1268 else if (event == EVT_KEY_LONG(KEY_ENTER)) {
1269 killEvents(event);
1270 uint8_t newVal = findNextUnusedModelId(g_eeGeneral.currModel, moduleIdx);
1271 if (newVal != g_model.header.modelId[moduleIdx]) {
1272 modelHeaders[g_eeGeneral.currModel].modelId[moduleIdx] = g_model.header.modelId[moduleIdx] = newVal;
1273 storageDirty(EE_MODEL);
1278 lcdDrawText(MODEL_SETUP_2ND_COLUMN+xOffsetBind, y, STR_MODULE_BIND, l_posHorz==1 ? attr : 0);
1279 lcdDrawText(MODEL_SETUP_2ND_COLUMN+MODEL_SETUP_RANGE_OFS+xOffsetBind, y, STR_MODULE_RANGE, l_posHorz==2 ? attr : 0);
1280 uint8_t newFlag = 0;
1281 #if defined(MULTIMODULE)
1282 if (getMultiBindStatus(moduleIdx) == MULTI_BIND_FINISHED) {
1283 setMultiBindStatus(moduleIdx, MULTI_NORMAL_OPERATION);
1284 s_editMode = 0;
1286 #endif
1287 #if defined(PCBTARANIS)
1288 if (attr && l_posHorz > 0) {
1289 if (s_editMode > 0) {
1290 if (l_posHorz == 1) {
1291 if (isModuleR9MNonAccess(moduleIdx) || isModuleD16(moduleIdx)) {
1292 #if defined(PCBXLITE)
1293 if (EVT_KEY_MASK(event) == KEY_ENTER) {
1294 #elif defined(NAVIGATION_9X)
1295 if (event == EVT_KEY_FIRST(KEY_ENTER)) {
1296 #else
1297 if (event == EVT_KEY_BREAK(KEY_ENTER)) {
1298 #endif
1299 killEvents(event);
1300 startBindMenu(moduleIdx);
1301 continue;
1303 if (moduleState[moduleIdx].mode == MODULE_MODE_BIND) {
1304 newFlag = MODULE_MODE_BIND;
1306 else {
1307 if (!popupMenuItemsCount) {
1308 s_editMode = 0; // this is when popup is exited before a choice is made
1312 else {
1313 newFlag = MODULE_MODE_BIND;
1316 else if (l_posHorz == 2) {
1317 newFlag = MODULE_MODE_RANGECHECK;
1321 #else
1322 if (attr && l_posHorz>0 && s_editMode>0) {
1323 if (l_posHorz == 1)
1324 newFlag = MODULE_MODE_BIND;
1325 else if (l_posHorz == 2)
1326 newFlag = MODULE_MODE_RANGECHECK;
1328 #endif
1329 moduleState[moduleIdx].mode = newFlag;
1331 #if defined(MULTIMODULE)
1332 if (newFlag == MODULE_MODE_BIND) {
1333 setMultiBindStatus(moduleIdx, MULTI_BIND_INITIATED);
1335 #endif
1339 break;
1342 #if defined(PCBSKY9X) && defined(REVX)
1343 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_OUTPUT_TYPE:
1345 uint8_t moduleIdx = CURRENT_MODULE_EDITED(k);
1346 ModuleData & moduleData = g_model.moduleData[moduleIdx];
1347 moduleData.ppm.outputType = editChoice(MODEL_SETUP_2ND_COLUMN, y, STR_OUTPUT_TYPE, STR_VOUTPUT_TYPE, moduleData.ppm.outputType, 0, 1, attr, event);
1348 break;
1350 #endif
1352 #if defined(HARDWARE_INTERNAL_MODULE)
1353 case ITEM_MODEL_SETUP_INTERNAL_MODULE_FAILSAFE:
1354 #endif
1355 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_FAILSAFE: {
1356 uint8_t moduleIdx = CURRENT_MODULE_EDITED(k);
1357 ModuleData &moduleData = g_model.moduleData[moduleIdx];
1358 lcdDrawTextAlignedLeft(y, STR_FAILSAFE);
1359 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_VFAILSAFE, moduleData.failsafeMode, menuHorizontalPosition == 0 ? attr : 0);
1360 if (moduleData.failsafeMode == FAILSAFE_CUSTOM)
1361 lcdDrawText(MODEL_SETUP_2ND_COLUMN + MODEL_SETUP_SET_FAILSAFE_OFS, y, STR_SET, menuHorizontalPosition == 1 ? attr : 0);
1362 if (attr) {
1363 if (moduleData.failsafeMode != FAILSAFE_CUSTOM)
1364 menuHorizontalPosition = 0;
1365 if (menuHorizontalPosition == 0) {
1366 if (s_editMode > 0) {
1367 CHECK_INCDEC_MODELVAR_ZERO(event, moduleData.failsafeMode, isModuleR9M(moduleIdx) ? FAILSAFE_NOPULSES : FAILSAFE_LAST);
1368 if (checkIncDec_Ret)
1369 SEND_FAILSAFE_NOW(moduleIdx);
1372 else if (menuHorizontalPosition == 1) {
1373 s_editMode = 0;
1374 if (moduleData.failsafeMode == FAILSAFE_CUSTOM) {
1375 if (event == EVT_KEY_LONG(KEY_ENTER)) {
1376 killEvents(event);
1377 setCustomFailsafe(moduleIdx);
1378 storageDirty(EE_MODEL);
1379 AUDIO_WARNING1();
1380 SEND_FAILSAFE_NOW(moduleIdx);
1382 else if (event == EVT_KEY_BREAK(KEY_ENTER) && attr) {
1383 g_moduleIdx = moduleIdx;
1384 pushMenu(menuModelFailsafe);
1388 else {
1389 lcdDrawSolidFilledRect(MODEL_SETUP_2ND_COLUMN, y, LCD_W - MODEL_SETUP_2ND_COLUMN, 8);
1393 break;
1395 #if defined(INTERNAL_MODULE_PXX1) && defined(EXTERNAL_ANTENNA)
1396 case ITEM_MODEL_SETUP_INTERNAL_MODULE_ANTENNA:
1397 reusableBuffer.moduleSetup.antennaMode = editChoice(MODEL_SETUP_2ND_COLUMN, y, INDENT TR_ANTENNA, STR_ANTENNA_MODES,
1398 reusableBuffer.moduleSetup.antennaMode == ANTENNA_MODE_PER_MODEL ? ANTENNA_MODE_INTERNAL : reusableBuffer.moduleSetup.antennaMode,
1399 ANTENNA_MODE_INTERNAL, ANTENNA_MODE_EXTERNAL, attr, event,
1400 [](int value) { return value != ANTENNA_MODE_PER_MODEL; });
1401 if (event && !s_editMode && reusableBuffer.moduleSetup.antennaMode != g_model.moduleData[INTERNAL_MODULE].pxx.antennaMode) {
1402 if (reusableBuffer.moduleSetup.antennaMode == ANTENNA_MODE_EXTERNAL && !isExternalAntennaEnabled()) {
1403 POPUP_CONFIRMATION(STR_ANTENNACONFIRM1, onModelAntennaSwitchConfirm);
1404 SET_WARNING_INFO(STR_ANTENNACONFIRM2, sizeof(TR_ANTENNACONFIRM2), 0);
1406 else {
1407 g_model.moduleData[INTERNAL_MODULE].pxx.antennaMode = reusableBuffer.moduleSetup.antennaMode;
1408 checkExternalAntenna();
1411 break;
1412 #endif
1414 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_OPTIONS:
1416 uint8_t moduleIdx = CURRENT_MODULE_EDITED(k);
1417 #if defined(MULTIMODULE)
1418 if (isModuleMultimodule(moduleIdx)) {
1419 int optionValue = g_model.moduleData[moduleIdx].multi.optionValue;
1421 const uint8_t multi_proto = g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol();
1422 if (multi_proto < MODULE_SUBTYPE_MULTI_LAST) {
1423 const mm_protocol_definition * pdef = getMultiProtocolDefinition(multi_proto);
1424 if (pdef->optionsstr) {
1425 lcdDrawText(INDENT_WIDTH, y, pdef->optionsstr);
1426 if (attr && pdef->optionsstr == STR_MULTI_RFTUNE) {
1427 lcdDrawText(MODEL_SETUP_2ND_COLUMN + 23, y, "RSSI(", LEFT);
1428 lcdDrawNumber(lcdLastRightPos, y, TELEMETRY_RSSI(), LEFT);
1429 lcdDrawText(lcdLastRightPos, y, ")", LEFT);
1433 else {
1434 MultiModuleStatus &status = getMultiModuleStatus(moduleIdx);
1435 lcdDrawText(INDENT_WIDTH, y, mm_options_strings::options[status.optionDisp]);
1436 if (attr && status.optionDisp == 2) {
1437 lcdDrawText(MODEL_SETUP_2ND_COLUMN + 23, y, "RSSI(", LEFT);
1438 lcdDrawNumber(lcdLastRightPos, y, TELEMETRY_RSSI(), LEFT);
1439 lcdDrawText(lcdLastRightPos, y, ")", LEFT);
1443 if (multi_proto == MODULE_SUBTYPE_MULTI_FS_AFHDS2A)
1444 optionValue = 50 + 5 * optionValue;
1446 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, optionValue, LEFT | attr);
1447 if (attr) {
1448 if (multi_proto == MODULE_SUBTYPE_MULTI_FS_AFHDS2A) {
1449 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].multi.optionValue, 0, 70);
1451 else if (multi_proto == MODULE_SUBTYPE_MULTI_OLRS) {
1452 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].multi.optionValue, -1, 7);
1454 else {
1455 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].multi.optionValue, -128, 127);
1459 #endif
1460 if (isModuleR9MNonAccess(moduleIdx)) {
1461 lcdDrawTextAlignedLeft(y, STR_MODULE_TELEMETRY);
1462 if (isSportLineUsedByInternalModule())
1463 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_DISABLE_INTERNAL);
1464 else
1465 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_MODULE_TELEM_ON);
1467 else if (isModuleSBUS(moduleIdx)) {
1468 lcdDrawTextAlignedLeft(y, STR_WARN_BATTVOLTAGE);
1469 putsVolts(lcdLastRightPos, y, getBatteryVoltage(), attr | PREC2 | LEFT);
1471 break;
1474 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_POWER:
1476 uint8_t moduleIdx = CURRENT_MODULE_EDITED(k);
1477 auto & module = g_model.moduleData[moduleIdx];
1478 // Lite FCC / Lite FLEX / Lite Pro Flex
1479 if (isModuleTypeR9MNonAccess(module.type)) {
1480 lcdDrawTextAlignedLeft(y, STR_RFPOWER);
1481 if (isModuleR9M_FCC_VARIANT(moduleIdx)) {
1482 // FCC and FLEX modes ...
1483 if (isModuleTypeR9MLiteNonPro(module.type)) { // R9M lite FCC has only one power value, so displayed for info only
1484 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_R9M_LITE_FCC_POWER_VALUES, 0, LEFT);
1485 if (attr) {
1486 REPEAT_LAST_CURSOR_MOVE();
1489 else {
1490 module.pxx.power = min<uint8_t>(module.pxx.power, R9M_FCC_POWER_MAX); // Sanitize
1491 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_R9M_FCC_POWER_VALUES, module.pxx.power, LEFT | attr);
1492 if (attr) {
1493 CHECK_INCDEC_MODELVAR_ZERO(event, module.pxx.power, R9M_FCC_POWER_MAX);
1494 if (s_editMode == 0 && reusableBuffer.moduleSetup.r9mPower != module.pxx.power && module.channelsCount > maxModuleChannels_M8(moduleIdx)) {
1495 module.channelsStart = 0;
1496 module.channelsCount = maxModuleChannels_M8(moduleIdx);
1501 else {
1502 // EU-LBT mode ...
1503 if (isModuleTypeR9MLiteNonPro(module.type)) {
1504 // R9M Lite in EU-LBT mode ...
1505 module.pxx.power = min<uint8_t>(module.pxx.power, R9M_LITE_LBT_POWER_MAX); // Sanitize
1506 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_R9M_LITE_LBT_POWER_VALUES, module.pxx.power, LEFT | attr);
1507 if (attr) {
1508 CHECK_INCDEC_MODELVAR_ZERO(event, module.pxx.power, R9M_LITE_LBT_POWER_MAX);
1509 if (s_editMode == 0 && reusableBuffer.moduleSetup.r9mPower != module.pxx.power) {
1510 module.channelsStart = 0;
1511 if (module.channelsCount > maxModuleChannels_M8(moduleIdx))
1512 module.channelsCount = maxModuleChannels_M8(moduleIdx);
1513 if (reusableBuffer.moduleSetup.r9mPower + module.pxx.power < 5) { // switching between mode 2 and 3 does not require rebind
1514 POPUP_WARNING(STR_REBIND);
1516 reusableBuffer.moduleSetup.r9mPower = module.pxx.power;
1520 else {
1521 // R9M (full size) or R9M Lite Pro in EU-LBT mode ...
1522 module.pxx.power = min((uint8_t) module.pxx.power, (uint8_t) R9M_LBT_POWER_MAX); // Sanitize
1523 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_R9M_LBT_POWER_VALUES, module.pxx.power, LEFT | attr);
1524 if (attr) {
1525 CHECK_INCDEC_MODELVAR_ZERO(event, module.pxx.power, R9M_LBT_POWER_MAX);
1526 if (s_editMode == 0 && reusableBuffer.moduleSetup.r9mPower != module.pxx.power) {
1527 module.channelsStart = 0;
1528 if (module.channelsCount > maxModuleChannels_M8(moduleIdx))
1529 module.channelsCount = maxModuleChannels_M8(moduleIdx);
1530 if (reusableBuffer.moduleSetup.r9mPower + module.pxx.power < 5) { //switching between mode 2 and 3 does not require rebind
1531 POPUP_WARNING(STR_REBIND);
1533 reusableBuffer.moduleSetup.r9mPower = module.pxx.power;
1539 #if defined(MULTIMODULE)
1540 else if (isModuleMultimodule(moduleIdx)) {
1541 module.multi.lowPowerMode = editCheckBox(module.multi.lowPowerMode, MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_LOWPOWER, attr, event);
1543 #endif
1546 break;
1548 #if defined(MULTIMODULE)
1549 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AUTOBIND:
1550 if (g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol() == MODULE_SUBTYPE_MULTI_DSM2)
1551 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);
1552 else
1553 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);
1554 break;
1556 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_DISABLE_TELEM:
1557 g_model.moduleData[EXTERNAL_MODULE].multi.disableTelemetry = editCheckBox(g_model.moduleData[EXTERNAL_MODULE].multi.disableTelemetry, MODEL_SETUP_2ND_COLUMN, y, INDENT TR_DISABLE_TELEM, attr, event);
1558 break;
1560 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_DISABLE_MAPPING:
1561 g_model.moduleData[EXTERNAL_MODULE].multi.disableMapping = editCheckBox(g_model.moduleData[EXTERNAL_MODULE].multi.disableMapping, MODEL_SETUP_2ND_COLUMN, y, INDENT TR_DISABLE_CH_MAP, attr, event);
1562 break;
1564 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_STATUS: {
1565 lcdDrawTextAlignedLeft(y, STR_MODULE_STATUS);
1567 char statusText[64];
1568 getMultiModuleStatus(EXTERNAL_MODULE).getStatusString(statusText);
1569 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, statusText);
1570 break;
1573 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_SYNCSTATUS: {
1574 lcdDrawTextAlignedLeft(y, STR_MODULE_SYNC);
1576 char statusText[64];
1577 getMultiSyncStatus(EXTERNAL_MODULE).getRefreshString(statusText);
1578 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, statusText);
1579 break;
1581 #endif
1583 #if 0
1584 case ITEM_MODEL_SETUP_PPM2_PROTOCOL:
1585 lcdDrawTextAlignedLeft(y, "Port2");
1586 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_VPROTOS, 0, 0);
1587 lcdDrawText(MODEL_SETUP_2ND_COLUMN+4*FW+3, y, STR_CH, menuHorizontalPosition<=0 ? attr : 0);
1588 lcdDrawNumber(lcdLastRightPos, y, g_model.moduleData[1].channelsStart+1, LEFT | (menuHorizontalPosition<=0 ? attr : 0));
1589 lcdDrawChar(lcdLastRightPos, y, '-');
1590 lcdDrawNumber(lcdLastRightPos + FW+1, y, g_model.moduleData[1].channelsStart+8+g_model.moduleData[1].channelsCount, LEFT | (menuHorizontalPosition!=0 ? attr : 0));
1591 if (attr && s_editMode > 0) {
1592 switch (menuHorizontalPosition) {
1593 case 0:
1594 CHECK_INCDEC_MODELVAR_ZERO(event, g_model.moduleData[1].channelsStart, 32-8-g_model.moduleData[1].channelsCount);
1595 setDefaultPpmFrameLength(1);
1596 break;
1597 case 1:
1598 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[1].channelsCount, -4, min<int8_t>(8, 32-8-g_model.moduleData[1].channelsStart));
1599 setDefaultPpmFrameLength(1);
1600 break;
1603 break;
1605 case ITEM_MODEL_SETUP_PPM2_PARAMS:
1606 lcdDrawTextAlignedLeft(y, STR_PPMFRAME);
1607 lcdDrawText(MODEL_SETUP_2ND_COLUMN+3*FW, y, STR_MS);
1608 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, (int16_t)g_model.moduleData[1].ppmFrameLength*5 + 225, (menuHorizontalPosition<=0 ? attr : 0) | PREC1 | LEFT);
1609 lcdDrawChar(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, 'u');
1610 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, (g_model.moduleData[1].ppmDelay*50)+300, RIGHT | ((menuHorizontalPosition < 0 || menuHorizontalPosition==1) ? attr : 0));
1611 lcdDrawChar(MODEL_SETUP_2ND_COLUMN+10*FW, y, g_model.moduleData[1].ppmPulsePol ? '+' : '-', (menuHorizontalPosition < 0 || menuHorizontalPosition==2) ? attr : 0);
1612 if (attr && s_editMode > 0) {
1613 switch (menuHorizontalPosition) {
1614 case 0:
1615 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[1].ppmFrameLength, -20, 35);
1616 break;
1617 case 1:
1618 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[1].ppmDelay, -4, 10);
1619 break;
1620 case 2:
1621 CHECK_INCDEC_MODELVAR_ZERO(event, g_model.moduleData[1].ppmPulsePol, 1);
1622 break;
1625 break;
1626 #endif
1631 #if defined(PXX)
1632 if (isModuleInRangeCheckMode()) {
1633 showMessageBox("RSSI: ");
1634 lcdDrawNumber(WARNING_LINE_X, 5*FH, TELEMETRY_RSSI(), BOLD);
1636 #endif
1638 // some field just finished being edited
1639 if (old_editMode > 0 && s_editMode == 0) {
1640 switch(menuVerticalPosition) {
1641 #if defined(HARDWARE_INTERNAL_MODULE)
1642 case ITEM_MODEL_SETUP_INTERNAL_MODULE_NOT_ACCESS_BIND:
1643 case ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_MODEL_NUM:
1644 if (menuHorizontalPosition == 0)
1645 checkModelIdUnique(g_eeGeneral.currModel, INTERNAL_MODULE);
1646 break;
1647 #endif
1648 #if defined(PCBSKY9X)
1649 case ITEM_MODEL_SETUP_EXTRA_MODULE_BIND:
1650 if (menuHorizontalPosition == 0)
1651 checkModelIdUnique(g_eeGeneral.currModel, EXTRA_MODULE);
1652 break;
1653 #endif
1654 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_NOT_ACCESS_BIND:
1655 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_MODEL_NUM:
1656 if (menuHorizontalPosition == 0)
1657 checkModelIdUnique(g_eeGeneral.currModel, EXTERNAL_MODULE);
1658 break;