Bsongis/companion r9m fixes (#5914)
[opentx.git] / radio / src / gui / 128x64 / model_setup.cpp
blob5c79385c62e33d2e33d3e7555de1c19caf7a74fd
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 #if defined(PCBTARANIS)
29 uint8_t getSwitchWarningsCount()
31 int count = 0;
32 for (int i=0; i<NUM_SWITCHES; ++i) {
33 if (SWITCH_WARNING_ALLOWED(i)) {
34 ++count;
37 return count;
39 #endif
41 enum MenuModelSetupItems {
42 ITEM_MODEL_NAME,
43 ITEM_MODEL_TIMER1,
44 CASE_CPUARM(ITEM_MODEL_TIMER1_NAME)
45 CASE_PERSISTENT_TIMERS(ITEM_MODEL_TIMER1_PERSISTENT)
46 ITEM_MODEL_TIMER1_MINUTE_BEEP,
47 ITEM_MODEL_TIMER1_COUNTDOWN_BEEP,
48 ITEM_MODEL_TIMER2,
49 CASE_CPUARM(ITEM_MODEL_TIMER2_NAME)
50 CASE_PERSISTENT_TIMERS(ITEM_MODEL_TIMER2_PERSISTENT)
51 ITEM_MODEL_TIMER2_MINUTE_BEEP,
52 ITEM_MODEL_TIMER2_COUNTDOWN_BEEP,
53 CASE_CPUARM(ITEM_MODEL_TIMER3)
54 CASE_CPUARM(ITEM_MODEL_TIMER3_NAME)
55 CASE_CPUARM(ITEM_MODEL_TIMER3_PERSISTENT)
56 CASE_CPUARM(ITEM_MODEL_TIMER3_MINUTE_BEEP)
57 CASE_CPUARM(ITEM_MODEL_TIMER3_COUNTDOWN_BEEP)
58 ITEM_MODEL_EXTENDED_LIMITS,
59 ITEM_MODEL_EXTENDED_TRIMS,
60 CASE_CPUARM(ITEM_MODEL_DISPLAY_TRIMS)
61 ITEM_MODEL_TRIM_INC,
62 ITEM_MODEL_THROTTLE_REVERSED,
63 ITEM_MODEL_THROTTLE_TRACE,
64 ITEM_MODEL_THROTTLE_TRIM,
65 CASE_CPUARM(ITEM_MODEL_PREFLIGHT_LABEL)
66 CASE_CPUARM(ITEM_MODEL_CHECKLIST_DISPLAY)
67 ITEM_MODEL_THROTTLE_WARNING,
68 ITEM_MODEL_SWITCHES_WARNING,
69 #if defined(PCBTARANIS)
70 ITEM_MODEL_SWITCHES_WARNING2,
71 ITEM_MODEL_POTS_WARNING,
72 #endif
73 ITEM_MODEL_BEEP_CENTER,
74 CASE_CPUARM(ITEM_MODEL_USE_GLOBAL_FUNCTIONS)
75 #if defined(PCBTARANIS)
76 ITEM_MODEL_INTERNAL_MODULE_LABEL,
77 ITEM_MODEL_INTERNAL_MODULE_MODE,
78 ITEM_MODEL_INTERNAL_MODULE_CHANNELS,
79 ITEM_MODEL_INTERNAL_MODULE_BIND,
80 ITEM_MODEL_INTERNAL_MODULE_FAILSAFE,
81 #if defined(PCBXLITE)
82 ITEM_MODEL_INTERNAL_MODULE_ANTENNA,
83 #endif
84 #endif
85 #if defined(CPUARM)
86 ITEM_MODEL_EXTERNAL_MODULE_LABEL,
87 ITEM_MODEL_EXTERNAL_MODULE_MODE,
88 #if defined(MULTIMODULE)
89 ITEM_MODEL_EXTERNAL_MODULE_SUBTYPE,
90 ITEM_MODEL_EXTERNAL_MODULE_STATUS,
91 ITEM_MODEL_EXTERNAL_MODULE_SYNCSTATUS,
92 #endif
93 ITEM_MODEL_EXTERNAL_MODULE_CHANNELS,
94 ITEM_MODEL_EXTERNAL_MODULE_BIND,
95 #if defined(PCBSKY9X) && defined(REVX)
96 ITEM_MODEL_EXTERNAL_MODULE_OUTPUT_TYPE,
97 #endif
98 ITEM_MODEL_EXTERNAL_MODULE_FAILSAFE,
99 ITEM_MODEL_EXTERNAL_MODULE_OPTIONS,
100 #if defined(MULTIMODULE)
101 ITEM_MODEL_EXTERNAL_MODULE_AUTOBIND,
102 #endif
103 ITEM_MODEL_EXTERNAL_MODULE_POWER,
104 #if defined(PCBSKY9X) && !defined(REVA)
105 ITEM_MODEL_EXTRA_MODULE_LABEL,
106 ITEM_MODEL_EXTRA_MODULE_CHANNELS,
107 ITEM_MODEL_EXTRA_MODULE_BIND,
108 #endif
109 #else
110 ITEM_MODEL_PPM1_PROTOCOL,
111 ITEM_MODEL_PPM1_PARAMS,
112 #endif
113 #if defined(PCBX7)
114 ITEM_MODEL_TRAINER_LABEL,
115 ITEM_MODEL_TRAINER_MODE,
116 #if defined(BLUETOOTH)
117 ITEM_MODEL_TRAINER_BLUETOOTH,
118 #endif
119 ITEM_MODEL_TRAINER_CHANNELS,
120 ITEM_MODEL_TRAINER_PARAMS,
121 #elif defined(PCBXLITE)
122 ITEM_MODEL_TRAINER_LABEL,
123 ITEM_MODEL_TRAINER_MODE,
124 ITEM_MODEL_TRAINER_BLUETOOTH,
125 ITEM_MODEL_TRAINER_CHANNELS,
126 #endif
127 ITEM_MODEL_SETUP_MAX
130 #if defined(PCBSKY9X)
131 #define FIELD_PROTOCOL_MAX 2
132 #else
133 #define FIELD_PROTOCOL_MAX 1
134 #endif
136 #define MODEL_SETUP_2ND_COLUMN (LCD_W-11*FW)
137 #define MODEL_SETUP_BIND_OFS 2*FW+1
138 #define MODEL_SETUP_RANGE_OFS 4*FW+3
139 #define MODEL_SETUP_SET_FAILSAFE_OFS 7*FW-2
141 #if defined(PCBTARANIS)
142 #define CURRENT_MODULE_EDITED(k) (k>=ITEM_MODEL_TRAINER_LABEL ? TRAINER_MODULE : (k>=ITEM_MODEL_EXTERNAL_MODULE_LABEL ? EXTERNAL_MODULE : INTERNAL_MODULE))
143 #elif defined(PCBSKY9X) && !defined(REVA)
144 #define CURRENT_MODULE_EDITED(k) (k>=ITEM_MODEL_EXTRA_MODULE_LABEL ? EXTRA_MODULE : EXTERNAL_MODULE)
145 #else
146 #define CURRENT_MODULE_EDITED(k) (EXTERNAL_MODULE)
147 #endif
149 #if defined(CPUARM)
150 #if defined(PCBXLITE)
151 #define SW_WARN_ROWS uint8_t(NAVIGATION_LINE_BY_LINE|getSwitchWarningsCount()), uint8_t(getSwitchWarningsCount() > 5 ? TITLE_ROW : HIDDEN_ROW) // X-Lite needs an additional column for full line selection (<])
152 #else
153 #define SW_WARN_ROWS uint8_t(NAVIGATION_LINE_BY_LINE|(getSwitchWarningsCount()-1)), uint8_t(getSwitchWarningsCount() > 5 ? TITLE_ROW : HIDDEN_ROW)
154 #endif
155 #if !defined(TARANIS_INTERNAL_PPM)
156 #define INTERNAL_MODULE_MODE_ROWS 0 // (OFF / RF protocols)
157 #else
158 #define INTERNAL_MODULE_MODE_ROWS (IS_MODULE_XJT(INTERNAL_MODULE) ? (uint8_t)1 : (uint8_t)0) // Module type + RF protocols
159 #endif
160 #define IF_INTERNAL_MODULE_ON(x) (IS_INTERNAL_MODULE_ENABLED()? (uint8_t)(x) : HIDDEN_ROW )
161 #define IF_EXTERNAL_MODULE_ON(x) (IS_EXTERNAL_MODULE_ENABLED()? (uint8_t)(x) : HIDDEN_ROW)
162 #define INTERNAL_MODULE_CHANNELS_ROWS IF_INTERNAL_MODULE_ON(1)
163 #define EXTERNAL_MODULE_BIND_ROWS() ((IS_MODULE_XJT(EXTERNAL_MODULE) && IS_D8_RX(EXTERNAL_MODULE)) || IS_MODULE_SBUS(EXTERNAL_MODULE)) ? (uint8_t)1 : (IS_MODULE_PPM(EXTERNAL_MODULE) || IS_MODULE_PXX(EXTERNAL_MODULE) || IS_MODULE_DSM2(EXTERNAL_MODULE) || IS_MODULE_MULTIMODULE(EXTERNAL_MODULE)) ? (uint8_t)2 : HIDDEN_ROW
165 #if defined(PCBSKY9X) && defined(REVX)
166 #define OUTPUT_TYPE_ROWS() (IS_MODULE_PPM(EXTERNAL_MODULE) ? (uint8_t)0 : HIDDEN_ROW) ,
167 #else
168 #define OUTPUT_TYPE_ROWS()
169 #endif
170 #define PORT_CHANNELS_ROWS(x) (x==EXTERNAL_MODULE ? EXTERNAL_MODULE_CHANNELS_ROWS : 0)
172 #define EXTERNAL_MODULE_MODE_ROWS (IS_MODULE_PXX(EXTERNAL_MODULE) || IS_MODULE_DSM2(EXTERNAL_MODULE) || IS_MODULE_MULTIMODULE(EXTERNAL_MODULE)) ? (uint8_t)1 : (uint8_t)0
174 #define CURSOR_ON_CELL (true)
175 #define MODEL_SETUP_MAX_LINES (HEADER_LINE+ITEM_MODEL_SETUP_MAX)
176 #define POT_WARN_ITEMS() ((g_model.potsWarnMode) ? (uint8_t)(NUM_POTS+NUM_SLIDERS) : (uint8_t)0)
177 #define TIMER_ROWS 2, 0, CASE_PERSISTENT_TIMERS(0) 0, 0
178 #if defined(PCBSKY9X) && !defined(REVA)
179 #define EXTRA_MODULE_ROWS LABEL(ExtraModule), 1, 2,
180 #else
181 #define EXTRA_MODULE_ROWS
182 #endif
184 #if defined(PCBX7)
185 #define ANTENNA_ROW
186 #if defined(BLUETOOTH)
187 #define TRAINER_BLUETOOTH_M_ROW ((bluetoothDistantAddr[0] == '\0' || bluetoothState == BLUETOOTH_STATE_CONNECTED) ? (uint8_t)0 : (uint8_t)1)
188 #define TRAINER_BLUETOOTH_S_ROW (bluetoothDistantAddr[0] == '\0' ? HIDDEN_ROW : LABEL())
189 #define TRAINER_BLUETOOTH_ROW (g_model.trainerMode == TRAINER_MODE_MASTER_BLUETOOTH ? TRAINER_BLUETOOTH_M_ROW : (g_model.trainerMode == TRAINER_MODE_SLAVE_BLUETOOTH ? TRAINER_BLUETOOTH_S_ROW : HIDDEN_ROW)),
190 #else
191 #define TRAINER_BLUETOOTH_ROW
192 #endif
193 #define TRAINER_CHANNELS_ROW (IS_SLAVE_TRAINER() ? (uint8_t)1 : HIDDEN_ROW)
194 #define TRAINER_PARAMS_ROW (IS_SLAVE_TRAINER() ? (uint8_t)2 : HIDDEN_ROW)
195 #define TRAINER_ROWS LABEL(Trainer), 0, TRAINER_BLUETOOTH_ROW TRAINER_CHANNELS_ROW, TRAINER_PARAMS_ROW
196 #elif defined(PCBXLITE)
197 #define ANTENNA_ROW IF_INTERNAL_MODULE_ON(0),
198 #define IF_BT_TRAINER_ON(x) (g_eeGeneral.bluetoothMode == BLUETOOTH_TRAINER ? (uint8_t)(x) : HIDDEN_ROW)
199 #define TRAINER_BLUETOOTH_M_ROW ((bluetoothDistantAddr[0] == '\0' || bluetoothState == BLUETOOTH_STATE_CONNECTED) ? (uint8_t)0 : (uint8_t)1)
200 #define TRAINER_BLUETOOTH_S_ROW (bluetoothDistantAddr[0] == '\0' ? HIDDEN_ROW : LABEL())
201 #define TRAINER_BLUETOOTH_ROW (g_model.trainerMode == TRAINER_MODE_MASTER_BLUETOOTH ? TRAINER_BLUETOOTH_M_ROW : (g_model.trainerMode == TRAINER_MODE_SLAVE_BLUETOOTH ? TRAINER_BLUETOOTH_S_ROW : HIDDEN_ROW))
202 #define TRAINER_CHANNELS_ROW (IS_SLAVE_TRAINER() ? (uint8_t)1 : HIDDEN_ROW)
203 #define TRAINER_ROWS IF_BT_TRAINER_ON(LABEL(Trainer)), IF_BT_TRAINER_ON(0), IF_BT_TRAINER_ON(TRAINER_BLUETOOTH_ROW), IF_BT_TRAINER_ON(TRAINER_CHANNELS_ROW)
204 #else
205 #define TRAINER_ROWS
206 #endif
208 #elif defined(CPUM64)
209 #define CURSOR_ON_CELL (true)
210 #define MODEL_SETUP_MAX_LINES ((IS_PPM_PROTOCOL(protocol)||IS_DSM2_PROTOCOL(protocol)||IS_PXX_PROTOCOL(protocol)) ? HEADER_LINE+ITEM_MODEL_SETUP_MAX : HEADER_LINE+ITEM_MODEL_SETUP_MAX-1)
211 #elif defined(PCBXLITE)
212 #define CURSOR_ON_CELL (menuHorizontalPosition >= 0)
213 #define MODEL_SETUP_MAX_LINES ((IS_PPM_PROTOCOL(protocol)||IS_DSM2_PROTOCOL(protocol)||IS_PXX_PROTOCOL(protocol)) ? HEADER_LINE+ITEM_MODEL_SETUP_MAX : HEADER_LINE+ITEM_MODEL_SETUP_MAX-1)
214 #else
215 #define CURSOR_ON_CELL (true)
216 #define MODEL_SETUP_MAX_LINES ((IS_PPM_PROTOCOL(protocol)||IS_DSM2_PROTOCOL(protocol)||IS_PXX_PROTOCOL(protocol)) ? HEADER_LINE+ITEM_MODEL_SETUP_MAX : HEADER_LINE+ITEM_MODEL_SETUP_MAX-1)
217 #endif
219 #if defined(PCBTARANIS)
220 void onBindMenu(const char * result)
222 uint8_t moduleIdx = CURRENT_MODULE_EDITED(menuVerticalPosition);
224 if (result == STR_BINDING_1_8_TELEM_ON) {
225 g_model.moduleData[moduleIdx].pxx.receiver_telem_off = false;
226 g_model.moduleData[moduleIdx].pxx.receiver_channel_9_16 = false;
228 else if (result == STR_BINDING_1_8_TELEM_OFF) {
229 g_model.moduleData[moduleIdx].pxx.receiver_telem_off = true;
230 g_model.moduleData[moduleIdx].pxx.receiver_channel_9_16 = false;
232 else if (result == STR_BINDING_9_16_TELEM_ON) {
233 g_model.moduleData[moduleIdx].pxx.receiver_telem_off = false;
234 g_model.moduleData[moduleIdx].pxx.receiver_channel_9_16 = true;
236 else if (result == STR_BINDING_9_16_TELEM_OFF) {
237 g_model.moduleData[moduleIdx].pxx.receiver_telem_off = true;
238 g_model.moduleData[moduleIdx].pxx.receiver_channel_9_16 = true;
240 else {
241 return;
244 moduleFlag[moduleIdx] = MODULE_BIND;
246 #endif
249 void menuModelSetup(event_t event)
251 #if defined(PCBXLITE)
252 // Switch to external antenna confirmation
253 if (warningResult) {
254 warningResult = 0;
255 g_model.moduleData[INTERNAL_MODULE].pxx.external_antenna = XJT_EXTERNAL_ANTENNA;
257 #endif
259 #if defined(CPUARM)
260 static uint8_t selectedPxxPower = g_model.moduleData[EXTERNAL_MODULE].pxx.power; //TODO could go to the reusable struct
261 #endif
263 int8_t old_editMode = s_editMode;
265 #if defined(PCBTARANIS)
266 MENU_TAB({
267 HEADER_LINE_COLUMNS
269 TIMER_ROWS, TIMER_ROWS, TIMER_ROWS,
270 0, // Extended limits
271 1, // Extended trims
272 0, // Show trims
273 0, // Trims step
274 0, // Throttle reverse
275 0, // Throttle trace source
276 0, // Throttle trim
277 CASE_CPUARM(LABEL(PreflightCheck))
278 CASE_CPUARM(0) // Checklist
279 0, // Throttle warning
280 SW_WARN_ROWS, // Switch warning
281 POT_WARN_ITEMS(), // Pot warning
282 NUM_STICKS + NUM_POTS + NUM_SLIDERS + NUM_ROTARY_ENCODERS - 1, // Center beeps
283 0, // Global functions
284 LABEL(InternalModule),
285 INTERNAL_MODULE_MODE_ROWS,
286 INTERNAL_MODULE_CHANNELS_ROWS,
287 IF_INTERNAL_MODULE_ON(HAS_RF_PROTOCOL_MODELINDEX(g_model.moduleData[INTERNAL_MODULE].rfProtocol) ? (uint8_t)2 : (uint8_t)1),
288 IF_INTERNAL_MODULE_ON(FAILSAFE_ROWS(INTERNAL_MODULE)),
289 ANTENNA_ROW
290 LABEL(ExternalModule),
291 EXTERNAL_MODULE_MODE_ROWS,
292 MULTIMODULE_SUBTYPE_ROWS(EXTERNAL_MODULE)
293 MULTIMODULE_STATUS_ROWS
294 EXTERNAL_MODULE_CHANNELS_ROWS,
295 EXTERNAL_MODULE_BIND_ROWS(),
296 OUTPUT_TYPE_ROWS()
297 FAILSAFE_ROWS(EXTERNAL_MODULE),
298 EXTERNAL_MODULE_OPTION_ROW,
299 MULTIMODULE_MODULE_ROWS
300 EXTERNAL_MODULE_POWER_ROW,
301 EXTRA_MODULE_ROWS
302 TRAINER_ROWS });
303 #elif defined(CPUARM)
304 MENU_TAB({ HEADER_LINE_COLUMNS 0, TIMER_ROWS, TIMER_ROWS, TIMER_ROWS, 0, 1, 0, 0, 0, 0, 0, CASE_CPUARM(LABEL(PreflightCheck)) CASE_CPUARM(0) 0, NUM_SWITCHES-1, NUM_STICKS+NUM_POTS+NUM_SLIDERS+NUM_ROTARY_ENCODERS-1, 0,
305 LABEL(ExternalModule),
306 EXTERNAL_MODULE_MODE_ROWS,
307 MULTIMODULE_SUBTYPE_ROWS(EXTERNAL_MODULE)
308 MULTIMODULE_STATUS_ROWS
309 EXTERNAL_MODULE_CHANNELS_ROWS,
310 EXTERNAL_MODULE_BIND_ROWS(),
311 OUTPUT_TYPE_ROWS()
312 FAILSAFE_ROWS(EXTERNAL_MODULE),
313 EXTERNAL_MODULE_OPTION_ROW,
314 MULTIMODULE_MODULE_ROWS
315 EXTERNAL_MODULE_POWER_ROW,
316 EXTRA_MODULE_ROWS
317 TRAINER_ROWS });
318 #elif defined(CPUM64)
319 uint8_t protocol = g_model.protocol;
320 MENU_TAB({ HEADER_LINE_COLUMNS 0, 2, CASE_PERSISTENT_TIMERS(0) 0, 0, 2, CASE_PERSISTENT_TIMERS(0) 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, NUM_STICKS+NUM_POTS+NUM_SLIDERS+NUM_ROTARY_ENCODERS-1, FIELD_PROTOCOL_MAX, 2 });
321 #else
322 uint8_t protocol = g_model.protocol;
323 MENU_TAB({ HEADER_LINE_COLUMNS 0, 2, CASE_PERSISTENT_TIMERS(0) 0, 0, 2, CASE_PERSISTENT_TIMERS(0) 0, 0, 0, 1, 0, 0, 0, 0, 0, NUM_SWITCHES, NUM_STICKS+NUM_POTS+NUM_SLIDERS+NUM_ROTARY_ENCODERS-1, FIELD_PROTOCOL_MAX, 2, CASE_PCBSKY9X(1) CASE_PCBSKY9X(2) });
324 #endif
326 MENU_CHECK(menuTabModel, MENU_MODEL_SETUP, HEADER_LINE+MODEL_SETUP_MAX_LINES);
328 #if defined(CPUARM) && (defined(DSM2) || defined(PXX))
329 if (menuEvent) {
330 moduleFlag[0] = 0;
331 #if NUM_MODULES > 1
332 moduleFlag[1] = 0;
333 #endif
335 #endif
337 TITLE(STR_MENUSETUP);
339 uint8_t sub = menuVerticalPosition - HEADER_LINE;
340 int8_t editMode = s_editMode;
342 for (uint8_t i=0; i<NUM_BODY_LINES; ++i) {
343 coord_t y = MENU_HEADER_HEIGHT + 1 + i*FH;
344 uint8_t k = i + menuVerticalOffset;
345 #if defined(CPUARM)
346 for (int j=0; j<=k; j++) {
347 if (mstate_tab[j+HEADER_LINE] == HIDDEN_ROW) {
348 if (++k >= (int)DIM(mstate_tab)) {
349 return;
353 #endif
355 LcdFlags blink = ((editMode>0) ? BLINK|INVERS : INVERS);
356 LcdFlags attr = (sub == k ? blink : 0);
358 switch (k) {
359 case ITEM_MODEL_NAME:
360 editSingleName(MODEL_SETUP_2ND_COLUMN, y, STR_MODELNAME, g_model.header.name, sizeof(g_model.header.name), event, attr);
361 #if defined(CPUARM)
362 memcpy(modelHeaders[g_eeGeneral.currModel].name, g_model.header.name, sizeof(g_model.header.name));
363 #endif
364 break;
366 #if defined(CPUARM)
367 case ITEM_MODEL_TIMER1:
368 case ITEM_MODEL_TIMER2:
369 case ITEM_MODEL_TIMER3:
371 unsigned int timerIdx = (k>=ITEM_MODEL_TIMER3 ? 2 : (k>=ITEM_MODEL_TIMER2 ? 1 : 0));
372 TimerData * timer = &g_model.timers[timerIdx];
373 drawStringWithIndex(0*FW, y, STR_TIMER, timerIdx+1);
374 drawTimerMode(MODEL_SETUP_2ND_COLUMN, y, timer->mode, menuHorizontalPosition==0 ? attr : 0);
375 drawTimer(MODEL_SETUP_2ND_COLUMN+5*FW-2+5*FWNUM+1, y, timer->start, RIGHT | (menuHorizontalPosition==1 ? attr : 0), menuHorizontalPosition==2 ? attr : 0);
376 if (attr && (editMode>0 || p1valdiff)) {
377 div_t qr = div(timer->start, 60);
378 switch (menuHorizontalPosition) {
379 case 0:
381 int8_t timerMode = timer->mode;
382 if (timerMode < 0) timerMode -= TMRMODE_COUNT-1;
383 CHECK_INCDEC_MODELVAR_CHECK(event, timerMode, -TMRMODE_COUNT-SWSRC_LAST+1, TMRMODE_COUNT+SWSRC_LAST-1, isSwitchAvailableInTimers);
384 if (timerMode < 0) timerMode += TMRMODE_COUNT-1;
385 timer->mode = timerMode;
386 #if defined(AUTOSWITCH)
387 if (s_editMode>0) {
388 int8_t val = timer->mode - (TMRMODE_COUNT-1);
389 int8_t switchVal = checkIncDecMovedSwitch(val);
390 if (val != switchVal) {
391 timer->mode = switchVal + (TMRMODE_COUNT-1);
392 storageDirty(EE_MODEL);
395 #endif
396 break;
398 case 1:
399 CHECK_INCDEC_MODELVAR_ZERO(event, qr.quot, 539); // 8:59
400 timer->start = qr.rem + qr.quot*60;
401 break;
402 case 2:
403 qr.rem -= checkIncDecModel(event, qr.rem+2, 1, 62)-2;
404 timer->start -= qr.rem ;
405 if ((int16_t)timer->start < 0) timer->start=0;
406 if ((int16_t)timer->start > 5999) timer->start=32399; // 8:59:59
407 break;
410 break;
413 case ITEM_MODEL_TIMER1_NAME:
414 case ITEM_MODEL_TIMER2_NAME:
415 case ITEM_MODEL_TIMER3_NAME:
417 TimerData * timer = &g_model.timers[k>=ITEM_MODEL_TIMER3 ? 2 : (k>=ITEM_MODEL_TIMER2 ? 1 : 0)];
418 editSingleName(MODEL_SETUP_2ND_COLUMN, y, STR_TIMER_NAME, timer->name, sizeof(timer->name), event, attr);
419 break;
422 case ITEM_MODEL_TIMER1_MINUTE_BEEP:
423 case ITEM_MODEL_TIMER2_MINUTE_BEEP:
424 case ITEM_MODEL_TIMER3_MINUTE_BEEP:
426 TimerData * timer = &g_model.timers[k>=ITEM_MODEL_TIMER3 ? 2 : (k>=ITEM_MODEL_TIMER2 ? 1 : 0)];
427 timer->minuteBeep = editCheckBox(timer->minuteBeep, MODEL_SETUP_2ND_COLUMN, y, STR_MINUTEBEEP, attr, event);
428 break;
431 case ITEM_MODEL_TIMER1_COUNTDOWN_BEEP:
432 case ITEM_MODEL_TIMER2_COUNTDOWN_BEEP:
433 case ITEM_MODEL_TIMER3_COUNTDOWN_BEEP:
435 TimerData * timer = &g_model.timers[k>=ITEM_MODEL_TIMER3 ? 2 : (k>=ITEM_MODEL_TIMER2 ? 1 : 0)];
436 timer->countdownBeep = editChoice(MODEL_SETUP_2ND_COLUMN, y, STR_BEEPCOUNTDOWN, STR_VBEEPCOUNTDOWN, timer->countdownBeep, COUNTDOWN_SILENT, COUNTDOWN_COUNT-1, attr, event);
437 break;
440 case ITEM_MODEL_TIMER1_PERSISTENT:
441 case ITEM_MODEL_TIMER2_PERSISTENT:
442 case ITEM_MODEL_TIMER3_PERSISTENT:
444 TimerData * timer = &g_model.timers[k>=ITEM_MODEL_TIMER3 ? 2 : (k>=ITEM_MODEL_TIMER2 ? 1 : 0)];
445 timer->persistent = editChoice(MODEL_SETUP_2ND_COLUMN, y, STR_PERSISTENT, STR_VPERSISTENT, timer->persistent, 0, 2, attr, event);
446 break;
448 #else // AVR
449 case ITEM_MODEL_TIMER1:
450 case ITEM_MODEL_TIMER2:
451 case ITEM_MODEL_TIMER1_MINUTE_BEEP:
452 case ITEM_MODEL_TIMER2_MINUTE_BEEP:
453 case ITEM_MODEL_TIMER1_COUNTDOWN_BEEP:
454 case ITEM_MODEL_TIMER2_COUNTDOWN_BEEP:
456 TimerData *timer = &g_model.timers[k>=ITEM_MODEL_TIMER2 ? 1 : 0];
457 if (k==ITEM_MODEL_TIMER1_MINUTE_BEEP || k==ITEM_MODEL_TIMER2_MINUTE_BEEP) {
458 timer->minuteBeep = editCheckBox(timer->minuteBeep, MODEL_SETUP_2ND_COLUMN, y, STR_MINUTEBEEP, attr, event);
460 else if (k==ITEM_MODEL_TIMER1_COUNTDOWN_BEEP || k==ITEM_MODEL_TIMER2_COUNTDOWN_BEEP) {
461 timer->countdownBeep = editCheckBox(timer->countdownBeep, MODEL_SETUP_2ND_COLUMN, y, STR_BEEPCOUNTDOWN, attr, event);
463 else {
464 drawStringWithIndex(0*FW, y, STR_TIMER, k>=ITEM_MODEL_TIMER2 ? 2 : 1);
465 drawTimerMode(MODEL_SETUP_2ND_COLUMN, y, timer->mode, menuHorizontalPosition==0 ? attr : 0);
466 drawTimer(MODEL_SETUP_2ND_COLUMN+5*FW-2+5*FWNUM+1, y, timer->start, menuHorizontalPosition==1 ? attr : 0, menuHorizontalPosition==2 ? attr : 0);
467 if (attr && (editMode>0 || p1valdiff)) {
468 div_t qr = div(timer->start, 60);
469 switch (menuHorizontalPosition) {
470 case 0:
471 CHECK_INCDEC_MODELVAR_CHECK(event, timer->mode, SWSRC_FIRST, TMRMODE_COUNT+SWSRC_LAST-1/*SWSRC_None removed*/, isSwitchAvailableInTimers);
472 break;
473 case 1:
474 CHECK_INCDEC_MODELVAR_ZERO(event, qr.quot, 59);
475 timer->start = qr.rem + qr.quot*60;
476 break;
477 case 2:
478 qr.rem -= checkIncDecModel(event, qr.rem+2, 1, 62) - 2;
479 if ((int16_t)timer->start >= qr.rem) {
480 timer->start -= qr.rem ;
482 if ((int16_t)timer->start > 3599) {
483 timer->start = 3599; // 59:59
485 break;
489 break;
492 #if defined(CPUM2560)
493 case ITEM_MODEL_TIMER1_PERSISTENT:
494 case ITEM_MODEL_TIMER2_PERSISTENT:
496 TimerData &timer = g_model.timers[k==ITEM_MODEL_TIMER2_PERSISTENT];
497 timer.persistent = editChoice(MODEL_SETUP_2ND_COLUMN, y, STR_PERSISTENT, STR_VPERSISTENT, timer.persistent, 0, 2, attr, event);
498 break;
500 #endif
501 #endif
503 case ITEM_MODEL_EXTENDED_LIMITS:
504 ON_OFF_MENU_ITEM(g_model.extendedLimits, MODEL_SETUP_2ND_COLUMN, y, STR_ELIMITS, attr, event);
505 break;
507 case ITEM_MODEL_EXTENDED_TRIMS:
508 #if defined(CPUM64)
509 ON_OFF_MENU_ITEM(g_model.extendedTrims, MODEL_SETUP_2ND_COLUMN, y, STR_ETRIMS, attr, event);
510 #else
511 ON_OFF_MENU_ITEM(g_model.extendedTrims, MODEL_SETUP_2ND_COLUMN, y, STR_ETRIMS, menuHorizontalPosition<=0 ? attr : 0, event==EVT_KEY_BREAK(KEY_ENTER) ? event : 0);
512 lcdDrawText(MODEL_SETUP_2ND_COLUMN+4*FW, y, STR_RESET_BTN, (menuHorizontalPosition>0 && !NO_HIGHLIGHT()) ? attr : 0);
513 if (attr && menuHorizontalPosition>0) {
514 s_editMode = 0;
515 if (event==EVT_KEY_LONG(KEY_ENTER)) {
516 START_NO_HIGHLIGHT();
517 for (uint8_t i=0; i<MAX_FLIGHT_MODES; i++) {
518 memclear(&g_model.flightModeData[i], TRIMS_ARRAY_SIZE);
520 storageDirty(EE_MODEL);
521 AUDIO_WARNING1();
524 #endif
525 break;
527 #if defined(CPUARM)
528 case ITEM_MODEL_DISPLAY_TRIMS:
529 g_model.displayTrims = editChoice(MODEL_SETUP_2ND_COLUMN, y, STR_DISPLAY_TRIMS, STR_VDISPLAYTRIMS, g_model.displayTrims, 0, 2, attr, event);
530 break;
531 #endif
533 case ITEM_MODEL_TRIM_INC:
534 g_model.trimInc = editChoice(MODEL_SETUP_2ND_COLUMN, y, STR_TRIMINC, STR_VTRIMINC, g_model.trimInc, -2, 2, attr, event);
535 break;
537 case ITEM_MODEL_THROTTLE_REVERSED:
538 ON_OFF_MENU_ITEM(g_model.throttleReversed, MODEL_SETUP_2ND_COLUMN, y, STR_THROTTLEREVERSE, attr, event ) ;
539 break;
541 case ITEM_MODEL_THROTTLE_TRACE:
543 lcdDrawTextAlignedLeft(y, STR_TTRACE);
544 if (attr) CHECK_INCDEC_MODELVAR_ZERO(event, g_model.thrTraceSrc, NUM_POTS+NUM_SLIDERS+MAX_OUTPUT_CHANNELS);
545 uint8_t idx = g_model.thrTraceSrc + MIXSRC_Thr;
546 if (idx > MIXSRC_Thr)
547 idx += 1;
548 if (idx >= MIXSRC_FIRST_POT+NUM_POTS+NUM_SLIDERS)
549 idx += MIXSRC_CH1 - MIXSRC_FIRST_POT - NUM_POTS - NUM_SLIDERS;
550 drawSource(MODEL_SETUP_2ND_COLUMN, y, idx, attr);
551 break;
554 case ITEM_MODEL_THROTTLE_TRIM:
555 ON_OFF_MENU_ITEM(g_model.thrTrim, MODEL_SETUP_2ND_COLUMN, y, STR_TTRIM, attr, event);
556 break;
558 #if defined(CPUARM)
559 case ITEM_MODEL_PREFLIGHT_LABEL:
560 lcdDrawTextAlignedLeft(y, STR_PREFLIGHT);
561 break;
563 case ITEM_MODEL_CHECKLIST_DISPLAY:
564 ON_OFF_MENU_ITEM(g_model.displayChecklist, MODEL_SETUP_2ND_COLUMN, y, STR_CHECKLIST, attr, event);
565 break;
566 #endif
568 case ITEM_MODEL_THROTTLE_WARNING:
569 g_model.disableThrottleWarning = !editCheckBox(!g_model.disableThrottleWarning, MODEL_SETUP_2ND_COLUMN, y, STR_THROTTLEWARNING, attr, event);
570 break;
572 #if defined(PCBTARANIS)
573 case ITEM_MODEL_SWITCHES_WARNING2:
574 if (i==0) {
575 if (CURSOR_MOVED_LEFT(event))
576 menuVerticalOffset--;
577 else
578 menuVerticalOffset++;
580 break;
581 #endif
583 case ITEM_MODEL_SWITCHES_WARNING:
584 #if defined(PCBTARANIS)
586 #define FIRSTSW_STR STR_VSRCRAW+(MIXSRC_FIRST_SWITCH-MIXSRC_Rud+1)*length
587 uint8_t length = pgm_read_byte(STR_VSRCRAW);
588 horzpos_t l_posHorz = menuHorizontalPosition;
590 if (i>=NUM_BODY_LINES-2 && getSwitchWarningsCount() > 5*(NUM_BODY_LINES-i)) {
591 if (CURSOR_MOVED_LEFT(event))
592 menuVerticalOffset--;
593 else
594 menuVerticalOffset++;
595 break;
598 swarnstate_t states = g_model.switchWarningState;
599 char c;
601 lcdDrawTextAlignedLeft(y, STR_SWITCHWARNING);
602 #if defined(PCBXLITE)
603 lcdDrawText(LCD_W, y, "<]", RIGHT);
604 if (attr) {
605 if (menuHorizontalPosition > NUM_SWITCHES)
606 menuHorizontalPosition = NUM_SWITCHES;
608 if (attr && menuHorizontalPosition == NUM_SWITCHES) {
609 #else
610 if (attr) {
611 #endif
612 s_editMode = 0;
613 if (!READ_ONLY()) {
614 switch (event) {
615 case EVT_KEY_BREAK(KEY_ENTER):
616 break;
618 case EVT_KEY_LONG(KEY_ENTER):
619 if (menuHorizontalPosition < 0 || menuHorizontalPosition >= NUM_SWITCHES) {
620 START_NO_HIGHLIGHT();
621 getMovedSwitch();
622 g_model.switchWarningState = switches_states;
623 AUDIO_WARNING1();
624 storageDirty(EE_MODEL);
626 killEvents(event);
627 break;
632 LcdFlags line = attr;
634 int current = 0;
635 for (int i=0; i<NUM_SWITCHES; i++) {
636 if (SWITCH_WARNING_ALLOWED(i)) {
637 div_t qr = div(current, 5);
638 if (!READ_ONLY() && event==EVT_KEY_BREAK(KEY_ENTER) && line && l_posHorz==current) {
639 g_model.switchWarningEnable ^= (1 << i);
640 storageDirty(EE_MODEL);
641 #if defined(PCBXLITE)
642 s_editMode = 0;
643 #endif
645 uint8_t swactive = !(g_model.switchWarningEnable & (1<<i));
646 c = "\300-\301"[states & 0x03];
647 //lcdDrawChar(MODEL_SETUP_2ND_COLUMN+qr.rem*(2*FW+1), y+FH*qr.quot, 'A'+i, line && (menuHorizontalPosition==current) ? INVERS : 0);
648 lcdDrawSizedText(MODEL_SETUP_2ND_COLUMN + qr.rem*((2*FW)+1), y+FH*qr.quot, FIRSTSW_STR+(i*length)+3, 1, line && (menuHorizontalPosition==current) ? INVERS : 0);
649 if (swactive) lcdDrawChar(lcdNextPos, y+FH*qr.quot, c);
650 ++current;
652 states >>= 2;
654 if (attr && ((menuHorizontalPosition < 0) || menuHorizontalPosition >= NUM_SWITCHES)) {
655 lcdDrawFilledRect(MODEL_SETUP_2ND_COLUMN-1, y-1, 8*(2*FW+1), 1+FH*((current+4)/5));
657 #else //PCBTARANIS
659 lcdDrawTextAlignedLeft(y, STR_SWITCHWARNING);
660 swarnstate_t states = g_model.switchWarningState;
661 char c;
662 if (attr) {
663 s_editMode = 0;
664 if (!READ_ONLY()) {
665 switch (event) {
666 CASE_EVT_ROTARY_BREAK
667 case EVT_KEY_BREAK(KEY_ENTER):
668 #if defined(CPUM64)
669 g_model.switchWarningEnable ^= (1 << menuHorizontalPosition);
670 storageDirty(EE_MODEL);
671 #else
672 if (menuHorizontalPosition < NUM_SWITCHES-1) {
673 g_model.switchWarningEnable ^= (1 << menuHorizontalPosition);
674 storageDirty(EE_MODEL);
676 #endif
677 break;
679 case EVT_KEY_LONG(KEY_ENTER):
680 #if defined(CPUM64)
681 getMovedSwitch();
682 g_model.switchWarningState = switches_states;
683 AUDIO_WARNING1();
684 storageDirty(EE_MODEL);
685 #else
686 if (menuHorizontalPosition == NUM_SWITCHES-1) {
687 START_NO_HIGHLIGHT();
688 getMovedSwitch();
689 g_model.switchWarningState = switches_states;
690 AUDIO_WARNING1();
691 storageDirty(EE_MODEL);
693 #endif
694 killEvents(event);
695 break;
699 LcdFlags line = attr;
701 for (uint8_t i=0; i<NUM_SWITCHES-1/*not on TRN switch*/; i++) {
702 uint8_t swactive = !(g_model.switchWarningEnable & 1 << i);
703 attr = 0;
705 if (IS_3POS(i)) {
706 c = '0'+(states & 0x03);
707 states >>= 2;
709 else {
710 if ((states & 0x01) && swactive)
711 attr = INVERS;
712 c = pgm_read_byte(STR_VSWITCHES - 2 + 9 + (3*(i+1)));
713 states >>= 1;
715 if (line && (menuHorizontalPosition == i)) {
716 attr = BLINK | INVERS;
718 #if defined(CPUARM)
719 lcdDrawChar(MODEL_SETUP_2ND_COLUMN+i*FW, y, (swactive) ? c : '-', attr);
720 #else
721 lcdDrawChar(MODEL_SETUP_2ND_COLUMN+i*FW, y, (swactive || (attr & BLINK)) ? c : '-', attr);
722 #endif
723 #if !defined(CPUM64)
724 lcdDrawText(MODEL_SETUP_2ND_COLUMN+(NUM_SWITCHES*FW), y, PSTR("<]"), (menuHorizontalPosition == NUM_SWITCHES-1 && !NO_HIGHLIGHT()) ? line : 0);
725 #endif
727 #endif
728 break;
730 #if defined(PCBTARANIS)
731 case ITEM_MODEL_POTS_WARNING:
732 lcdDrawTextAlignedLeft(y, STR_POTWARNING);
733 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, PSTR("\004""OFF\0""Man\0""Auto"), g_model.potsWarnMode, (menuHorizontalPosition == 0) ? attr : 0);
734 if (attr && (menuHorizontalPosition == 0)) {
735 CHECK_INCDEC_MODELVAR(event, g_model.potsWarnMode, POTS_WARN_OFF, POTS_WARN_AUTO);
736 storageDirty(EE_MODEL);
739 if (attr) {
740 if (menuHorizontalPosition > 0) s_editMode = 0;
741 if (!READ_ONLY() && menuHorizontalPosition > 0) {
742 switch (event) {
743 case EVT_KEY_LONG(KEY_ENTER):
744 killEvents(event);
745 if (g_model.potsWarnMode == POTS_WARN_MANUAL) {
746 SAVE_POT_POSITION(menuHorizontalPosition-1);
747 AUDIO_WARNING1();
748 storageDirty(EE_MODEL);
750 break;
751 case EVT_KEY_BREAK(KEY_ENTER):
752 g_model.potsWarnEnabled ^= (1 << (menuHorizontalPosition-1));
753 storageDirty(EE_MODEL);
754 break;
758 if (g_model.potsWarnMode) {
759 coord_t x = MODEL_SETUP_2ND_COLUMN+28;
760 for (int i=0; i<NUM_POTS+NUM_SLIDERS; ++i) {
761 if (i<NUM_XPOTS && !IS_POT_SLIDER_AVAILABLE(POT1+i)) {
762 if (attr && (menuHorizontalPosition==i+1)) REPEAT_LAST_CURSOR_MOVE();
764 else {
765 LcdFlags flags = ((menuHorizontalPosition==i+1) && attr) ? BLINK : 0;
766 if ((!attr || menuHorizontalPosition >= 0) && !(g_model.potsWarnEnabled & (1 << i))) {
767 flags |= INVERS;
770 // TODO add a new function
771 lcdDrawSizedText(x, y, STR_VSRCRAW+2+STR_VSRCRAW[0]*(NUM_STICKS+1+i), STR_VSRCRAW[0]-1, flags & ~ZCHAR);
772 x = lcdNextPos+3;
776 break;
777 #endif
779 case ITEM_MODEL_BEEP_CENTER:
780 lcdDrawTextAlignedLeft(y, STR_BEEPCTR);
781 for (uint8_t i=0; i<NUM_STICKS+NUM_POTS+NUM_SLIDERS+NUM_ROTARY_ENCODERS; i++) {
782 // TODO flash saving, \001 not needed in STR_RETA123
783 coord_t x = MODEL_SETUP_2ND_COLUMN+i*FW;
784 lcdDrawTextAtIndex(x, y, STR_RETA123, i, ((menuHorizontalPosition==i) && attr) ? BLINK|INVERS : (((g_model.beepANACenter & ((BeepANACenter)1<<i)) || (attr && CURSOR_ON_LINE())) ? INVERS : 0 ) );
786 if (attr && CURSOR_ON_CELL) {
787 if (event==EVT_KEY_BREAK(KEY_ENTER) || p1valdiff) {
788 if (READ_ONLY_UNLOCKED()) {
789 s_editMode = 0;
790 g_model.beepANACenter ^= ((BeepANACenter)1<<menuHorizontalPosition);
791 storageDirty(EE_MODEL);
795 break;
797 #if defined(CPUARM)
798 case ITEM_MODEL_USE_GLOBAL_FUNCTIONS:
799 lcdDrawTextAlignedLeft(y, STR_USE_GLOBAL_FUNCS);
800 drawCheckBox(MODEL_SETUP_2ND_COLUMN, y, !g_model.noGlobalFunctions, attr);
801 if (attr) g_model.noGlobalFunctions = !checkIncDecModel(event, !g_model.noGlobalFunctions, 0, 1);
802 break;
803 #endif
805 #if defined(PCBTARANIS)
806 case ITEM_MODEL_INTERNAL_MODULE_LABEL:
807 lcdDrawTextAlignedLeft(y, TR_INTERNALRF);
808 break;
810 case ITEM_MODEL_INTERNAL_MODULE_MODE:
811 lcdDrawTextAlignedLeft(y, STR_MODE);
812 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_XJT_PROTOCOLS, 1+g_model.moduleData[0].rfProtocol, attr);
813 if (attr) {
814 g_model.moduleData[INTERNAL_MODULE].rfProtocol = checkIncDec(event, g_model.moduleData[INTERNAL_MODULE].rfProtocol, RF_PROTO_OFF, RF_PROTO_LAST, EE_MODEL, isRfProtocolAvailable);
815 if (checkIncDec_Ret) {
816 g_model.moduleData[0].type = MODULE_TYPE_XJT;
817 g_model.moduleData[0].channelsStart = 0;
818 g_model.moduleData[0].channelsCount = DEFAULT_CHANNELS(INTERNAL_MODULE);
819 if (g_model.moduleData[INTERNAL_MODULE].rfProtocol == RF_PROTO_OFF)
820 g_model.moduleData[INTERNAL_MODULE].type = MODULE_TYPE_NONE;
823 break;
824 #endif
826 #if defined(PCBSKY9X)
827 case ITEM_MODEL_EXTRA_MODULE_LABEL:
828 lcdDrawTextAlignedLeft(y, "RF Port 2 (PPM)");
829 break;
830 #endif
832 #if defined(CPUARM)
833 case ITEM_MODEL_EXTERNAL_MODULE_LABEL:
834 lcdDrawTextAlignedLeft(y, TR_EXTERNALRF);
835 break;
837 case ITEM_MODEL_EXTERNAL_MODULE_MODE:
838 lcdDrawTextAlignedLeft(y, STR_MODE);
839 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_TARANIS_PROTOCOLS, g_model.moduleData[EXTERNAL_MODULE].type, menuHorizontalPosition==0 ? attr : 0);
840 if (IS_MODULE_XJT(EXTERNAL_MODULE))
841 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN+5*FW, y, STR_XJT_PROTOCOLS, 1+g_model.moduleData[EXTERNAL_MODULE].rfProtocol, menuHorizontalPosition==1 ? attr : 0);
842 else if (IS_MODULE_DSM2(EXTERNAL_MODULE))
843 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN+5*FW, y, STR_DSM_PROTOCOLS, g_model.moduleData[EXTERNAL_MODULE].rfProtocol, menuHorizontalPosition==1 ? attr : 0);
844 else if (IS_MODULE_R9M(EXTERNAL_MODULE))
845 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN+5*FW, y, STR_R9M_REGION, g_model.moduleData[EXTERNAL_MODULE].subType, (menuHorizontalPosition==1 ? attr : 0));
846 #if defined(MULTIMODULE)
847 else if (IS_MODULE_MULTIMODULE(EXTERNAL_MODULE)) {
848 int multi_rfProto = g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(false);
849 if (g_model.moduleData[EXTERNAL_MODULE].multi.customProto)
850 lcdDrawText(MODEL_SETUP_2ND_COLUMN+5*FW, y, STR_MULTI_CUSTOM, menuHorizontalPosition==1 ? attr : 0);
851 else
852 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN+5*FW, y, STR_MULTI_PROTOCOLS, multi_rfProto, menuHorizontalPosition==1 ? attr : 0);
854 #endif
855 if (attr && (editMode>0 || p1valdiff)) {
856 switch (menuHorizontalPosition) {
857 case 0:
858 g_model.moduleData[EXTERNAL_MODULE].type = checkIncDec(event, g_model.moduleData[EXTERNAL_MODULE].type, MODULE_TYPE_NONE, IS_TRAINER_EXTERNAL_MODULE() ? MODULE_TYPE_NONE : MODULE_TYPE_COUNT-1, EE_MODEL, isModuleAvailable);
859 if (checkIncDec_Ret) {
860 g_model.moduleData[EXTERNAL_MODULE].rfProtocol = 0;
861 g_model.moduleData[EXTERNAL_MODULE].channelsStart = 0;
862 g_model.moduleData[EXTERNAL_MODULE].channelsCount = DEFAULT_CHANNELS(EXTERNAL_MODULE);
863 if (IS_MODULE_SBUS(EXTERNAL_MODULE))
864 g_model.moduleData[EXTERNAL_MODULE].sbus.refreshRate = -31;
866 break;
867 case 1:
868 if (IS_MODULE_DSM2(EXTERNAL_MODULE))
869 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[EXTERNAL_MODULE].rfProtocol, DSM2_PROTO_LP45, DSM2_PROTO_DSMX);
870 else if (IS_MODULE_R9M(EXTERNAL_MODULE))
871 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[EXTERNAL_MODULE].subType, MODULE_SUBTYPE_R9M_FCC, MODULE_SUBTYPE_R9M_LBT);
872 #if defined(MULTIMODULE)
873 else if (IS_MODULE_MULTIMODULE(EXTERNAL_MODULE)) {
874 int multiRfProto = g_model.moduleData[EXTERNAL_MODULE].multi.customProto == 1 ? MM_RF_PROTO_CUSTOM : g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(false);
875 CHECK_INCDEC_MODELVAR(event, multiRfProto, MM_RF_PROTO_FIRST, MM_RF_PROTO_LAST);
876 if (checkIncDec_Ret) {
877 g_model.moduleData[EXTERNAL_MODULE].multi.customProto = (multiRfProto == MM_RF_PROTO_CUSTOM);
878 if (!g_model.moduleData[EXTERNAL_MODULE].multi.customProto)
879 g_model.moduleData[EXTERNAL_MODULE].setMultiProtocol(multiRfProto);
880 g_model.moduleData[EXTERNAL_MODULE].subType = 0;
881 // Sensible default for DSM2 (same as for ppm): 7ch@22ms + Autodetect settings enabled
882 if (g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(true) == MM_RF_PROTO_DSM2) {
883 g_model.moduleData[EXTERNAL_MODULE].multi.autoBindMode = 1;
885 else {
886 g_model.moduleData[EXTERNAL_MODULE].multi.autoBindMode = 0;
888 g_model.moduleData[EXTERNAL_MODULE].multi.optionValue = 0;
891 #endif
892 else {
893 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[EXTERNAL_MODULE].rfProtocol, RF_PROTO_X16, RF_PROTO_LAST);
895 if (checkIncDec_Ret) {
896 g_model.moduleData[EXTERNAL_MODULE].channelsStart = 0;
897 g_model.moduleData[EXTERNAL_MODULE].channelsCount = DEFAULT_CHANNELS(EXTERNAL_MODULE);
901 break;
902 #endif
904 #if defined(MULTIMODULE)
905 case ITEM_MODEL_EXTERNAL_MODULE_SUBTYPE:
907 lcdDrawTextAlignedLeft(y, STR_SUBTYPE);
908 uint8_t multi_rfProto = g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(true);
909 const mm_protocol_definition * pdef = getMultiProtocolDefinition(multi_rfProto);
911 if (multi_rfProto == MM_RF_CUSTOM_SELECTED) {
912 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN + 3 * FW, y, g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(false), RIGHT | (menuHorizontalPosition == 0 ? attr : 0), 2);
913 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN + 5 * FW, y, g_model.moduleData[EXTERNAL_MODULE].subType, RIGHT | (menuHorizontalPosition == 1 ? attr : 0), 2);
915 else {
916 if (pdef->subTypeString != nullptr)
917 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, pdef->subTypeString, g_model.moduleData[EXTERNAL_MODULE].subType, attr);
919 if (attr && (editMode > 0 || p1valdiff)) {
920 switch (menuHorizontalPosition) {
921 case 0:
922 if (multi_rfProto == MM_RF_CUSTOM_SELECTED)
923 g_model.moduleData[EXTERNAL_MODULE].setMultiProtocol(checkIncDec(event, g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(false), 0, 63, EE_MODEL));
924 else if (pdef->maxSubtype > 0)
925 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[EXTERNAL_MODULE].subType, 0, pdef->maxSubtype);
926 break;
927 case 1:
928 // Custom protocol, third column is subtype
929 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[EXTERNAL_MODULE].subType, 0, 7);
930 break;
934 break;
935 #endif
937 #if defined(PCBTARANIS)
938 case ITEM_MODEL_TRAINER_LABEL:
939 lcdDrawTextAlignedLeft(y, STR_TRAINER);
940 break;
942 case ITEM_MODEL_TRAINER_MODE:
943 lcdDrawTextAlignedLeft(y, STR_MODE);
944 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_VTRAINERMODES, g_model.trainerMode, attr);
945 if (attr) {
946 g_model.trainerMode = checkIncDec(event, g_model.trainerMode, 0, TRAINER_MODE_MAX(), EE_MODEL, isTrainerModeAvailable);
947 #if defined(BLUETOOTH)
948 if (checkIncDec_Ret) {
949 bluetoothState = BLUETOOTH_STATE_OFF;
950 bluetoothDistantAddr[0] = 0;
952 #endif
954 break;
955 #endif
957 #if defined(PCBTARANIS) && defined(BLUETOOTH)
958 case ITEM_MODEL_TRAINER_BLUETOOTH:
959 if (g_model.trainerMode == TRAINER_MODE_MASTER_BLUETOOTH) {
960 if (attr) {
961 s_editMode = 0;
963 if (bluetoothDistantAddr[0]) {
964 lcdDrawText(INDENT_WIDTH, y+1, bluetoothDistantAddr, TINSIZE);
965 if (bluetoothState != BLUETOOTH_STATE_CONNECTED) {
966 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, BUTTON("Bind"), menuHorizontalPosition == 0 ? attr : 0);
967 lcdDrawText(MODEL_SETUP_2ND_COLUMN+5*FW, y, BUTTON("Clear"), menuHorizontalPosition == 1 ? attr : 0);
969 else {
970 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, BUTTON("Clear"), attr);
972 if (attr && event == EVT_KEY_FIRST(KEY_ENTER)) {
973 if (bluetoothState == BLUETOOTH_STATE_CONNECTED || menuHorizontalPosition == 1) {
974 bluetoothState = BLUETOOTH_STATE_OFF;
975 bluetoothDistantAddr[0] = 0;
977 else {
978 bluetoothState = BLUETOOTH_STATE_BIND_REQUESTED;
982 else {
983 lcdDrawText(INDENT_WIDTH, y, "---");
984 if (bluetoothState < BLUETOOTH_STATE_IDLE)
985 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, BUTTON("Init"), attr);
986 else
987 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, BUTTON("Discover"), attr);
988 if (attr && event == EVT_KEY_FIRST(KEY_ENTER)) {
989 if (bluetoothState < BLUETOOTH_STATE_IDLE)
990 bluetoothState = BLUETOOTH_STATE_OFF;
991 else
992 bluetoothState = BLUETOOTH_STATE_DISCOVER_REQUESTED;
996 else {
997 if (bluetoothDistantAddr[0])
998 lcdDrawText(INDENT_WIDTH, y+1, bluetoothDistantAddr, TINSIZE);
999 else
1000 lcdDrawText(INDENT_WIDTH, y, "---");
1001 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, bluetoothState == BLUETOOTH_STATE_CONNECTED ? "Connected" : "!Connected");
1003 break;
1004 #endif
1006 #if defined(PCBTARANIS)
1007 case ITEM_MODEL_TRAINER_CHANNELS:
1008 case ITEM_MODEL_INTERNAL_MODULE_CHANNELS:
1009 #endif
1010 #if defined(PCBSKY9X)
1011 case ITEM_MODEL_EXTRA_MODULE_CHANNELS:
1012 #endif
1013 #if defined(CPUARM)
1014 case ITEM_MODEL_EXTERNAL_MODULE_CHANNELS:
1016 uint8_t moduleIdx = CURRENT_MODULE_EDITED(k);
1017 ModuleData & moduleData = g_model.moduleData[moduleIdx];
1018 lcdDrawTextAlignedLeft(y, STR_CHANNELRANGE);
1019 if ((int8_t)PORT_CHANNELS_ROWS(moduleIdx) >= 0) {
1020 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_CH, menuHorizontalPosition==0 ? attr : 0);
1021 lcdDrawNumber(lcdLastRightPos, y, moduleData.channelsStart+1, LEFT | (menuHorizontalPosition==0 ? attr : 0));
1022 lcdDrawChar(lcdLastRightPos, y, '-');
1023 lcdDrawNumber(lcdLastRightPos + FW+1, y, moduleData.channelsStart+NUM_CHANNELS(moduleIdx), LEFT | (menuHorizontalPosition==1 ? attr : 0));
1024 if (attr && (editMode>0 || p1valdiff)) {
1025 switch (menuHorizontalPosition) {
1026 case 0:
1027 CHECK_INCDEC_MODELVAR_ZERO(event, moduleData.channelsStart, 32-8-moduleData.channelsCount);
1028 break;
1029 case 1:
1030 CHECK_INCDEC_MODELVAR(event, moduleData.channelsCount, -4, min<int8_t>(MAX_CHANNELS(moduleIdx), 32-8-moduleData.channelsStart));
1031 if ((k == ITEM_MODEL_EXTERNAL_MODULE_CHANNELS && g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_PPM)) {
1032 SET_DEFAULT_PPM_FRAME_LENGTH(moduleIdx);
1034 break;
1038 break;
1040 #endif
1042 #if defined(PCBX7)
1043 case ITEM_MODEL_TRAINER_PARAMS:
1044 #endif
1045 #if defined(PCBTARANIS)
1046 case ITEM_MODEL_INTERNAL_MODULE_BIND:
1047 #endif
1048 #if defined(PCBSKY9X)
1049 case ITEM_MODEL_EXTRA_MODULE_BIND:
1050 #endif
1051 #if defined(CPUARM)
1052 case ITEM_MODEL_EXTERNAL_MODULE_BIND:
1054 uint8_t moduleIdx = CURRENT_MODULE_EDITED(k);
1055 ModuleData & moduleData = g_model.moduleData[moduleIdx];
1056 if (IS_MODULE_PPM(moduleIdx)) {
1057 lcdDrawTextAlignedLeft(y, STR_PPMFRAME);
1058 lcdDrawText(MODEL_SETUP_2ND_COLUMN+3*FW, y, STR_MS);
1059 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, (int16_t)moduleData.ppm.frameLength*5 + 225, (menuHorizontalPosition<=0 ? attr : 0) | PREC1|LEFT);
1060 lcdDrawChar(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, 'u');
1061 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, (moduleData.ppm.delay*50)+300, RIGHT | ((CURSOR_ON_LINE() || menuHorizontalPosition==1) ? attr : 0));
1062 lcdDrawChar(MODEL_SETUP_2ND_COLUMN+10*FW, y, moduleData.ppm.pulsePol ? '+' : '-', (CURSOR_ON_LINE() || menuHorizontalPosition==2) ? attr : 0);
1063 if (attr && (editMode>0 || p1valdiff)) {
1064 switch (menuHorizontalPosition) {
1065 case 0:
1066 CHECK_INCDEC_MODELVAR(event, moduleData.ppm.frameLength, -20, 35);
1067 break;
1068 case 1:
1069 CHECK_INCDEC_MODELVAR(event, moduleData.ppm.delay, -4, 10);
1070 break;
1071 case 2:
1072 CHECK_INCDEC_MODELVAR_ZERO(event, moduleData.ppm.pulsePol, 1);
1073 break;
1077 else if (IS_MODULE_SBUS(moduleIdx)) {
1078 lcdDrawTextAlignedLeft(y, STR_REFRESHRATE);
1079 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, (int16_t)moduleData.ppm.frameLength*5 + 225, (menuHorizontalPosition<=0 ? attr : 0) | PREC1|LEFT);
1080 lcdDrawText(lcdLastRightPos, y, STR_MS);
1081 lcdDrawText(MODEL_SETUP_2ND_COLUMN+5*FW+2, y, moduleData.sbus.noninverted ? "no inv" : "normal", (CURSOR_ON_LINE() || menuHorizontalPosition==1) ? attr : 0);
1083 if (attr && s_editMode>0) {
1084 switch (menuHorizontalPosition) {
1085 case 0:
1086 CHECK_INCDEC_MODELVAR(event, moduleData.ppm.frameLength, -33, 35);
1087 break;
1088 case 1:
1089 CHECK_INCDEC_MODELVAR_ZERO(event, moduleData.sbus.noninverted, 1);
1090 break;
1094 else {
1095 horzpos_t l_posHorz = menuHorizontalPosition;
1096 coord_t xOffsetBind = MODEL_SETUP_BIND_OFS;
1097 if (IS_MODULE_XJT(moduleIdx) && IS_D8_RX(moduleIdx)) {
1098 xOffsetBind = 0;
1099 lcdDrawTextAlignedLeft(y, STR_RECEIVER);
1100 if (attr) l_posHorz += 1;
1102 else {
1103 lcdDrawTextAlignedLeft(y, STR_RECEIVER_NUM);
1105 if (IS_MODULE_PXX(moduleIdx) || IS_MODULE_DSM2(moduleIdx) || IS_MODULE_MULTIMODULE(moduleIdx)) {
1106 if (xOffsetBind)
1107 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, g_model.header.modelId[moduleIdx], (l_posHorz==0 ? attr : 0) | LEADING0|LEFT, 2);
1108 if (attr && l_posHorz == 0) {
1109 if (editMode>0 || p1valdiff) {
1110 CHECK_INCDEC_MODELVAR_ZERO(event, g_model.header.modelId[moduleIdx], MAX_RX_NUM(moduleIdx));
1111 if (checkIncDec_Ret) {
1112 modelHeaders[g_eeGeneral.currModel].modelId[moduleIdx] = g_model.header.modelId[moduleIdx];
1114 else if (event == EVT_KEY_LONG(KEY_ENTER)) {
1115 killEvents(event);
1116 uint8_t newVal = findNextUnusedModelId(g_eeGeneral.currModel, moduleIdx);
1117 if (newVal != g_model.header.modelId[moduleIdx]) {
1118 modelHeaders[g_eeGeneral.currModel].modelId[moduleIdx] = g_model.header.modelId[moduleIdx] = newVal;
1119 storageDirty(EE_MODEL);
1124 lcdDrawText(MODEL_SETUP_2ND_COLUMN+xOffsetBind, y, STR_MODULE_BIND, l_posHorz==1 ? attr : 0);
1125 lcdDrawText(MODEL_SETUP_2ND_COLUMN+MODEL_SETUP_RANGE_OFS+xOffsetBind, y, STR_MODULE_RANGE, l_posHorz==2 ? attr : 0);
1126 uint8_t newFlag = 0;
1127 #if defined(MULTIMODULE)
1128 if (multiBindStatus == MULTI_BIND_FINISHED) {
1129 multiBindStatus = MULTI_NORMAL_OPERATION;
1130 s_editMode = 0;
1132 #endif
1133 #if defined(PCBTARANIS)
1134 if (attr && l_posHorz > 0) {
1135 if (s_editMode > 0) {
1136 if (l_posHorz == 1) {
1137 if (IS_MODULE_R9M(moduleIdx) || (IS_MODULE_XJT(moduleIdx) && g_model.moduleData[moduleIdx].rfProtocol== RF_PROTO_X16)) {
1138 #if defined(PCBXLITE)
1139 if (EVT_KEY_MASK(event) == KEY_ENTER) {
1140 #else
1141 if (event == EVT_KEY_BREAK(KEY_ENTER)) {
1142 #endif
1143 killEvents(event);
1144 uint8_t default_selection = 0; // R9M_LBT should default to 0 as available options are variables
1145 if (IS_MODULE_R9M_LBT(moduleIdx)) {
1146 if (BIND_TELEM_ALLOWED(moduleIdx))
1147 POPUP_MENU_ADD_ITEM(STR_BINDING_1_8_TELEM_ON);
1148 POPUP_MENU_ADD_ITEM(STR_BINDING_1_8_TELEM_OFF);
1149 if (BIND_TELEM_ALLOWED(moduleIdx) && BIND_CH9TO16_ALLOWED(moduleIdx))
1150 POPUP_MENU_ADD_ITEM(STR_BINDING_9_16_TELEM_ON);
1151 if (BIND_CH9TO16_ALLOWED(moduleIdx))
1152 POPUP_MENU_ADD_ITEM(STR_BINDING_9_16_TELEM_OFF);
1154 else {
1155 if (BIND_TELEM_ALLOWED(moduleIdx))
1156 POPUP_MENU_ADD_ITEM(STR_BINDING_1_8_TELEM_ON);
1157 POPUP_MENU_ADD_ITEM(STR_BINDING_1_8_TELEM_OFF);
1158 if (BIND_TELEM_ALLOWED(moduleIdx))
1159 POPUP_MENU_ADD_ITEM(STR_BINDING_9_16_TELEM_ON);
1160 POPUP_MENU_ADD_ITEM(STR_BINDING_9_16_TELEM_OFF);
1161 default_selection = g_model.moduleData[INTERNAL_MODULE].pxx.receiver_telem_off + (g_model.moduleData[INTERNAL_MODULE].pxx.receiver_channel_9_16 << 1);
1163 POPUP_MENU_SELECT_ITEM(default_selection);
1164 POPUP_MENU_START(onBindMenu);
1165 continue;
1167 if (moduleFlag[moduleIdx] == MODULE_BIND) {
1168 newFlag = MODULE_BIND;
1170 else {
1171 if (!popupMenuNoItems) {
1172 s_editMode = 0; // this is when popup is exited before a choice is made
1176 else {
1177 newFlag = MODULE_BIND;
1180 else if (l_posHorz == 2) {
1181 newFlag = MODULE_RANGECHECK;
1185 #else
1186 if (attr && l_posHorz>0 && s_editMode>0) {
1187 if (l_posHorz == 1)
1188 newFlag = MODULE_BIND;
1189 else if (l_posHorz == 2)
1190 newFlag = MODULE_RANGECHECK;
1192 #endif
1193 moduleFlag[moduleIdx] = newFlag;
1195 #if defined(MULTIMODULE)
1196 if (newFlag == MODULE_BIND) {
1197 multiBindStatus = MULTI_BIND_INITIATED;
1199 #endif
1203 break;
1205 #endif
1207 #if defined(PCBSKY9X) && defined(REVX)
1208 case ITEM_MODEL_EXTERNAL_MODULE_OUTPUT_TYPE:
1210 uint8_t moduleIdx = CURRENT_MODULE_EDITED(k);
1211 ModuleData & moduleData = g_model.moduleData[moduleIdx];
1212 moduleData.ppm.outputType = editChoice(MODEL_SETUP_2ND_COLUMN, y, STR_OUTPUT_TYPE, STR_VOUTPUT_TYPE, moduleData.ppm.outputType, 0, 1, attr, event);
1213 break;
1215 #endif
1217 #if defined(PCBTARANIS)
1218 case ITEM_MODEL_INTERNAL_MODULE_FAILSAFE:
1219 #endif
1220 #if defined(CPUARM)
1221 case ITEM_MODEL_EXTERNAL_MODULE_FAILSAFE: {
1222 uint8_t moduleIdx = CURRENT_MODULE_EDITED(k);
1223 ModuleData &moduleData = g_model.moduleData[moduleIdx];
1224 lcdDrawTextAlignedLeft(y, STR_FAILSAFE);
1225 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_VFAILSAFE, moduleData.failsafeMode, menuHorizontalPosition == 0 ? attr : 0);
1226 if (moduleData.failsafeMode == FAILSAFE_CUSTOM)
1227 lcdDrawText(MODEL_SETUP_2ND_COLUMN + MODEL_SETUP_SET_FAILSAFE_OFS, y, STR_SET, menuHorizontalPosition == 1 ? attr : 0);
1228 if (attr) {
1229 if (moduleData.failsafeMode != FAILSAFE_CUSTOM)
1230 menuHorizontalPosition = 0;
1231 if (menuHorizontalPosition == 0) {
1232 if (editMode > 0 || p1valdiff) {
1233 CHECK_INCDEC_MODELVAR_ZERO(event, moduleData.failsafeMode, FAILSAFE_LAST);
1234 if (checkIncDec_Ret) SEND_FAILSAFE_NOW(moduleIdx);
1236 } else if (menuHorizontalPosition == 1) {
1237 s_editMode = 0;
1238 if (moduleData.failsafeMode == FAILSAFE_CUSTOM) {
1239 if (event == EVT_KEY_LONG(KEY_ENTER)) {
1240 killEvents(event);
1241 setCustomFailsafe(moduleIdx);
1242 storageDirty(EE_MODEL);
1243 AUDIO_WARNING1();
1244 SEND_FAILSAFE_NOW(moduleIdx);
1246 else if (event == EVT_KEY_BREAK(KEY_ENTER)) {
1247 g_moduleIdx = moduleIdx;
1248 pushMenu(menuModelFailsafe);
1251 } else {
1252 lcdDrawSolidFilledRect(MODEL_SETUP_2ND_COLUMN, y, LCD_W - MODEL_SETUP_2ND_COLUMN, 8);
1256 break;
1258 #if defined(PCBXLITE)
1259 case ITEM_MODEL_INTERNAL_MODULE_ANTENNA:
1261 uint8_t newAntennaSel = editChoice(MODEL_SETUP_2ND_COLUMN, y, STR_ANTENNASELECTION, STR_VANTENNATYPES, g_model.moduleData[INTERNAL_MODULE].pxx.external_antenna, 0, 1, attr, event);
1262 if (newAntennaSel != g_model.moduleData[INTERNAL_MODULE].pxx.external_antenna && newAntennaSel == XJT_EXTERNAL_ANTENNA) {
1263 POPUP_CONFIRMATION(STR_ANTENNACONFIRM1);
1264 const char * w = STR_ANTENNACONFIRM2;
1265 SET_WARNING_INFO(w, strlen(w), 0);
1267 else {
1268 g_model.moduleData[INTERNAL_MODULE].pxx.external_antenna = newAntennaSel;
1270 break;
1272 #endif
1273 case ITEM_MODEL_EXTERNAL_MODULE_OPTIONS:
1275 uint8_t moduleIdx = CURRENT_MODULE_EDITED(k);
1276 #if defined(MULTIMODULE)
1277 if (IS_MODULE_MULTIMODULE(moduleIdx)) {
1278 int optionValue = g_model.moduleData[moduleIdx].multi.optionValue;
1280 const uint8_t multi_proto = g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(true);
1281 const mm_protocol_definition * pdef = getMultiProtocolDefinition(multi_proto);
1282 if (pdef->optionsstr)
1283 lcdDrawTextAlignedLeft(y, pdef->optionsstr);
1285 if (multi_proto == MM_RF_PROTO_FS_AFHDS2A)
1286 optionValue = 50 + 5 * optionValue;
1288 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, optionValue, LEFT | attr);
1289 if (attr) {
1290 if (multi_proto == MM_RF_PROTO_FS_AFHDS2A) {
1291 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].multi.optionValue, 0, 70);
1293 else if (multi_proto == MM_RF_PROTO_OLRS) {
1294 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].multi.optionValue, -1, 7);
1296 else {
1297 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].multi.optionValue, -128, 127);
1301 #endif
1302 if (IS_MODULE_R9M(moduleIdx)) {
1303 lcdDrawTextAlignedLeft(y, STR_MODULE_TELEMETRY);
1304 if (IS_TELEMETRY_INTERNAL_MODULE()) {
1305 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_DISABLE_INTERNAL);
1307 else {
1308 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_MODULE_TELEM_ON);
1311 else if (IS_MODULE_SBUS(moduleIdx)) {
1312 lcdDrawTextAlignedLeft(y, STR_WARN_BATTVOLTAGE);
1313 putsVolts(lcdLastRightPos, y, getBatteryVoltage(), attr | PREC2 | LEFT);
1315 break;
1318 case ITEM_MODEL_EXTERNAL_MODULE_POWER:
1320 uint8_t moduleIdx = CURRENT_MODULE_EDITED(k);
1321 if (IS_MODULE_R9M(moduleIdx)) {
1322 lcdDrawTextAlignedLeft(y, TR_MULTI_RFPOWER);
1323 if (IS_MODULE_R9M_FCC(moduleIdx)) {
1324 g_model.moduleData[moduleIdx].pxx.power = min((uint8_t)g_model.moduleData[moduleIdx].pxx.power, (uint8_t)R9M_FCC_POWER_MAX); // Lite FCC has only one setting
1325 #if defined(MODULE_R9M_FULLSIZE)
1326 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_R9M_FCC_POWER_VALUES, g_model.moduleData[moduleIdx].pxx.power, LEFT | attr);
1327 if (attr)
1328 CHECK_INCDEC_MODELVAR_ZERO(event, g_model.moduleData[moduleIdx].pxx.power, R9M_FCC_POWER_MAX);
1329 #else
1330 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_R9M_FCC_POWER_VALUES, g_model.moduleData[moduleIdx].pxx.power, LEFT);
1331 if (attr)
1332 REPEAT_LAST_CURSOR_MOVE();
1333 #endif
1335 else {
1336 g_model.moduleData[moduleIdx].pxx.power = min((uint8_t)g_model.moduleData[moduleIdx].pxx.power, (uint8_t)R9M_LBT_POWER_MAX);
1337 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_R9M_LBT_POWER_VALUES, selectedPxxPower, LEFT | attr);
1338 if (attr) {
1339 CHECK_INCDEC_MODELVAR_ZERO(event, selectedPxxPower, R9M_LBT_POWER_MAX);
1341 if (attr && editMode == 0 && selectedPxxPower != g_model.moduleData[moduleIdx].pxx.power) {
1342 if((selectedPxxPower + g_model.moduleData[moduleIdx].pxx.power) < 5) //switching between mode 2 and 3 does not require rebind
1343 POPUP_WARNING(STR_REBIND);
1344 g_model.moduleData[moduleIdx].pxx.power = selectedPxxPower;
1348 #if defined(MULTIMODULE)
1349 else if (IS_MODULE_MULTIMODULE(moduleIdx)) {
1350 g_model.moduleData[EXTERNAL_MODULE].multi.lowPowerMode = editCheckBox(g_model.moduleData[EXTERNAL_MODULE].multi.lowPowerMode, MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_LOWPOWER, attr, event);
1352 #endif
1355 break;
1357 #if defined(MULTIMODULE)
1358 case ITEM_MODEL_EXTERNAL_MODULE_AUTOBIND:
1359 if (g_model.moduleData[EXTERNAL_MODULE].getMultiProtocol(true) == MM_RF_PROTO_DSM2)
1360 g_model.moduleData[EXTERNAL_MODULE].multi.autoBindMode = editCheckBox(g_model.moduleData[EXTERNAL_MODULE].multi.autoBindMode, MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_DSM_AUTODTECT, attr, event);
1361 else
1362 g_model.moduleData[EXTERNAL_MODULE].multi.autoBindMode = editCheckBox(g_model.moduleData[EXTERNAL_MODULE].multi.autoBindMode, MODEL_SETUP_2ND_COLUMN, y, STR_MULTI_AUTOBIND, attr, event);
1363 break;
1365 case ITEM_MODEL_EXTERNAL_MODULE_STATUS: {
1366 lcdDrawTextAlignedLeft(y, STR_MODULE_STATUS);
1368 char statusText[64];
1369 multiModuleStatus.getStatusString(statusText);
1370 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, statusText);
1371 break;
1374 case ITEM_MODEL_EXTERNAL_MODULE_SYNCSTATUS: {
1375 lcdDrawTextAlignedLeft(y, STR_MODULE_SYNC);
1377 char statusText[64];
1378 multiSyncStatus.getRefreshString(statusText);
1379 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, statusText);
1380 break;
1382 #endif
1383 #endif
1385 #if !defined(CPUARM)
1386 case ITEM_MODEL_PPM1_PROTOCOL:
1387 lcdDrawTextAlignedLeft(y, NO_INDENT(STR_PROTO));
1388 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_VPROTOS, protocol, menuHorizontalPosition<=0 ? attr : 0);
1389 if (IS_PPM_PROTOCOL(protocol)) {
1390 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN+7*FW, y, STR_NCHANNELS, g_model.ppmNCH+2, menuHorizontalPosition!=0 ? attr : 0);
1392 else if (menuHorizontalPosition>0 && attr) {
1393 MOVE_CURSOR_FROM_HERE();
1395 if (attr && (editMode>0 || p1valdiff || (!IS_PPM_PROTOCOL(protocol) && !IS_DSM2_PROTOCOL(protocol)))) {
1396 switch (menuHorizontalPosition) {
1397 case 0:
1398 CHECK_INCDEC_MODELVAR_ZERO(event, g_model.protocol, PROTO_MAX-1);
1399 break;
1400 case 1:
1401 CHECK_INCDEC_MODELVAR(event, g_model.ppmNCH, -2, 4);
1402 g_model.ppmFrameLength = g_model.ppmNCH * 8;
1403 break;
1406 break;
1407 #endif
1409 #if 0
1410 case ITEM_MODEL_PPM2_PROTOCOL:
1411 lcdDrawTextAlignedLeft(y, PSTR("Port2"));
1412 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_VPROTOS, 0, 0);
1413 lcdDrawText(MODEL_SETUP_2ND_COLUMN+4*FW+3, y, STR_CH, menuHorizontalPosition<=0 ? attr : 0);
1414 lcdDrawNumber(lcdLastRightPos, y, g_model.moduleData[1].channelsStart+1, LEFT | (menuHorizontalPosition<=0 ? attr : 0));
1415 lcdDrawChar(lcdLastRightPos, y, '-');
1416 lcdDrawNumber(lcdLastRightPos + FW+1, y, g_model.moduleData[1].channelsStart+8+g_model.moduleData[1].channelsCount, LEFT | (menuHorizontalPosition!=0 ? attr : 0));
1417 if (attr && (editMode>0 || p1valdiff)) {
1418 switch (menuHorizontalPosition) {
1419 case 0:
1420 CHECK_INCDEC_MODELVAR_ZERO(event, g_model.moduleData[1].channelsStart, 32-8-g_model.moduleData[1].channelsCount);
1421 SET_DEFAULT_PPM_FRAME_LENGTH(1);
1422 break;
1423 case 1:
1424 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[1].channelsCount, -4, min<int8_t>(8, 32-8-g_model.moduleData[1].channelsStart));
1425 SET_DEFAULT_PPM_FRAME_LENGTH(1);
1426 break;
1429 break;
1431 case ITEM_MODEL_PPM2_PARAMS:
1432 lcdDrawTextAlignedLeft(y, STR_PPMFRAME);
1433 lcdDrawText(MODEL_SETUP_2ND_COLUMN+3*FW, y, STR_MS);
1434 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, (int16_t)g_model.moduleData[1].ppmFrameLength*5 + 225, (menuHorizontalPosition<=0 ? attr : 0) | PREC1 | LEFT);
1435 lcdDrawChar(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, 'u');
1436 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, (g_model.moduleData[1].ppmDelay*50)+300, RIGHT | ((menuHorizontalPosition < 0 || menuHorizontalPosition==1) ? attr : 0));
1437 lcdDrawChar(MODEL_SETUP_2ND_COLUMN+10*FW, y, g_model.moduleData[1].ppmPulsePol ? '+' : '-', (menuHorizontalPosition < 0 || menuHorizontalPosition==2) ? attr : 0);
1438 if (attr && (editMode>0 || p1valdiff)) {
1439 switch (menuHorizontalPosition) {
1440 case 0:
1441 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[1].ppmFrameLength, -20, 35);
1442 break;
1443 case 1:
1444 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[1].ppmDelay, -4, 10);
1445 break;
1446 case 2:
1447 CHECK_INCDEC_MODELVAR_ZERO(event, g_model.moduleData[1].ppmPulsePol, 1);
1448 break;
1451 break;
1452 #endif
1454 #if !defined(CPUARM)
1455 case ITEM_MODEL_PPM1_PARAMS:
1456 if (IS_PPM_PROTOCOL(protocol)) {
1457 lcdDrawTextAlignedLeft(y, STR_PPMFRAME);
1458 lcdDrawText(MODEL_SETUP_2ND_COLUMN+3*FW, y, STR_MS);
1459 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, (int16_t)g_model.ppmFrameLength*5 + 225, (menuHorizontalPosition<=0 ? attr : 0) | PREC1|LEFT);
1460 lcdDrawChar(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, 'u');
1461 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN+8*FW+2, y, (g_model.ppmDelay*50)+300, (CURSOR_ON_LINE() || menuHorizontalPosition==1) ? attr : 0);
1462 lcdDrawChar(MODEL_SETUP_2ND_COLUMN+10*FW, y, g_model.pulsePol ? '+' : '-', (CURSOR_ON_LINE() || menuHorizontalPosition==2) ? attr : 0);
1463 if (attr && (editMode>0 || p1valdiff)) {
1464 switch (menuHorizontalPosition) {
1465 case 0:
1466 CHECK_INCDEC_MODELVAR(event, g_model.ppmFrameLength, -20, 35);
1467 break;
1468 case 1:
1469 CHECK_INCDEC_MODELVAR(event, g_model.ppmDelay, -4, 10);
1470 break;
1471 case 2:
1472 CHECK_INCDEC_MODELVAR_ZERO(event, g_model.pulsePol, 1);
1473 break;
1477 #if defined(DSM2) || defined(PXX)
1478 else if (IS_DSM2_PROTOCOL(protocol) || IS_PXX_PROTOCOL(protocol)) {
1479 if (attr && menuHorizontalPosition > 1) {
1480 REPEAT_LAST_CURSOR_MOVE(); // limit 3 column row to 2 colums (Rx_Num and RANGE fields)
1482 lcdDrawTextAlignedLeft(y, STR_RECEIVER_NUM);
1483 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, g_model.header.modelId[0], (menuHorizontalPosition<=0 ? attr : 0) | LEADING0|LEFT, 2);
1484 if (attr && (menuHorizontalPosition==0 && (editMode>0 || p1valdiff))) {
1485 CHECK_INCDEC_MODELVAR_ZERO(event, g_model.header.modelId[0], 99);
1487 #if defined(PXX)
1488 if (protocol == PROTO_PXX) {
1489 lcdDrawText(MODEL_SETUP_2ND_COLUMN+4*FW, y, STR_SYNCMENU, menuHorizontalPosition!=0 ? attr : 0);
1490 uint8_t newFlag = 0;
1491 if (attr && menuHorizontalPosition>0 && editMode>0) {
1492 // send reset code
1493 newFlag = MODULE_BIND;
1495 moduleFlag[0] = newFlag;
1497 #endif
1498 #if defined(DSM2)
1499 if (IS_DSM2_PROTOCOL(protocol)) {
1500 lcdDrawText(MODEL_SETUP_2ND_COLUMN+4*FW, y, STR_MODULE_RANGE, menuHorizontalPosition!=0 ? attr : 0);
1501 moduleFlag[0] = (attr && menuHorizontalPosition>0 && editMode>0) ? MODULE_RANGECHECK : 0; // [MENU] key toggles range check mode
1503 #endif
1505 #endif
1506 break;
1507 #endif
1511 #if defined(CPUARM) && defined(PXX)
1512 if (IS_RANGECHECK_ENABLE()) {
1513 showMessageBox("RSSI: ");
1514 lcdDrawNumber(16+4*FW, 5*FH, TELEMETRY_RSSI(), BOLD);
1516 #endif
1518 // some field just finished being edited
1519 if (old_editMode > 0 && s_editMode == 0) {
1520 switch(menuVerticalPosition) {
1521 #if defined(PCBTARANIS)
1522 case ITEM_MODEL_INTERNAL_MODULE_BIND:
1523 if (menuHorizontalPosition == 0)
1524 checkModelIdUnique(g_eeGeneral.currModel, INTERNAL_MODULE);
1525 break;
1526 #endif
1527 #if defined(PCBSKY9X)
1528 case ITEM_MODEL_EXTRA_MODULE_BIND:
1529 if (menuHorizontalPosition == 0)
1530 checkModelIdUnique(g_eeGeneral.currModel, EXTRA_MODULE);
1531 break;
1532 #endif
1533 #if defined(CPUARM)
1534 case ITEM_MODEL_EXTERNAL_MODULE_BIND:
1535 if (menuHorizontalPosition == 0)
1536 checkModelIdUnique(g_eeGeneral.currModel, EXTERNAL_MODULE);
1537 break;
1538 #endif
1543 #if defined(CPUARM)
1544 void menuModelFailsafe(event_t event)
1546 const uint8_t channelStart = g_model.moduleData[g_moduleIdx].channelsStart;
1547 const int lim = (g_model.extendedLimits ? (512 * LIMIT_EXT_PERCENT / 100) : 512) * 2;
1548 uint8_t wbar = LCD_W - FW * 4 - FWNUM * 4;
1549 #if defined(PPM_UNIT_PERCENT_PREC1)
1550 wbar -= 6;
1551 #endif
1553 if (event == EVT_KEY_LONG(KEY_ENTER)) {
1554 killEvents(event);
1555 event = 0;
1557 if (menuVerticalPosition < NUM_CHANNELS(g_moduleIdx)) {
1558 if (s_editMode) {
1559 g_model.moduleData[g_moduleIdx].failsafeChannels[menuVerticalPosition] = channelOutputs[menuVerticalPosition+channelStart];
1560 s_editMode = 0;
1562 else {
1563 int16_t & failsafe = g_model.moduleData[g_moduleIdx].failsafeChannels[menuVerticalPosition];
1564 if (failsafe < FAILSAFE_CHANNEL_HOLD)
1565 failsafe = FAILSAFE_CHANNEL_HOLD;
1566 else if (failsafe == FAILSAFE_CHANNEL_HOLD)
1567 failsafe = FAILSAFE_CHANNEL_NOPULSE;
1568 else
1569 failsafe = 0;
1572 else {
1573 // "Outputs => Failsafe" menu item
1574 setCustomFailsafe(g_moduleIdx);
1577 storageDirty(EE_MODEL);
1578 AUDIO_WARNING1();
1579 SEND_FAILSAFE_NOW(g_moduleIdx);
1582 SIMPLE_SUBMENU_NOTITLE(NUM_CHANNELS(g_moduleIdx) + 1);
1584 lcdDrawTextAlignedCenter(0, FAILSAFESET);
1585 lcdInvertLine(0);
1587 const coord_t x = 1;
1588 coord_t y = FH + 1;
1589 uint8_t line = (menuVerticalPosition >= NUM_CHANNELS(g_moduleIdx) ? 2 : 0);
1590 uint8_t ch = (menuVerticalPosition >= 8 ? 8 : 0) + line;
1592 // Channels
1593 for (; line < 8; line++) {
1594 const int32_t channelValue = channelOutputs[ch+channelStart];
1595 int32_t failsafeValue = g_model.moduleData[g_moduleIdx].failsafeChannels[ch];
1597 //Channel
1598 putsChn(x+1, y, ch+1, SMLSIZE);
1600 // Value
1601 LcdFlags flags = TINSIZE;
1602 if (menuVerticalPosition == ch) {
1603 flags |= INVERS;
1604 if (s_editMode) {
1605 if (failsafeValue == FAILSAFE_CHANNEL_HOLD || failsafeValue == FAILSAFE_CHANNEL_NOPULSE) {
1606 s_editMode = 0;
1608 else {
1609 flags |= BLINK;
1610 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[g_moduleIdx].failsafeChannels[ch], -lim, +lim);
1615 uint8_t xValue = x+LCD_W-4-wbar;
1616 if (failsafeValue == FAILSAFE_CHANNEL_HOLD) {
1617 lcdDrawText(xValue, y, STR_HOLD, RIGHT|flags);
1618 failsafeValue = 0;
1620 else if (failsafeValue == FAILSAFE_CHANNEL_NOPULSE) {
1621 lcdDrawText(xValue, y, STR_NONE, RIGHT|flags);
1622 failsafeValue = 0;
1624 else {
1625 #if defined(PPM_UNIT_US)
1626 lcdDrawNumber(xValue, y, PPM_CH_CENTER(ch)+failsafeValue/2, RIGHT|flags);
1627 #elif defined(PPM_UNIT_PERCENT_PREC1)
1628 lcdDrawNumber(xValue, y, calcRESXto1000(failsafeValue), RIGHT|PREC1|flags);
1629 #else
1630 lcdDrawNumber(xValue, y, calcRESXto1000(failsafeValue)/10, RIGHT|flags);
1631 #endif
1634 // Gauge
1635 #if !defined(PCBX7) // X7 LCD doesn't like too many horizontal lines
1636 lcdDrawRect(x+LCD_W-3-wbar, y, wbar+1, 6);
1637 #endif
1638 const uint8_t lenChannel = limit<uint8_t>(1, (abs(channelValue) * wbar/2 + lim/2) / lim, wbar/2);
1639 const uint8_t lenFailsafe = limit<uint8_t>(1, (abs(failsafeValue) * wbar/2 + lim/2) / lim, wbar/2);
1640 const coord_t xChannel = (channelValue>0) ? x+LCD_W-3-wbar/2 : x+LCD_W-2-wbar/2-lenChannel;
1641 const coord_t xFailsafe = (failsafeValue>0) ? x+LCD_W-3-wbar/2 : x+LCD_W-2-wbar/2-lenFailsafe;
1642 lcdDrawHorizontalLine(xChannel, y+1, lenChannel, DOTTED, 0);
1643 lcdDrawHorizontalLine(xChannel, y+2, lenChannel, DOTTED, 0);
1644 lcdDrawSolidHorizontalLine(xFailsafe, y+3, lenFailsafe);
1645 lcdDrawSolidHorizontalLine(xFailsafe, y+4, lenFailsafe);
1647 y += FH - 1;
1649 if (++ch >= NUM_CHANNELS(g_moduleIdx))
1650 break;
1653 if (menuVerticalPosition >= NUM_CHANNELS(g_moduleIdx)) {
1654 // Outputs => Failsafe
1655 lcdDrawText(CENTER_OFS, LCD_H - (FH + 1), STR_OUTPUTS2FAILSAFE, INVERS);
1658 #endif