Cosmetics
[opentx.git] / radio / src / gui / 480x272 / model_setup.cpp
blob93fed1953f3115ee8e47fd261da66692379b1d14
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"
22 #include "storage/modelslist.h"
24 uint8_t g_moduleIdx;
26 enum MenuModelSetupItems {
27 ITEM_MODEL_SETUP_NAME,
28 ITEM_MODEL_SETUP_BITMAP,
29 ITEM_MODEL_SETUP_TIMER1,
30 ITEM_MODEL_SETUP_TIMER1_NAME,
31 ITEM_MODEL_SETUP_TIMER1_PERSISTENT,
32 ITEM_MODEL_SETUP_TIMER1_MINUTE_BEEP,
33 ITEM_MODEL_SETUP_TIMER1_COUNTDOWN_BEEP,
34 #if TIMERS > 1
35 ITEM_MODEL_SETUP_TIMER2,
36 ITEM_MODEL_SETUP_TIMER2_NAME,
37 ITEM_MODEL_SETUP_TIMER2_PERSISTENT,
38 ITEM_MODEL_SETUP_TIMER2_MINUTE_BEEP,
39 ITEM_MODEL_SETUP_TIMER2_COUNTDOWN_BEEP,
40 #endif
41 #if TIMERS > 2
42 ITEM_MODEL_SETUP_TIMER3,
43 ITEM_MODEL_SETUP_TIMER3_NAME,
44 ITEM_MODEL_SETUP_TIMER3_PERSISTENT,
45 ITEM_MODEL_SETUP_TIMER3_MINUTE_BEEP,
46 ITEM_MODEL_SETUP_TIMER3_COUNTDOWN_BEEP,
47 #endif
48 ITEM_MODEL_SETUP_EXTENDED_LIMITS,
49 ITEM_MODEL_SETUP_EXTENDED_TRIMS,
50 ITEM_MODEL_SETUP_DISPLAY_TRIMS,
51 ITEM_MODEL_SETUP_TRIM_INC,
52 ITEM_MODEL_SETUP_THROTTLE_LABEL,
53 ITEM_MODEL_SETUP_THROTTLE_REVERSED,
54 ITEM_MODEL_SETUP_THROTTLE_TRACE,
55 ITEM_MODEL_SETUP_THROTTLE_TRIM,
56 ITEM_MODEL_SETUP_PREFLIGHT_LABEL,
57 ITEM_MODEL_SETUP_CHECKLIST_DISPLAY,
58 ITEM_MODEL_SETUP_THROTTLE_WARNING,
59 ITEM_MODEL_SETUP_SWITCHES_WARNING,
60 ITEM_MODEL_SETUP_POTS_SLIDERS_WARNING_STATE,
61 ITEM_MODEL_SETUP_POTS_WARNING,
62 ITEM_MODEL_SETUP_SLIDERS_WARNING,
63 ITEM_MODEL_SETUP_BEEP_CENTER,
64 ITEM_MODEL_SETUP_USE_GLOBAL_FUNCTIONS,
66 #if defined(PXX2)
67 ITEM_MODEL_SETUP_REGISTRATION_ID,
68 #endif
69 #if defined(HARDWARE_INTERNAL_MODULE)
70 ITEM_MODEL_SETUP_INTERNAL_MODULE_LABEL,
71 ITEM_MODEL_SETUP_INTERNAL_MODULE_TYPE,
72 #if defined(MULTIMODULE)
73 ITEM_MODEL_SETUP_INTERNAL_MODULE_STATUS,
74 ITEM_MODEL_SETUP_INTERNAL_MODULE_SYNCSTATUS,
75 #endif
76 ITEM_MODEL_SETUP_INTERNAL_MODULE_CHANNELS,
77 ITEM_MODEL_SETUP_INTERNAL_MODULE_NOT_ACCESS_RXNUM_BIND_RANGE,
78 ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_MODEL_NUM,
79 ITEM_MODEL_SETUP_INTERNAL_MODULE_OPTIONS,
80 #if defined(MULTIMODULE)
81 ITEM_MODEL_SETUP_INTERNAL_MODULE_AUTOBIND,
82 ITEM_MODEL_SETUP_INTERNAL_MODULE_DISABLE_TELEM,
83 ITEM_MODEL_SETUP_INTERNAL_MODULE_DISABLE_MAPPING,
84 #endif
85 #if defined(INTERNAL_MODULE_PXX1) && defined(EXTERNAL_ANTENNA)
86 ITEM_MODEL_SETUP_INTERNAL_MODULE_ANTENNA,
87 #endif
88 ITEM_MODEL_SETUP_INTERNAL_MODULE_POWER,
89 ITEM_MODEL_SETUP_INTERNAL_MODULE_FAILSAFE,
90 ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_REGISTER_RANGE,
91 ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_OPTIONS,
92 ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_1,
93 ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_2,
94 ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_3,
95 #endif
96 ITEM_MODEL_SETUP_EXTERNAL_MODULE_LABEL,
97 ITEM_MODEL_SETUP_EXTERNAL_MODULE_TYPE,
98 #if defined(MULTIMODULE)
99 ITEM_MODEL_SETUP_EXTERNAL_MODULE_STATUS,
100 ITEM_MODEL_SETUP_EXTERNAL_MODULE_SYNCSTATUS,
101 #endif
102 ITEM_MODEL_SETUP_EXTERNAL_MODULE_CHANNELS,
103 ITEM_MODEL_SETUP_EXTERNAL_MODULE_NOT_ACCESS_BIND,
104 ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_MODEL_NUM,
105 ITEM_MODEL_SETUP_EXTERNAL_MODULE_OPTIONS,
106 #if defined(MULTIMODULE)
107 ITEM_MODEL_SETUP_EXTERNAL_MODULE_AUTOBIND,
108 ITEM_MODEL_SETUP_EXTERNAL_MODULE_DISABLE_TELEM,
109 ITEM_MODEL_SETUP_EXTERNAL_MODULE_DISABLE_MAPPING,
110 #endif
111 ITEM_MODEL_SETUP_EXTERNAL_MODULE_POWER,
112 ITEM_MODEL_SETUP_EXTERNAL_MODULE_FAILSAFE,
113 ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_REGISTER_RANGE,
114 ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_OPTIONS,
115 ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_1,
116 ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_2,
117 ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_3,
119 ITEM_MODEL_SETUP_TRAINER_LABEL,
120 ITEM_MODEL_SETUP_TRAINER_MODE,
121 #if defined(BLUETOOTH)
122 ITEM_MODEL_SETUP_TRAINER_BLUETOOTH,
123 #endif
124 ITEM_MODEL_SETUP_TRAINER_CHANNELS,
125 ITEM_MODEL_SETUP_TRAINER_PPM_PARAMS,
126 ITEM_MODEL_SETUP_MAX
129 #define MODEL_SETUP_2ND_COLUMN 200
130 #define MODEL_SETUP_3RD_COLUMN 270
131 #define MODEL_SETUP_4TH_COLUMN 350
132 #define MODEL_SETUP_BIND_OFS 40
133 #define MODEL_SETUP_RANGE_OFS 80
134 #define MODEL_SETUP_SET_FAILSAFE_OFS 100
135 #define MODEL_SETUP_SLIDPOT_SPACING 45
137 #define CURRENT_MODULE_EDITED(k) (k >= ITEM_MODEL_SETUP_EXTERNAL_MODULE_LABEL ? EXTERNAL_MODULE : INTERNAL_MODULE)
138 #if defined(HARDWARE_INTERNAL_MODULE)
139 #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))
140 #else
141 #define CURRENT_RECEIVER_EDITED(k) (EXTERNAL_MODULE)
142 #endif
144 #if defined(BLUETOOTH)
145 void onBluetoothConnectMenu(const char * result)
147 if (result != STR_EXIT) {
148 uint8_t index = (result - reusableBuffer.moduleSetup.bt.devices[0]) / sizeof(reusableBuffer.moduleSetup.bt.devices[0]);
149 strncpy(bluetooth.distantAddr, reusableBuffer.moduleSetup.bt.devices[index], LEN_BLUETOOTH_ADDR);
150 bluetooth.state = BLUETOOTH_STATE_BIND_REQUESTED;
152 else {
153 reusableBuffer.moduleSetup.bt.devicesCount = 0;
154 bluetooth.state = BLUETOOTH_STATE_DISCOVER_END;
157 #endif
159 #include "common/stdlcd/model_setup_pxx1.cpp"
161 void onPXX2R9MBindModeMenu(const char * result)
163 if (result == STR_16CH_WITH_TELEMETRY) {
164 reusableBuffer.moduleSetup.bindInformation.lbtMode = 1;
166 else if (result == STR_16CH_WITHOUT_TELEMETRY) {
167 reusableBuffer.moduleSetup.bindInformation.lbtMode = 2;
169 else if (result == STR_FLEX_915) {
170 reusableBuffer.moduleSetup.bindInformation.flexMode = 0;
172 else if (result == STR_FLEX_868) {
173 reusableBuffer.moduleSetup.bindInformation.flexMode = 1;
175 else {
176 // the user pressed [Exit]
177 uint8_t moduleIdx = CURRENT_MODULE_EDITED(menuVerticalPosition);
178 uint8_t receiverIdx = CURRENT_RECEIVER_EDITED(menuVerticalPosition);
179 moduleState[moduleIdx].mode = MODULE_MODE_NORMAL;
180 reusableBuffer.moduleSetup.bindInformation.step = 0;
181 removePXX2ReceiverIfEmpty(moduleIdx, receiverIdx);
182 return;
185 #if defined(SIMU)
186 uint8_t moduleIdx = CURRENT_MODULE_EDITED(menuVerticalPosition);
187 uint8_t receiverIdx = CURRENT_RECEIVER_EDITED(menuVerticalPosition);
188 memcpy(g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx], reusableBuffer.moduleSetup.bindInformation.candidateReceiversNames[reusableBuffer.moduleSetup.bindInformation.selectedReceiverIndex], PXX2_LEN_RX_NAME);
189 storageDirty(EE_MODEL);
190 moduleState[moduleIdx].mode = MODULE_MODE_NORMAL;
191 reusableBuffer.moduleSetup.bindInformation.step = BIND_OK;
192 POPUP_INFORMATION(STR_BIND_OK);
193 #else
194 reusableBuffer.moduleSetup.bindInformation.step = BIND_START;
195 #endif
198 // TODO code duplicated
201 void onResetReceiverConfirm(const char * result)
203 if (result == STR_OK) {
204 uint8_t moduleIdx = CURRENT_MODULE_EDITED(menuVerticalPosition - HEADER_LINE);
205 uint8_t receiverIdx = CURRENT_RECEIVER_EDITED(menuVerticalPosition - HEADER_LINE);
206 moduleState[moduleIdx].mode = MODULE_MODE_RESET;
207 removePXX2Receiver(moduleIdx, receiverIdx);
211 void onPXX2ReceiverMenu(const char * result)
213 uint8_t moduleIdx = CURRENT_MODULE_EDITED(menuVerticalPosition - HEADER_LINE);
214 uint8_t receiverIdx = CURRENT_RECEIVER_EDITED(menuVerticalPosition - HEADER_LINE);
216 if (result == STR_OPTIONS) {
217 memclear(&reusableBuffer.hardwareAndSettings, sizeof(reusableBuffer.hardwareAndSettings));
218 reusableBuffer.hardwareAndSettings.receiverSettings.receiverId = receiverIdx;
219 g_moduleIdx = moduleIdx;
220 pushMenu(menuModelReceiverOptions);
222 else if (result == STR_BIND) {
223 memclear(&reusableBuffer.moduleSetup.bindInformation, sizeof(BindInformation));
224 reusableBuffer.moduleSetup.bindInformation.rxUid = receiverIdx;
225 if (isModuleR9MAccess(moduleIdx)) {
226 reusableBuffer.moduleSetup.bindInformation.step = BIND_MODULE_TX_INFORMATION_REQUEST;
227 #if defined(SIMU)
228 reusableBuffer.moduleSetup.pxx2.moduleInformation.information.modelID = 1;
229 reusableBuffer.moduleSetup.pxx2.moduleInformation.information.variant = 2;
230 #else
231 moduleState[moduleIdx].readModuleInformation(&reusableBuffer.moduleSetup.pxx2.moduleInformation, PXX2_HW_INFO_TX_ID, PXX2_HW_INFO_TX_ID);
232 #endif
234 else {
235 moduleState[moduleIdx].startBind(&reusableBuffer.moduleSetup.bindInformation);
237 s_editMode = 1;
239 else if (result == STR_SHARE) {
240 reusableBuffer.moduleSetup.pxx2.shareReceiverIndex = receiverIdx;
241 moduleState[moduleIdx].mode = MODULE_MODE_SHARE;
242 s_editMode = 1;
244 else if (result == STR_DELETE || result == STR_RESET) {
245 memclear(&reusableBuffer.moduleSetup.pxx2, sizeof(reusableBuffer.moduleSetup.pxx2));
246 reusableBuffer.moduleSetup.pxx2.resetReceiverIndex = receiverIdx;
247 reusableBuffer.moduleSetup.pxx2.resetReceiverFlags = (result == STR_RESET ? 0xFF : 0x01);
248 POPUP_CONFIRMATION(result == STR_RESET ? STR_RECEIVER_RESET : STR_RECEIVER_DELETE, onResetReceiverConfirm);
250 else {
251 removePXX2ReceiverIfEmpty(moduleIdx, receiverIdx);
255 // TODO code duplicated
256 void onPXX2BindMenu(const char * result)
258 if (result != STR_EXIT) {
259 uint8_t moduleIdx = CURRENT_MODULE_EDITED(menuVerticalPosition - HEADER_LINE);
260 reusableBuffer.moduleSetup.bindInformation.selectedReceiverIndex = (result - reusableBuffer.moduleSetup.bindInformation.candidateReceiversNames[0]) / sizeof(reusableBuffer.moduleSetup.bindInformation.candidateReceiversNames[0]);
261 if (isModuleR9MAccess(moduleIdx) && reusableBuffer.moduleSetup.pxx2.moduleInformation.information.variant == PXX2_VARIANT_EU) {
262 reusableBuffer.moduleSetup.bindInformation.step = BIND_RX_NAME_SELECTED;
263 POPUP_MENU_ADD_ITEM(STR_16CH_WITH_TELEMETRY);
264 POPUP_MENU_ADD_ITEM(STR_16CH_WITHOUT_TELEMETRY);
265 POPUP_MENU_START(onPXX2R9MBindModeMenu);
267 else if (isModuleR9MAccess(moduleIdx) && reusableBuffer.moduleSetup.pxx2.moduleInformation.information.variant == PXX2_VARIANT_FLEX) {
268 reusableBuffer.moduleSetup.bindInformation.step = BIND_RX_NAME_SELECTED;
269 POPUP_MENU_ADD_ITEM(STR_FLEX_868);
270 POPUP_MENU_ADD_ITEM(STR_FLEX_915);
271 POPUP_MENU_START(onPXX2R9MBindModeMenu);
273 else {
274 #if defined(SIMU)
275 uint8_t receiverIdx = CURRENT_RECEIVER_EDITED(menuVerticalPosition - HEADER_LINE);
276 memcpy(g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx], result, PXX2_LEN_RX_NAME);
277 storageDirty(EE_MODEL);
278 moduleState[moduleIdx].mode = MODULE_MODE_NORMAL;
279 reusableBuffer.moduleSetup.bindInformation.step = BIND_OK;
280 POPUP_INFORMATION(STR_BIND_OK);
281 #else
282 reusableBuffer.moduleSetup.bindInformation.step = BIND_START;
283 #endif
286 else {
287 // the user pressed [Exit]
288 uint8_t moduleIdx = CURRENT_MODULE_EDITED(menuVerticalPosition - HEADER_LINE);
289 uint8_t receiverIdx = CURRENT_RECEIVER_EDITED(menuVerticalPosition - HEADER_LINE);
290 moduleState[moduleIdx].mode = MODULE_MODE_NORMAL;
291 removePXX2ReceiverIfEmpty(moduleIdx, receiverIdx);
292 s_editMode = 0;
296 enum PopupRegisterItems {
297 ITEM_REGISTER_PASSWORD,
298 ITEM_REGISTER_MODULE_INDEX,
299 ITEM_REGISTER_RECEIVER_NAME,
300 ITEM_REGISTER_BUTTONS
303 constexpr coord_t REGISTER_TOP = POPUP_Y + 10;
304 constexpr coord_t REGISTER_H = 6 * FH - 4;
305 constexpr coord_t REGISTER_COLUMN_1 = POPUP_X + 10;
306 constexpr coord_t REGISTER_COLUMN_2 = POPUP_X + 150;
308 void runPopupRegister(event_t event)
310 uint8_t backupVerticalPosition = menuVerticalPosition;
311 uint8_t backupHorizontalPosition = menuHorizontalPosition;
312 uint8_t backupVerticalOffset = menuVerticalOffset;
313 int8_t backupEditMode = s_editMode;
315 menuVerticalPosition = reusableBuffer.moduleSetup.pxx2.registerPopupVerticalPosition;
316 menuHorizontalPosition = reusableBuffer.moduleSetup.pxx2.registerPopupHorizontalPosition;
317 s_editMode = reusableBuffer.moduleSetup.pxx2.registerPopupEditMode;
319 switch (event) {
320 case EVT_KEY_BREAK(KEY_ENTER):
321 if (menuVerticalPosition != ITEM_REGISTER_BUTTONS) {
322 break;
324 else if (reusableBuffer.moduleSetup.pxx2.registerStep >= REGISTER_RX_NAME_RECEIVED && menuHorizontalPosition == 0) {
325 // [Enter] pressed
326 reusableBuffer.moduleSetup.pxx2.registerStep = REGISTER_RX_NAME_SELECTED;
327 backupEditMode = EDIT_MODIFY_FIELD; // so that the [Register] button blinks and the REGISTER process can continue
329 // no break
331 case EVT_KEY_LONG(KEY_EXIT):
332 s_editMode = 0;
333 // no break;
335 case EVT_KEY_FIRST(KEY_EXIT):
336 if (s_editMode <= 0) {
337 warningText = nullptr;
339 break;
342 if (warningText) {
343 const uint8_t dialogRows[] = { 0, 0, uint8_t(reusableBuffer.moduleSetup.pxx2.registerStep < REGISTER_RX_NAME_RECEIVED ? READONLY_ROW : 0), uint8_t(reusableBuffer.moduleSetup.pxx2.registerStep < REGISTER_RX_NAME_RECEIVED ? 0 : 1)};
344 check(event, 0, nullptr, 0, dialogRows, 3, 4); // TODO add a comment for 3 - HEADER_LINE once understood
346 drawPopupBackgroundAndBorder(POPUP_X, REGISTER_TOP, POPUP_W, REGISTER_H);
347 // showMessageBox(warningText);
349 // registration password
350 lcdDrawText(REGISTER_COLUMN_1, REGISTER_TOP + 8, STR_REG_ID);
351 editName(REGISTER_COLUMN_2, REGISTER_TOP + 8, g_model.modelRegistrationID, PXX2_LEN_REGISTRATION_ID, event, menuVerticalPosition == ITEM_REGISTER_PASSWORD);
353 // loop index (will be removed in future)
354 lcdDrawText(REGISTER_COLUMN_1, REGISTER_TOP + 8 + FH, "UID");
355 lcdDrawNumber(REGISTER_COLUMN_2, REGISTER_TOP + 8 + FH, reusableBuffer.moduleSetup.pxx2.registerLoopIndex, menuVerticalPosition == ITEM_REGISTER_MODULE_INDEX ? (s_editMode ? INVERS + BLINK : INVERS) : 0);
356 if (menuVerticalPosition == ITEM_REGISTER_MODULE_INDEX && s_editMode) {
357 CHECK_INCDEC_MODELVAR_ZERO(event, reusableBuffer.moduleSetup.pxx2.registerLoopIndex, 2);
360 // RX name
361 if (reusableBuffer.moduleSetup.pxx2.registerStep < REGISTER_RX_NAME_RECEIVED) {
362 lcdDrawText(REGISTER_COLUMN_1, REGISTER_TOP + 8 + 2 * FH, STR_WAITING);
363 lcdDrawText(REGISTER_COLUMN_1, REGISTER_TOP + 8 + 4 * FH, TR_EXIT, menuVerticalPosition == ITEM_REGISTER_BUTTONS ? INVERS : 0);
365 else {
366 lcdDrawText(REGISTER_COLUMN_1, REGISTER_TOP + 8 + 2 * FH, STR_RX_NAME);
367 editName(REGISTER_COLUMN_2, REGISTER_TOP + 8 + 2 * FH, reusableBuffer.moduleSetup.pxx2.registerRxName, PXX2_LEN_RX_NAME, event, menuVerticalPosition == ITEM_REGISTER_RECEIVER_NAME);
368 lcdDrawText(REGISTER_COLUMN_1, REGISTER_TOP + 8 + 4 * FH, TR_ENTER, menuVerticalPosition == ITEM_REGISTER_BUTTONS && menuHorizontalPosition == 0 ? INVERS : 0);
369 lcdDrawText(REGISTER_COLUMN_2, REGISTER_TOP + 8 + 4 * FH, TR_EXIT, menuVerticalPosition == ITEM_REGISTER_BUTTONS && menuHorizontalPosition == 1 ? INVERS : 0);
372 reusableBuffer.moduleSetup.pxx2.registerPopupVerticalPosition = menuVerticalPosition;
373 reusableBuffer.moduleSetup.pxx2.registerPopupHorizontalPosition = menuHorizontalPosition;
374 reusableBuffer.moduleSetup.pxx2.registerPopupEditMode = s_editMode;
377 menuVerticalPosition = backupVerticalPosition;
378 menuHorizontalPosition = backupHorizontalPosition;
379 menuVerticalOffset = backupVerticalOffset;
380 s_editMode = backupEditMode;
383 void startRegisterDialog(uint8_t module)
385 memclear(&reusableBuffer.moduleSetup.pxx2, sizeof(reusableBuffer.moduleSetup.pxx2));
386 moduleState[module].mode = MODULE_MODE_REGISTER;
387 s_editMode = 0;
388 POPUP_INPUT("", runPopupRegister);
391 void checkModelIdUnique(uint8_t moduleIdx)
393 if (isModuleXJTD8(moduleIdx))
394 return;
396 char * warn_buf = reusableBuffer.moduleSetup.msg;
398 // cannot rely exactly on WARNING_LINE_LEN so using WARNING_LINE_LEN-2
399 size_t warn_buf_len = sizeof(reusableBuffer.moduleSetup.msg) - WARNING_LINE_LEN - 2;
400 if (!modelslist.isModelIdUnique(moduleIdx,warn_buf,warn_buf_len)) {
401 if (warn_buf[0] != 0) {
402 POPUP_WARNING(STR_MODELIDUSED);
403 SET_WARNING_INFO(warn_buf, sizeof(reusableBuffer.moduleSetup.msg), 0);
408 void onModelSetupBitmapMenu(const char * result)
410 if (result == STR_UPDATE_LIST) {
411 if (!sdListFiles(BITMAPS_PATH, BITMAPS_EXT, sizeof(g_model.header.bitmap)-LEN_BITMAPS_EXT, NULL)) {
412 POPUP_WARNING(STR_NO_BITMAPS_ON_SD);
415 else if (result != STR_EXIT) {
416 // The user choosed a bmp file in the list
417 copySelection(g_model.header.bitmap, result, sizeof(g_model.header.bitmap));
418 storageDirty(EE_MODEL);
419 if (modelslist.getCurrentModel())
420 modelslist.getCurrentModel()->resetBuffer();
424 void editTimerMode(int timerIdx, coord_t y, LcdFlags attr, event_t event)
426 TimerData & timer = g_model.timers[timerIdx];
427 if (attr && menuHorizontalPosition < 0) {
428 lcdDrawSolidFilledRect(MODEL_SETUP_2ND_COLUMN-INVERT_HORZ_MARGIN, y-INVERT_VERT_MARGIN+1, 115+2*INVERT_HORZ_MARGIN, INVERT_LINE_HEIGHT, TEXT_INVERTED_BGCOLOR);
430 drawStringWithIndex(MENUS_MARGIN_LEFT, y, STR_TIMER, timerIdx+1);
431 drawTimerMode(MODEL_SETUP_2ND_COLUMN, y, timer.mode, (menuHorizontalPosition<=0 ? attr : 0));
432 drawTimer(MODEL_SETUP_2ND_COLUMN+50, y, timer.start, (menuHorizontalPosition!=0 ? attr|TIMEHOUR : TIMEHOUR));
433 if (attr && s_editMode>0) {
434 switch (menuHorizontalPosition) {
435 case 0:
437 int32_t timerMode = timer.mode;
438 if (timerMode < 0) timerMode -= TMRMODE_COUNT-1;
439 CHECK_INCDEC_MODELVAR_CHECK(event, timerMode, -TMRMODE_COUNT-SWSRC_LAST+1, TMRMODE_COUNT+SWSRC_LAST-1, isSwitchAvailableInTimers);
440 if (timerMode < 0) timerMode += TMRMODE_COUNT-1;
441 timer.mode = timerMode;
442 #if defined(AUTOSWITCH)
443 if (s_editMode>0) {
444 int8_t val = timer.mode - (TMRMODE_COUNT-1);
445 int8_t switchVal = checkIncDecMovedSwitch(val);
446 if (val != switchVal) {
447 timer.mode = switchVal + (TMRMODE_COUNT-1);
448 storageDirty(EE_MODEL);
451 #endif
452 break;
454 case 1:
456 const int stopsMinutes[] = { 8, 60, 120, 180, 240, 300, 600, 900, 1200 };
457 timer.start = checkIncDec(event, timer.start, 0, TIMER_MAX, EE_MODEL, NULL, (const CheckIncDecStops&)stopsMinutes);
458 break;
464 void editTimerCountdown(int timerIdx, coord_t y, LcdFlags attr, event_t event)
466 TimerData & timer = g_model.timers[timerIdx];
467 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_BEEPCOUNTDOWN);
468 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_VBEEPCOUNTDOWN, timer.countdownBeep, (menuHorizontalPosition == 0 ? attr : 0));
469 if (timer.countdownBeep != COUNTDOWN_SILENT) {
470 lcdDrawNumber(MODEL_SETUP_3RD_COLUMN, y, TIMER_COUNTDOWN_START(timerIdx), (menuHorizontalPosition == 1 ? attr : 0) | LEFT, 0, NULL, "s");
472 if (attr && s_editMode > 0) {
473 switch (menuHorizontalPosition) {
474 case 0:
475 CHECK_INCDEC_MODELVAR(event, timer.countdownBeep, COUNTDOWN_SILENT, COUNTDOWN_COUNT - 1);
476 break;
477 case 1:
478 timer.countdownStart = -checkIncDecModel(event, -timer.countdownStart, -1, +2);
479 break;
484 int getSwitchWarningsCount()
486 int count = 0;
487 for (int i=0; i<NUM_SWITCHES; ++i) {
488 if (SWITCH_WARNING_ALLOWED(i)) {
489 ++count;
492 return count;
495 #define IF_INTERNAL_MODULE_ON(x) (IS_INTERNAL_MODULE_ENABLED() ? (uint8_t)(x) : HIDDEN_ROW)
496 #define IF_EXTERNAL_MODULE_ON(x) (IS_EXTERNAL_MODULE_ENABLED() ? (uint8_t)(x) : HIDDEN_ROW)
498 #define IF_PXX2_MODULE(module, xxx) (isModulePXX2(module) ? (uint8_t)(xxx) : HIDDEN_ROW)
499 #define IF_NOT_PXX2_MODULE(module, xxx) (isModulePXX2(module) ? HIDDEN_ROW : (uint8_t)(xxx))
500 #define IF_ACCESS_MODULE_RF(module, xxx) (isModuleRFAccess(module) ? (uint8_t)(xxx) : HIDDEN_ROW)
501 #define IF_NOT_ACCESS_MODULE_RF(module, xxx) (isModuleRFAccess(module) ? HIDDEN_ROW : (uint8_t)(xxx))
503 #define TIMER_ROWS(x) NAVIGATION_LINE_BY_LINE|1, 0, 0, 0, g_model.timers[x].countdownBeep != COUNTDOWN_SILENT ? (uint8_t)1 : (uint8_t)0
505 inline uint8_t MODULE_TYPE_ROWS(int moduleIdx)
507 if (isModuleXJT(moduleIdx) || isModuleR9MNonAccess(moduleIdx) || isModuleDSM2(moduleIdx) || isModulePXX2(moduleIdx))
508 return 1;
509 #if defined(MULTIMODULE)
510 else if (isModuleMultimodule(moduleIdx)) {
511 return 1 + MULTIMODULE_RFPROTO_COLUMNS(moduleIdx);
513 #endif
514 else
515 return 0;
518 #if TIMERS == 1
519 #define TIMERS_ROWS TIMER_ROWS(0)
520 #elif TIMERS == 2
521 #define TIMERS_ROWS TIMER_ROWS(0), TIMER_ROWS(1)
522 #elif TIMERS == 3
523 #define TIMERS_ROWS TIMER_ROWS(0), TIMER_ROWS(1), TIMER_ROWS(2)
524 #endif
526 #if defined(BLUETOOTH)
527 #define TRAINER_CHANNELS_ROW (g_model.trainerData.mode == TRAINER_MODE_SLAVE ? (uint8_t)1 : (g_model.trainerData.mode == TRAINER_MODE_SLAVE_BLUETOOTH ? (uint8_t)0 : HIDDEN_ROW))
528 #define TRAINER_PPM_PARAMS_ROW (g_model.trainerData.mode == TRAINER_MODE_SLAVE ? (uint8_t)2 : HIDDEN_ROW)
529 #define IF_BT_TRAINER_ON(x) (g_eeGeneral.bluetoothMode == BLUETOOTH_TRAINER ? (uint8_t)(x) : HIDDEN_ROW)
530 #define TRAINER_BLUETOOTH_M_ROW ((bluetooth.distantAddr[0] == '\0' || bluetooth.state == BLUETOOTH_STATE_CONNECTED) ? (uint8_t)0 : (uint8_t)1)
531 #define TRAINER_BLUETOOTH_S_ROW (bluetooth.distantAddr[0] == '\0' ? HIDDEN_ROW : LABEL())
532 #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))
533 #define TRAINER_ROWS LABEL(Trainer), 0, IF_BT_TRAINER_ON(TRAINER_BLUETOOTH_ROW), TRAINER_CHANNELS_ROW, TRAINER_PPM_PARAMS_ROW
534 #else
535 #define TRAINER_CHANNELS_ROW (g_model.trainerData.mode == TRAINER_MODE_SLAVE ? (uint8_t)1 : HIDDEN_ROW)
536 #define TRAINER_PPM_PARAMS_ROW (HIDDEN_ROW)
537 #define TRAINER_ROWS LABEL(Trainer), 0, TRAINER_CHANNELS_ROW, TRAINER_PPM_PARAMS_ROW
538 #endif
540 #if defined(PXX2)
541 #define REGISTRATION_ID_ROWS uint8_t((isDefaultModelRegistrationID() || (warningText && popupFunc == runPopupRegister)) ? HIDDEN_ROW : READONLY_ROW),
542 #else
543 #define REGISTRATION_ID_ROWS
544 #endif
546 #if defined(INTERNAL_MODULE_PXX1) && defined(EXTERNAL_ANTENNA)
547 #define EXTERNAL_ANTENNA_ROW ((isModuleXJT(INTERNAL_MODULE) && g_eeGeneral.antennaMode == ANTENNA_MODE_PER_MODEL) ? (uint8_t)0 : HIDDEN_ROW),
548 void onModelAntennaSwitchConfirm(const char * result)
550 if (result == STR_OK) {
551 // Switch to external antenna confirmation
552 g_model.moduleData[INTERNAL_MODULE].pxx.antennaMode = ANTENNA_MODE_EXTERNAL;
553 globalData.externalAntennaEnabled = true;
554 storageDirty(EE_MODEL);
556 else {
557 reusableBuffer.moduleSetup.antennaMode = g_model.moduleData[INTERNAL_MODULE].pxx.antennaMode;
560 #else
561 #define EXTERNAL_ANTENNA_ROW
562 #endif
564 #if defined(HARDWARE_INTERNAL_MODULE)
565 #define INTERNAL_MODULE_ROWS \
566 LABEL(InternalModule), \
567 MODULE_TYPE_ROWS(INTERNAL_MODULE), /* ITEM_MODEL_SETUP_INTERNAL_MODULE_TYPE*/ \
568 MULTIMODULE_STATUS_ROWS(INTERNAL_MODULE) /* ITEM_MODEL_SETUP_INTERNAL_MODULE_STATUS, ITEM_MODEL_SETUP_INTERNAL_MODULE_SYNCSTATUS */ \
569 MODULE_CHANNELS_ROWS(INTERNAL_MODULE), /* ITEM_MODEL_SETUP_INTERNAL_MODULE_CHANNELS*/ \
570 IF_NOT_ACCESS_MODULE_RF(INTERNAL_MODULE, IF_INTERNAL_MODULE_ON(IF_INTERNAL_MODULE_ON(isModuleRxNumAvailable(INTERNAL_MODULE) ? (uint8_t)2 : (uint8_t)1))), /* *ITEM_MODEL_SETUP_INTERNAL_MODULE_NOT_ACCESS_RXNUM_BIND_RANGE */\
571 IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 0), /* ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_MODEL_NUM*/ \
572 MODULE_OPTION_ROW(INTERNAL_MODULE), /* ITEM_MODEL_SETUP_INTERNAL_MODULE_OPTIONS */ \
573 MULTIMODULE_MODULE_ROWS(INTERNAL_MODULE) /* ITEM_MODEL_SETUP_INTERNAL_MODULE_AUTOBIND */ \
574 /* ITEM_MODEL_SETUP_INTERNAL_MODULE_DISABLE_TELEM */ \
575 /* ITEM_MODEL_SETUP_INTERNAL_MODULE_DISABLE_MAPPING */ \
576 EXTERNAL_ANTENNA_ROW /* ITEM_MODEL_SETUP_INTERNAL_MODULE_ANTENNA */ \
577 MODULE_POWER_ROW(INTERNAL_MODULE), /* ITEM_MODEL_SETUP_INTERNAL_MODULE_POWER */ \
578 IF_INTERNAL_MODULE_ON(FAILSAFE_ROWS(INTERNAL_MODULE)), /* ITEM_MODEL_SETUP_INTERNAL_MODULE_FAILSAFE */ \
579 IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 1), /* ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_REGISTER_RANGE */ \
580 IF_PXX2_MODULE(INTERNAL_MODULE, 0), /* ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_OPTIONS*/ \
581 IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 0), /* ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_1 */ \
582 IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 0), /* ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_2 */ \
583 IF_ACCESS_MODULE_RF(INTERNAL_MODULE, 0), /* ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_3 */
584 #else
585 #define INTERNAL_MODULE_ROWS
586 #endif
589 bool menuModelSetup(event_t event)
591 bool CURSOR_ON_CELL = (menuHorizontalPosition >= 0);
593 int8_t old_editMode = s_editMode;
595 MENU(STR_MENUSETUP, MODEL_ICONS, menuTabModel, MENU_MODEL_SETUP, ITEM_MODEL_SETUP_MAX,
596 { 0, // Model name
597 0, // Model bitmap
598 TIMERS_ROWS,
599 0, // Extended limits
600 1, // Extended trims
601 0, // Show trims
602 0, // Trims step
604 LABEL(Throttle),
605 0, // Throttle reverse
606 0, // Throttle trace source
607 0, // Throttle trim
609 LABEL(PreflightCheck),
610 0, // Display checklist
611 0, // Throttle state
612 uint8_t(NAVIGATION_LINE_BY_LINE|(getSwitchWarningsCount()-1)), // Switches warnings
613 0, // Pots & Sliders warning mode
614 g_model.potsWarnMode ? uint8_t(NAVIGATION_LINE_BY_LINE|(NUM_POTS-1)) : HIDDEN_ROW, // Pots positions
615 g_model.potsWarnMode ? uint8_t(NAVIGATION_LINE_BY_LINE|(NUM_SLIDERS-1)) : HIDDEN_ROW, // Sliders positions
617 NAVIGATION_LINE_BY_LINE|(NUM_STICKS+NUM_POTS+NUM_SLIDERS-1), // Center beeps
618 0, // Global functions
620 REGISTRATION_ID_ROWS
622 INTERNAL_MODULE_ROWS
624 LABEL(ExternalModule),
625 MODULE_TYPE_ROWS(EXTERNAL_MODULE),
626 MULTIMODULE_STATUS_ROWS(EXTERNAL_MODULE)
627 MODULE_CHANNELS_ROWS(EXTERNAL_MODULE),
628 IF_NOT_ACCESS_MODULE_RF(EXTERNAL_MODULE, MODULE_BIND_ROWS(EXTERNAL_MODULE)),
629 IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // RxNum for ACCESS
630 IF_NOT_PXX2_MODULE(EXTERNAL_MODULE, MODULE_OPTION_ROW(EXTERNAL_MODULE)),
631 MULTIMODULE_MODULE_ROWS(EXTERNAL_MODULE)
632 MODULE_POWER_ROW(EXTERNAL_MODULE),
633 FAILSAFE_ROWS(EXTERNAL_MODULE),
634 IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 1), // Range check and Register buttons
635 IF_PXX2_MODULE(EXTERNAL_MODULE, 0), // Module options
636 IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // Receiver 1
637 IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // Receiver 2
638 IF_ACCESS_MODULE_RF(EXTERNAL_MODULE, 0), // Receiver 3
640 TRAINER_ROWS
643 if (event == EVT_ENTRY || event == EVT_ENTRY_UP) {
644 memclear(&reusableBuffer.moduleSetup, sizeof(reusableBuffer.moduleSetup));
645 reusableBuffer.moduleSetup.r9mPower = g_model.moduleData[EXTERNAL_MODULE].pxx.power;
646 reusableBuffer.moduleSetup.previousType = g_model.moduleData[EXTERNAL_MODULE].type;
647 reusableBuffer.moduleSetup.newType = g_model.moduleData[EXTERNAL_MODULE].type;
648 #if defined(INTERNAL_MODULE_PXX1) && defined(EXTERNAL_ANTENNA)
649 reusableBuffer.moduleSetup.antennaMode = g_model.moduleData[INTERNAL_MODULE].pxx.antennaMode;
650 #endif
653 if (menuEvent) {
654 moduleState[0].mode = 0;
655 moduleState[1].mode = 0;
658 int sub = menuVerticalPosition;
660 for (uint8_t i = 0; i < NUM_BODY_LINES; ++i) {
661 coord_t y = MENU_CONTENT_TOP + i*FH;
662 uint8_t k = i + menuVerticalOffset;
663 for (int j = 0; j <= k; j++) {
664 if (mstate_tab[j] == HIDDEN_ROW)
665 k++;
667 uint8_t moduleIdx = CURRENT_MODULE_EDITED(k);
668 LcdFlags blink = ((s_editMode>0) ? BLINK|INVERS : INVERS);
669 LcdFlags attr = (sub == k ? blink : 0);
671 switch (k) {
672 case ITEM_MODEL_SETUP_NAME:
673 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MODELNAME);
674 editName(MODEL_SETUP_2ND_COLUMN, y, g_model.header.name, sizeof(g_model.header.name), event, attr);
675 break;
677 case ITEM_MODEL_SETUP_BITMAP:
678 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_BITMAP);
679 if (ZEXIST(g_model.header.bitmap))
680 lcdDrawSizedText(MODEL_SETUP_2ND_COLUMN, y, g_model.header.bitmap, sizeof(g_model.header.bitmap), attr);
681 else
682 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_VCSWFUNC, 0, attr);
683 if (attr && event==EVT_KEY_BREAK(KEY_ENTER) && READ_ONLY_UNLOCKED()) {
684 s_editMode = 0;
685 if (sdListFiles(BITMAPS_PATH, BITMAPS_EXT, sizeof(g_model.header.bitmap)-LEN_BITMAPS_EXT, g_model.header.bitmap, LIST_NONE_SD_FILE | LIST_SD_FILE_EXT)) {
686 POPUP_MENU_START(onModelSetupBitmapMenu);
688 else {
689 POPUP_WARNING(STR_NO_BITMAPS_ON_SD);
692 break;
694 case ITEM_MODEL_SETUP_TIMER1:
695 editTimerMode(0, y, attr, event);
696 break;
698 case ITEM_MODEL_SETUP_TIMER1_NAME:
699 lcdDrawText(MENUS_MARGIN_LEFT, y, INDENT TR_NAME);
700 editName(MODEL_SETUP_2ND_COLUMN, y, g_model.timers[0].name, LEN_TIMER_NAME, event, attr);
701 break;
703 case ITEM_MODEL_SETUP_TIMER1_MINUTE_BEEP:
704 lcdDrawText(MENUS_MARGIN_LEFT, y, INDENT TR_MINUTEBEEP);
705 g_model.timers[0].minuteBeep = editCheckBox(g_model.timers[0].minuteBeep, MODEL_SETUP_2ND_COLUMN, y, attr, event);
706 break;
708 case ITEM_MODEL_SETUP_TIMER1_COUNTDOWN_BEEP:
709 editTimerCountdown(0, y, attr, event);
710 break;
712 case ITEM_MODEL_SETUP_TIMER1_PERSISTENT:
713 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_PERSISTENT);
714 g_model.timers[0].persistent = editChoice(MODEL_SETUP_2ND_COLUMN, y, STR_VPERSISTENT, g_model.timers[0].persistent, 0, 2, attr, event);
715 break;
717 #if TIMERS > 1
718 case ITEM_MODEL_SETUP_TIMER2:
719 editTimerMode(1, y, attr, event);
720 break;
722 case ITEM_MODEL_SETUP_TIMER2_NAME:
723 lcdDrawText(MENUS_MARGIN_LEFT, y, INDENT TR_NAME);
724 editName(MODEL_SETUP_2ND_COLUMN, y, g_model.timers[1].name, LEN_TIMER_NAME, event, attr);
725 break;
727 case ITEM_MODEL_SETUP_TIMER2_MINUTE_BEEP:
728 lcdDrawText(MENUS_MARGIN_LEFT, y, INDENT TR_MINUTEBEEP);
729 g_model.timers[1].minuteBeep = editCheckBox(g_model.timers[1].minuteBeep, MODEL_SETUP_2ND_COLUMN, y, attr, event);
730 break;
732 case ITEM_MODEL_SETUP_TIMER2_COUNTDOWN_BEEP:
733 editTimerCountdown(1, y, attr, event);
734 break;
736 case ITEM_MODEL_SETUP_TIMER2_PERSISTENT:
737 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_PERSISTENT);
738 g_model.timers[1].persistent = editChoice(MODEL_SETUP_2ND_COLUMN, y, STR_VPERSISTENT, g_model.timers[1].persistent, 0, 2, attr, event);
739 break;
740 #endif
742 #if TIMERS > 2
743 case ITEM_MODEL_SETUP_TIMER3:
744 editTimerMode(2, y, attr, event);
745 break;
747 case ITEM_MODEL_SETUP_TIMER3_NAME:
748 lcdDrawText(MENUS_MARGIN_LEFT, y, INDENT TR_NAME);
749 editName(MODEL_SETUP_2ND_COLUMN, y, g_model.timers[2].name, LEN_TIMER_NAME, event, attr);
750 break;
752 case ITEM_MODEL_SETUP_TIMER3_MINUTE_BEEP:
753 lcdDrawText(MENUS_MARGIN_LEFT, y, INDENT TR_MINUTEBEEP);
754 g_model.timers[2].minuteBeep = editCheckBox(g_model.timers[2].minuteBeep, MODEL_SETUP_2ND_COLUMN, y, attr, event);
755 break;
757 case ITEM_MODEL_SETUP_TIMER3_COUNTDOWN_BEEP:
758 editTimerCountdown(2, y, attr, event);
759 break;
761 case ITEM_MODEL_SETUP_TIMER3_PERSISTENT:
762 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_PERSISTENT);
763 g_model.timers[2].persistent = editChoice(MODEL_SETUP_2ND_COLUMN, y, STR_VPERSISTENT, g_model.timers[2].persistent, 0, 2, attr, event);
764 break;
765 #endif
767 case ITEM_MODEL_SETUP_EXTENDED_LIMITS:
768 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_ELIMITS);
769 g_model.extendedLimits = editCheckBox(g_model.extendedLimits, MODEL_SETUP_2ND_COLUMN, y, attr, event);
770 break;
772 case ITEM_MODEL_SETUP_EXTENDED_TRIMS:
773 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_ETRIMS);
774 g_model.extendedTrims = editCheckBox(g_model.extendedTrims, MODEL_SETUP_2ND_COLUMN, y, menuHorizontalPosition<=0 ? attr : 0, event==EVT_KEY_BREAK(KEY_ENTER) ? event : 0);
775 lcdDrawText(MODEL_SETUP_2ND_COLUMN+18, y, STR_RESET_BTN, menuHorizontalPosition>0 && !NO_HIGHLIGHT() ? attr : 0);
776 if (attr && menuHorizontalPosition>0) {
777 s_editMode = 0;
778 if (event==EVT_KEY_LONG(KEY_ENTER)) {
779 START_NO_HIGHLIGHT();
780 for (uint8_t i=0; i<MAX_FLIGHT_MODES; i++) {
781 memclear(&g_model.flightModeData[i], TRIMS_ARRAY_SIZE);
783 storageDirty(EE_MODEL);
784 AUDIO_WARNING1();
787 break;
789 case ITEM_MODEL_SETUP_DISPLAY_TRIMS:
790 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_DISPLAY_TRIMS);
791 g_model.displayTrims = editChoice(MODEL_SETUP_2ND_COLUMN, y, "\006No\0 ChangeYes", g_model.displayTrims, 0, 2, attr, event);
792 break;
794 case ITEM_MODEL_SETUP_TRIM_INC:
795 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_TRIMINC);
796 g_model.trimInc = editChoice(MODEL_SETUP_2ND_COLUMN, y, STR_VTRIMINC, g_model.trimInc, -2, 2, attr, event);
797 break;
799 case ITEM_MODEL_SETUP_THROTTLE_LABEL:
800 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_THROTTLE_LABEL);
801 break;
803 case ITEM_MODEL_SETUP_THROTTLE_REVERSED:
804 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_THROTTLEREVERSE);
805 g_model.throttleReversed = editCheckBox(g_model.throttleReversed, MODEL_SETUP_2ND_COLUMN, y, attr, event);
806 break;
808 case ITEM_MODEL_SETUP_THROTTLE_TRACE:
810 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_TTRACE);
811 // check if source is available (EXT1 & EXT2 on X10)
812 if (attr) CHECK_INCDEC_MODELVAR_ZERO_CHECK(event, g_model.thrTraceSrc, NUM_POTS+NUM_SLIDERS+MAX_OUTPUT_CHANNELS, isThrottleSourceAvailable);
813 uint8_t idx = g_model.thrTraceSrc + MIXSRC_Thr;
814 if (idx > MIXSRC_Thr)
815 idx += 1;
816 if (idx >= MIXSRC_FIRST_POT+NUM_POTS+NUM_SLIDERS)
817 idx += MIXSRC_CH1 - MIXSRC_FIRST_POT - NUM_POTS - NUM_SLIDERS;
818 drawSource(MODEL_SETUP_2ND_COLUMN, y, idx, attr);
819 break;
822 case ITEM_MODEL_SETUP_THROTTLE_TRIM:
823 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_TTRIM);
824 g_model.thrTrim = editCheckBox(g_model.thrTrim, MODEL_SETUP_2ND_COLUMN, y, attr, event);
825 break;
827 case ITEM_MODEL_SETUP_PREFLIGHT_LABEL:
828 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_PREFLIGHT);
829 break;
831 case ITEM_MODEL_SETUP_CHECKLIST_DISPLAY:
832 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_CHECKLIST);
833 g_model.displayChecklist = editCheckBox(g_model.displayChecklist, MODEL_SETUP_2ND_COLUMN, y, attr, event);
834 break;
836 case ITEM_MODEL_SETUP_THROTTLE_WARNING:
837 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_THROTTLEWARNING);
838 g_model.disableThrottleWarning = !editCheckBox(!g_model.disableThrottleWarning, MODEL_SETUP_2ND_COLUMN, y, attr, event);
839 break;
841 case ITEM_MODEL_SETUP_SWITCHES_WARNING:
843 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SWITCHWARNING);
844 if (!READ_ONLY() && attr && menuHorizontalPosition<0 && event==EVT_KEY_LONG(KEY_ENTER)) {
845 killEvents(event);
846 START_NO_HIGHLIGHT();
847 getMovedSwitch();
848 for (int i=0; i<NUM_SWITCHES; i++) {
849 bool enabled = ((g_model.switchWarningState >> (3*i)) & 0x07);
850 if (enabled) {
851 g_model.switchWarningState &= ~(0x07 << (3*i));
852 unsigned int newState = (switches_states >> (2*i) & 0x03) + 1;
853 g_model.switchWarningState |= (newState << (3*i));
856 AUDIO_WARNING1();
857 storageDirty(EE_MODEL);
860 if (attr && menuHorizontalPosition < 0) {
861 lcdDrawSolidFilledRect(MODEL_SETUP_2ND_COLUMN-INVERT_HORZ_MARGIN, y-INVERT_VERT_MARGIN+1, (NUM_SWITCHES-1)*25+INVERT_HORZ_MARGIN, INVERT_LINE_HEIGHT, TEXT_INVERTED_BGCOLOR);
864 unsigned int newStates = 0;
865 for (int i=0, current=0; i<NUM_SWITCHES; i++) {
866 if (SWITCH_WARNING_ALLOWED(i)) {
867 unsigned int state = ((g_model.switchWarningState >> (3*i)) & 0x07);
868 LcdFlags color = (state > 0 ? TEXT_COLOR : TEXT_DISABLE_COLOR);
869 if (attr && menuHorizontalPosition < 0) {
870 color |= INVERS;
872 char s[3];
873 s[0] = 'A' + i;
874 s[1] = "x\300-\301"[state];
875 s[2] = '\0';
876 lcdDrawText(MODEL_SETUP_2ND_COLUMN+i*25, y, s, color|(menuHorizontalPosition==current ? attr : 0));
877 if (!READ_ONLY() && attr && menuHorizontalPosition==current) {
878 CHECK_INCDEC_MODELVAR_ZERO_CHECK(event, state, 3, IS_CONFIG_3POS(i) ? NULL : isSwitch2POSWarningStateAvailable);
880 newStates |= (state << (3*i));
881 ++current;
884 g_model.switchWarningState = newStates;
885 break;
888 case ITEM_MODEL_SETUP_POTS_SLIDERS_WARNING_STATE:
889 lcdDrawText(MENUS_MARGIN_LEFT, y,STR_POTWARNINGSTATE);
890 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, "\004""OFF\0""Man\0""Auto", g_model.potsWarnMode, attr);
891 if (attr) {
892 CHECK_INCDEC_MODELVAR(event, g_model.potsWarnMode, POTS_WARN_OFF, POTS_WARN_AUTO);
893 storageDirty(EE_MODEL);
895 break;
897 case ITEM_MODEL_SETUP_POTS_WARNING:
899 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_POTWARNING);
900 if (attr) {
901 if (!READ_ONLY() && menuHorizontalPosition >= 0 && event==EVT_KEY_LONG(KEY_ENTER)) {
902 killEvents(event);
903 if (g_model.potsWarnMode == POTS_WARN_MANUAL) {
904 SAVE_POT_POSITION(menuHorizontalPosition);
905 AUDIO_WARNING1();
906 storageDirty(EE_MODEL);
910 if (!READ_ONLY() && menuHorizontalPosition >= 0 && s_editMode && event==EVT_KEY_BREAK(KEY_ENTER)) {
911 s_editMode = 0;
912 g_model.potsWarnEnabled ^= (1 << (menuHorizontalPosition));
913 storageDirty(EE_MODEL);
917 if (attr && menuHorizontalPosition < 0) {
918 lcdDrawSolidFilledRect(MODEL_SETUP_2ND_COLUMN-INVERT_HORZ_MARGIN, y-INVERT_VERT_MARGIN+1, NUM_POTS*MODEL_SETUP_SLIDPOT_SPACING+INVERT_HORZ_MARGIN, INVERT_LINE_HEIGHT, TEXT_INVERTED_BGCOLOR);
921 if (g_model.potsWarnMode) {
922 coord_t x = MODEL_SETUP_2ND_COLUMN;
923 for (int i=0; i<NUM_POTS; ++i) {
924 LcdFlags flags = (((menuHorizontalPosition==i) && attr) ? INVERS : 0);
925 flags |= (g_model.potsWarnEnabled & (1 << i)) ? TEXT_DISABLE_COLOR : TEXT_COLOR;
926 if (attr && menuHorizontalPosition < 0) {
927 flags |= INVERS;
929 lcdDrawTextAtIndex(x, y, STR_VSRCRAW, NUM_STICKS+1+i, flags);
930 x += MODEL_SETUP_SLIDPOT_SPACING;
933 break;
936 case ITEM_MODEL_SETUP_SLIDERS_WARNING:
937 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_SLIDERWARNING);
938 if (attr) {
939 if (!READ_ONLY() && menuHorizontalPosition+1 && event==EVT_KEY_LONG(KEY_ENTER)) {
940 killEvents(event);
941 if (g_model.potsWarnMode == POTS_WARN_MANUAL) {
942 SAVE_POT_POSITION(menuHorizontalPosition+NUM_POTS);
943 AUDIO_WARNING1();
944 storageDirty(EE_MODEL);
948 if (!READ_ONLY() && menuHorizontalPosition+1 && s_editMode && event==EVT_KEY_BREAK(KEY_ENTER)) {
949 s_editMode = 0;
950 g_model.potsWarnEnabled ^= (1 << (menuHorizontalPosition+NUM_POTS));
951 storageDirty(EE_MODEL);
955 if (attr && menuHorizontalPosition < 0) {
956 lcdDrawSolidFilledRect(MODEL_SETUP_2ND_COLUMN-INVERT_HORZ_MARGIN, y-INVERT_VERT_MARGIN+1, NUM_SLIDERS*MODEL_SETUP_SLIDPOT_SPACING+INVERT_HORZ_MARGIN, INVERT_LINE_HEIGHT, TEXT_INVERTED_BGCOLOR);
959 if (g_model.potsWarnMode) {
960 coord_t x = MODEL_SETUP_2ND_COLUMN;
961 for (int i=NUM_POTS; i<NUM_POTS+NUM_SLIDERS; ++i) {
962 LcdFlags flags = (((menuHorizontalPosition==i-NUM_POTS) && attr) ? INVERS : 0);
963 flags |= (g_model.potsWarnEnabled & (1 << i)) ? TEXT_DISABLE_COLOR : TEXT_COLOR;
964 if (attr && menuHorizontalPosition < 0) {
965 flags |= INVERS;
967 lcdDrawTextAtIndex(x, y, STR_VSRCRAW, NUM_STICKS+1+i, flags);
968 x += MODEL_SETUP_SLIDPOT_SPACING;
971 break;
973 case ITEM_MODEL_SETUP_BEEP_CENTER:
974 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_BEEPCTR);
975 lcdNextPos = MODEL_SETUP_2ND_COLUMN - 3;
976 for (int i=0; i<NUM_STICKS+NUM_POTS+NUM_SLIDERS; i++) {
977 if (i >= NUM_STICKS && (!IS_POT_SLIDER_AVAILABLE(i) || IS_POT_MULTIPOS(i))) {
978 if (attr && menuHorizontalPosition == i) REPEAT_LAST_CURSOR_MOVE(NUM_STICKS+NUM_POTS+NUM_SLIDERS, true);
979 continue;
981 LcdFlags flags = ((menuHorizontalPosition==i && attr) ? INVERS : 0);
982 flags |= (g_model.beepANACenter & ((BeepANACenter)1<<i)) ? TEXT_COLOR : (TEXT_DISABLE_COLOR | NO_FONTCACHE);
983 if (attr && menuHorizontalPosition < 0) flags |= INVERS;
984 lcdDrawTextAtIndex(lcdNextPos+3, y, STR_RETA123, i, flags);
986 if (attr && CURSOR_ON_CELL) {
987 if (event==EVT_KEY_BREAK(KEY_ENTER)) {
988 if (READ_ONLY_UNLOCKED()) {
989 s_editMode = 0;
990 g_model.beepANACenter ^= ((BeepANACenter)1<<menuHorizontalPosition);
991 storageDirty(EE_MODEL);
995 break;
997 case ITEM_MODEL_SETUP_USE_GLOBAL_FUNCTIONS:
998 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_USE_GLOBAL_FUNCS);
999 drawCheckBox(MODEL_SETUP_2ND_COLUMN, y, !g_model.noGlobalFunctions, attr);
1000 if (attr) g_model.noGlobalFunctions = !checkIncDecModel(event, !g_model.noGlobalFunctions, 0, 1);
1001 break;
1003 #if defined(HARDWARE_INTERNAL_MODULE)
1004 case ITEM_MODEL_SETUP_INTERNAL_MODULE_LABEL:
1005 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_INTERNALRF);
1006 break;
1008 #if !defined(INTERNAL_MODULE_MULTI)
1009 case ITEM_MODEL_SETUP_INTERNAL_MODULE_TYPE:
1010 lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_MODE);
1011 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_INTERNAL_MODULE_PROTOCOLS, g_model.moduleData[INTERNAL_MODULE].type, menuHorizontalPosition==0 ? attr : 0);
1012 if (isModuleXJT(INTERNAL_MODULE))
1013 lcdDrawTextAtIndex(MODEL_SETUP_3RD_COLUMN, y, STR_XJT_ACCST_RF_PROTOCOLS, 1 + g_model.moduleData[INTERNAL_MODULE].subType, menuHorizontalPosition==1 ? attr : 0);
1014 else if (isModuleISRM(INTERNAL_MODULE))
1015 lcdDrawTextAtIndex(MODEL_SETUP_3RD_COLUMN, y, STR_ISRM_RF_PROTOCOLS, g_model.moduleData[INTERNAL_MODULE].subType, menuHorizontalPosition==1 ? attr : 0);
1016 if (attr) {
1017 if (menuHorizontalPosition == 0) {
1018 uint8_t moduleType = checkIncDec(event, g_model.moduleData[INTERNAL_MODULE].type, MODULE_TYPE_NONE, MODULE_TYPE_MAX, EE_MODEL, isInternalModuleAvailable);
1019 if (checkIncDec_Ret) {
1020 setModuleType(INTERNAL_MODULE, moduleType);
1023 else if (isModuleXJT(INTERNAL_MODULE)) {
1024 g_model.moduleData[INTERNAL_MODULE].subType = checkIncDec(event, g_model.moduleData[INTERNAL_MODULE].subType, 0, MODULE_SUBTYPE_PXX1_LAST, EE_MODEL, isRfProtocolAvailable);
1025 if (checkIncDec_Ret) {
1026 g_model.moduleData[0].type = MODULE_TYPE_XJT_PXX1;
1027 g_model.moduleData[0].channelsStart = 0;
1028 g_model.moduleData[0].channelsCount = defaultModuleChannels_M8(INTERNAL_MODULE);
1031 else if (isModulePXX2(INTERNAL_MODULE)) {
1032 g_model.moduleData[INTERNAL_MODULE].subType = checkIncDec(event, g_model.moduleData[INTERNAL_MODULE].subType, 0, MODULE_SUBTYPE_ISRM_PXX2_ACCST_D16, EE_MODEL);
1035 break;
1036 #endif
1038 #if defined(INTERNAL_MODULE_PXX1) && defined(EXTERNAL_ANTENNA)
1039 case ITEM_MODEL_SETUP_INTERNAL_MODULE_ANTENNA:
1040 lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_ANTENNA);
1041 reusableBuffer.moduleSetup.antennaMode = editChoice(MODEL_SETUP_2ND_COLUMN, y, STR_ANTENNA_MODES, reusableBuffer.moduleSetup.antennaMode == ANTENNA_MODE_PER_MODEL ? ANTENNA_MODE_INTERNAL : reusableBuffer.moduleSetup.antennaMode, ANTENNA_MODE_INTERNAL, ANTENNA_MODE_EXTERNAL, attr, event, [](int value) { return value != ANTENNA_MODE_PER_MODEL; });
1042 if (event && !s_editMode && reusableBuffer.moduleSetup.antennaMode != g_model.moduleData[INTERNAL_MODULE].pxx.antennaMode) {
1043 if (reusableBuffer.moduleSetup.antennaMode == ANTENNA_MODE_EXTERNAL && !isExternalAntennaEnabled()) {
1044 POPUP_CONFIRMATION(STR_ANTENNACONFIRM1, onModelAntennaSwitchConfirm);
1045 SET_WARNING_INFO(STR_ANTENNACONFIRM2, sizeof(TR_ANTENNACONFIRM2), 0);
1047 else {
1048 g_model.moduleData[INTERNAL_MODULE].pxx.antennaMode = reusableBuffer.moduleSetup.antennaMode;
1049 checkExternalAntenna();
1052 break;
1053 #endif
1054 #endif
1055 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_LABEL:
1056 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_EXTERNALRF);
1057 break;
1059 #if defined(INTERNAL_MODULE_MULTI)
1060 case ITEM_MODEL_SETUP_INTERNAL_MODULE_TYPE:
1061 #endif
1062 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_TYPE:
1063 lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_MODE);
1064 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_EXTERNAL_MODULE_PROTOCOLS, moduleIdx == EXTERNAL_MODULE ? reusableBuffer.moduleSetup.newType : g_model.moduleData[INTERNAL_MODULE].type, menuHorizontalPosition==0 ? attr : 0);
1065 if (isModuleXJT(moduleIdx))
1066 lcdDrawTextAtIndex(MODEL_SETUP_3RD_COLUMN, y, STR_XJT_ACCST_RF_PROTOCOLS, 1+g_model.moduleData[moduleIdx].subType, (menuHorizontalPosition==1 ? attr : 0));
1067 else if (isModuleDSM2(moduleIdx))
1068 lcdDrawTextAtIndex(MODEL_SETUP_3RD_COLUMN, y, STR_DSM_PROTOCOLS, g_model.moduleData[moduleIdx].rfProtocol, (menuHorizontalPosition==1 ? attr : 0));
1069 else if (isModuleR9MNonAccess(moduleIdx))
1070 lcdDrawTextAtIndex(MODEL_SETUP_3RD_COLUMN, y, STR_R9M_REGION, g_model.moduleData[moduleIdx].subType, (menuHorizontalPosition==1 ? attr : 0));
1071 #if defined(MULTIMODULE)
1072 else if (isModuleMultimodule(moduleIdx)) {
1073 int multi_rfProto = g_model.moduleData[moduleIdx].getMultiProtocol();
1074 lcdDrawMultiProtocolString(MODEL_SETUP_3RD_COLUMN, y, moduleIdx, multi_rfProto, menuHorizontalPosition == 1 ? attr : 0);
1075 if (MULTIMODULE_HAS_SUBTYPE(moduleIdx))
1076 lcdDrawMultiSubProtocolString(MODEL_SETUP_4TH_COLUMN, y, moduleIdx, g_model.moduleData[moduleIdx].subType, menuHorizontalPosition==2 ? attr : 0);
1078 #endif
1079 if (attr && menuHorizontalPosition == 0 && moduleIdx == EXTERNAL_MODULE) {
1080 if (s_editMode > 0) {
1081 g_model.moduleData[EXTERNAL_MODULE].type = MODULE_TYPE_NONE;
1083 else if (reusableBuffer.moduleSetup.newType != reusableBuffer.moduleSetup.previousType) {
1084 g_model.moduleData[EXTERNAL_MODULE].type = reusableBuffer.moduleSetup.newType;
1085 reusableBuffer.moduleSetup.previousType = reusableBuffer.moduleSetup.newType;
1086 setModuleType(EXTERNAL_MODULE, g_model.moduleData[EXTERNAL_MODULE].type);
1088 else if (g_model.moduleData[EXTERNAL_MODULE].type == MODULE_TYPE_NONE) {
1089 g_model.moduleData[EXTERNAL_MODULE].type = reusableBuffer.moduleSetup.newType;
1092 if (attr) {
1093 if (s_editMode > 0) {
1094 switch (menuHorizontalPosition) {
1095 case 0: {
1096 #if defined(HARDWARE_INTERNAL_MODULE)
1097 if (moduleIdx == INTERNAL_MODULE) {
1098 uint8_t moduleType = checkIncDec(event, g_model.moduleData[moduleIdx].type, MODULE_TYPE_NONE, MODULE_TYPE_MAX, EE_MODEL,
1099 isInternalModuleAvailable);
1100 if (checkIncDec_Ret) {
1101 setModuleType(moduleIdx, moduleType);
1104 else
1105 #endif
1106 reusableBuffer.moduleSetup.newType = checkIncDec(event, reusableBuffer.moduleSetup.newType, MODULE_TYPE_NONE, MODULE_TYPE_MAX, EE_MODEL,
1107 isExternalModuleAvailable);
1109 break;
1110 case 1:
1111 if (isModuleDSM2(moduleIdx))
1112 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].rfProtocol, DSM2_PROTO_LP45, DSM2_PROTO_DSMX);
1113 #if defined(MULTIMODULE)
1114 else if (isModuleMultimodule(moduleIdx)) {
1115 int multiRfProto = g_model.moduleData[moduleIdx].getMultiProtocol();
1116 CHECK_INCDEC_MODELVAR_CHECK(event, multiRfProto, MODULE_SUBTYPE_MULTI_FIRST, MULTI_MAX_PROTOCOLS, isMultiProtocolSelectable);
1117 if (checkIncDec_Ret) {
1118 g_model.moduleData[moduleIdx].setMultiProtocol(multiRfProto);
1119 g_model.moduleData[moduleIdx].subType = 0;
1120 resetMultiProtocolsOptions(moduleIdx);
1123 #endif
1124 else if (isModuleR9MNonAccess(moduleIdx)) {
1125 g_model.moduleData[moduleIdx].subType = checkIncDec(event, g_model.moduleData[moduleIdx].subType, MODULE_SUBTYPE_R9M_FCC,
1126 MODULE_SUBTYPE_R9M_LAST, EE_MODEL, isR9MModeAvailable);
1128 else {
1129 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].subType, MODULE_SUBTYPE_PXX1_ACCST_D16, MODULE_SUBTYPE_PXX1_LAST);
1131 if (checkIncDec_Ret) {
1132 g_model.moduleData[moduleIdx].channelsStart = 0;
1133 g_model.moduleData[moduleIdx].channelsCount = defaultModuleChannels_M8(moduleIdx);
1135 break;
1137 #if defined(MULTIMODULE)
1138 case 2: {
1139 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].subType, 0, getMaxMultiSubtype(moduleIdx));
1140 if (checkIncDec_Ret) {
1141 resetMultiProtocolsOptions(moduleIdx);
1143 break;
1145 #endif
1148 #if POPUP_LEVEL > 1
1149 else if (old_editMode > 0) {
1150 if (isModuleR9MNonAccess(moduleIdx)) {
1151 if (g_model.moduleData[moduleIdx].subType > MODULE_SUBTYPE_R9M_EU) {
1152 POPUP_WARNING(STR_MODULE_PROTOCOL_FLEX_WARN_LINE1);
1153 SET_WARNING_INFO(STR_MODULE_PROTOCOL_WARN_LINE2, sizeof(TR_MODULE_PROTOCOL_WARN_LINE2) - 1, 0);
1155 #if POPUP_LEVEL >= 3
1156 else if (g_model.moduleData[moduleIdx].subType == MODULE_SUBTYPE_R9M_EU) {
1157 POPUP_WARNING(STR_MODULE_PROTOCOL_EU_WARN_LINE1);
1158 SET_WARNING_INFO(STR_MODULE_PROTOCOL_WARN_LINE2, sizeof(TR_MODULE_PROTOCOL_WARN_LINE2) - 1, 0);
1160 else {
1161 POPUP_WARNING(STR_MODULE_PROTOCOL_FCC_WARN_LINE1);
1162 SET_WARNING_INFO(STR_MODULE_PROTOCOL_WARN_LINE2, sizeof(TR_MODULE_PROTOCOL_WARN_LINE2) - 1, 0);
1164 #endif
1167 #endif
1169 break;
1170 #if defined(HARDWARE_INTERNAL_MODULE)
1171 case ITEM_MODEL_SETUP_INTERNAL_MODULE_CHANNELS:
1172 #endif
1173 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_CHANNELS:
1175 ModuleData & moduleData = g_model.moduleData[moduleIdx];
1176 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_CHANNELRANGE);
1177 if ((int8_t)MODULE_CHANNELS_ROWS(moduleIdx) >= 0) {
1178 drawStringWithIndex(MODEL_SETUP_2ND_COLUMN, y, STR_CH, moduleData.channelsStart+1, menuHorizontalPosition==0 ? attr : 0);
1179 lcdDrawText(lcdNextPos+5, y, "-");
1180 drawStringWithIndex(lcdNextPos+5, y, STR_CH, moduleData.channelsStart+sentModuleChannels(moduleIdx), menuHorizontalPosition==1 ? attr : 0);
1181 const char * delay = getModuleDelay(moduleIdx);
1182 if (delay)
1183 lcdDrawText(lcdNextPos + 15, y, delay);
1184 if (attr && s_editMode>0) {
1185 switch (menuHorizontalPosition) {
1186 case 0:
1187 CHECK_INCDEC_MODELVAR_ZERO(event, moduleData.channelsStart, 32-8-moduleData.channelsCount);
1188 break;
1189 case 1:
1190 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);
1191 if (checkIncDec_Ret && moduleData.type == MODULE_TYPE_PPM) {
1192 setDefaultPpmFrameLength(moduleIdx);
1194 break;
1198 break;
1200 case ITEM_MODEL_SETUP_TRAINER_LABEL:
1201 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_TRAINER);
1202 break;
1204 case ITEM_MODEL_SETUP_TRAINER_MODE:
1205 lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_MODE);
1206 g_model.trainerData.mode = editChoice(MODEL_SETUP_2ND_COLUMN, y, STR_VTRAINERMODES, g_model.trainerData.mode, 0, TRAINER_MODE_MAX(), attr, event, isTrainerModeAvailable);
1207 #if defined(BLUETOOTH)
1208 if (attr && checkIncDec_Ret) {
1209 bluetooth.state = BLUETOOTH_STATE_OFF;
1210 bluetooth.distantAddr[0] = 0;
1212 #endif
1213 break;
1215 #if defined(BLUETOOTH)
1216 case ITEM_MODEL_SETUP_TRAINER_BLUETOOTH:
1217 if (g_model.trainerData.mode == TRAINER_MODE_MASTER_BLUETOOTH) {
1218 if (attr) {
1219 s_editMode = 0;
1221 if (bluetooth.distantAddr[0]) {
1222 lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, bluetooth.distantAddr);
1223 drawButton(MODEL_SETUP_2ND_COLUMN, y, STR_CLEAR, attr);
1224 if (attr && event == EVT_KEY_FIRST(KEY_ENTER)) {
1225 bluetooth.state = BLUETOOTH_STATE_CLEAR_REQUESTED;
1226 memclear(bluetooth.distantAddr, sizeof(bluetooth.distantAddr));
1229 else {
1230 lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, "---");
1231 if (bluetooth.state < BLUETOOTH_STATE_IDLE)
1232 drawButton(MODEL_SETUP_2ND_COLUMN, y, STR_BLUETOOTH_INIT, attr);
1233 else
1234 drawButton(MODEL_SETUP_2ND_COLUMN, y, STR_BLUETOOTH_DISC, attr);
1235 if (attr && event == EVT_KEY_FIRST(KEY_ENTER)) {
1236 if (bluetooth.state < BLUETOOTH_STATE_IDLE) {
1237 bluetooth.state = BLUETOOTH_STATE_OFF;
1239 else {
1240 reusableBuffer.moduleSetup.bt.devicesCount = 0;
1241 bluetooth.state = BLUETOOTH_STATE_DISCOVER_REQUESTED;
1246 if (bluetooth.state == BLUETOOTH_STATE_DISCOVER_END && reusableBuffer.moduleSetup.bt.devicesCount > 0) {
1247 popupMenuItemsCount = min<uint8_t>(reusableBuffer.moduleSetup.bt.devicesCount, MAX_BLUETOOTH_DISTANT_ADDR);
1248 for (uint8_t i = 0; i < popupMenuItemsCount; i++) {
1249 popupMenuItems[i] = reusableBuffer.moduleSetup.bt.devices[i];
1251 POPUP_MENU_START(onBluetoothConnectMenu);
1254 else {
1255 if (bluetooth.distantAddr[0])
1256 lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y+1, bluetooth.distantAddr);
1257 else
1258 lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, "---");
1259 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, bluetooth.state == BLUETOOTH_STATE_CONNECTED ? STR_CONNECTED : STR_NOT_CONNECTED);
1261 break;
1262 #endif
1264 case ITEM_MODEL_SETUP_TRAINER_CHANNELS:
1265 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_CHANNELRANGE);
1266 drawStringWithIndex(MODEL_SETUP_2ND_COLUMN, y, STR_CH, g_model.trainerData.channelsStart+1, menuHorizontalPosition==0 ? attr : 0);
1267 lcdDrawText(lcdNextPos+5, y, "-");
1268 drawStringWithIndex(lcdNextPos+5, y, STR_CH, g_model.trainerData.channelsStart + 8 + g_model.trainerData.channelsCount, menuHorizontalPosition==1 ? attr : 0);
1269 if (attr && s_editMode > 0) {
1270 switch (menuHorizontalPosition) {
1271 case 0:
1272 CHECK_INCDEC_MODELVAR_ZERO(event, g_model.trainerData.channelsStart, 32-8-g_model.trainerData.channelsCount);
1273 break;
1274 case 1:
1275 CHECK_INCDEC_MODELVAR(event, g_model.trainerData.channelsCount, -4, min<int8_t>(MAX_TRAINER_CHANNELS_M8, 32-8-g_model.trainerData.channelsStart));
1276 break;
1279 break;
1281 case ITEM_MODEL_SETUP_TRAINER_PPM_PARAMS:
1282 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_PPMFRAME);
1283 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, (int16_t)g_model.trainerData.frameLength*5 + 225, (menuHorizontalPosition<=0 ? attr : 0) | PREC1|LEFT, 0, NULL, STR_MS);
1284 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN+80, y, (g_model.trainerData.delay*50)+300, (CURSOR_ON_LINE() || menuHorizontalPosition==1) ? attr|LEFT : LEFT, 0, NULL, "us");
1285 lcdDrawText(MODEL_SETUP_2ND_COLUMN+160, y, g_model.trainerData.pulsePol ? "+" : "-", (CURSOR_ON_LINE() || menuHorizontalPosition==2) ? attr : 0);
1286 if (attr && s_editMode>0) {
1287 switch (menuHorizontalPosition) {
1288 case 0:
1289 CHECK_INCDEC_MODELVAR(event, g_model.trainerData.frameLength, -20, 35);
1290 break;
1291 case 1:
1292 CHECK_INCDEC_MODELVAR(event, g_model.trainerData.delay, -4, 10);
1293 break;
1294 case 2:
1295 CHECK_INCDEC_MODELVAR_ZERO(event, g_model.trainerData.pulsePol, 1);
1296 break;
1299 break;
1301 #if defined(PXX2)
1302 case ITEM_MODEL_SETUP_REGISTRATION_ID:
1303 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_REG_ID);
1304 if (isDefaultModelRegistrationID())
1305 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_PXX2_DEFAULT);
1306 else
1307 lcdDrawSizedText(MODEL_SETUP_2ND_COLUMN, y, g_model.modelRegistrationID, PXX2_LEN_REGISTRATION_ID, ZCHAR);
1308 break;
1309 #endif
1311 #if defined(HARDWARE_INTERNAL_MODULE)
1312 case ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_MODEL_NUM:
1313 #endif
1314 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_MODEL_NUM:
1316 lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_RECEIVER_NUM);
1317 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, g_model.header.modelId[moduleIdx], attr | LEADING0 | LEFT, 2);
1318 if (attr) {
1319 CHECK_INCDEC_MODELVAR_ZERO(event, g_model.header.modelId[moduleIdx], getMaxRxNum(moduleIdx));
1320 if (event == EVT_KEY_LONG(KEY_ENTER)) {
1321 killEvents(event);
1322 uint8_t newVal = modelslist.findNextUnusedModelId(moduleIdx);
1323 if (newVal != g_model.header.modelId[moduleIdx]) {
1324 g_model.header.modelId[moduleIdx] = newVal;
1325 storageDirty(EE_MODEL);
1330 break;
1331 #if defined(HARDWARE_INTERNAL_MODULE)
1332 case ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_REGISTER_RANGE:
1333 #endif
1334 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_REGISTER_RANGE:
1336 lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_MODULE);
1337 drawButton(MODEL_SETUP_2ND_COLUMN, y, STR_REGISTER, (menuHorizontalPosition == 0 ? attr : 0));
1338 drawButton(MODEL_SETUP_2ND_COLUMN + MODEL_SETUP_SET_FAILSAFE_OFS, y, STR_MODULE_RANGE, (menuHorizontalPosition == 1 ? attr : 0));
1339 if (attr) {
1340 if (moduleState[moduleIdx].mode == MODULE_MODE_NORMAL && s_editMode > 0) {
1341 if (menuHorizontalPosition == 0 && event == EVT_KEY_BREAK(KEY_ENTER)) {
1342 startRegisterDialog(moduleIdx);
1344 else if (menuHorizontalPosition == 1) {
1345 moduleState[moduleIdx].mode = MODULE_MODE_RANGECHECK;
1348 if (s_editMode == 0 && !warningText) {
1349 moduleState[moduleIdx].mode = MODULE_MODE_NORMAL;
1351 if (moduleState[moduleIdx].mode == MODULE_MODE_NORMAL) {
1352 // REGISTER finished
1353 s_editMode = 0;
1357 break;
1358 #if defined(HARDWARE_INTERNAL_MODULE)
1359 case ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_OPTIONS:
1360 #endif
1361 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_OPTIONS:
1362 lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_OPTIONS);
1363 drawButton(MODEL_SETUP_2ND_COLUMN, y, STR_SET, attr);
1364 if (event == EVT_KEY_BREAK(KEY_ENTER) && attr) {
1365 g_moduleIdx = CURRENT_MODULE_EDITED(k);
1366 pushMenu(menuModelModuleOptions);
1368 break;
1369 #if defined(HARDWARE_INTERNAL_MODULE)
1370 case ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_1:
1371 case ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_2:
1372 case ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_RECEIVER_3:
1373 #endif
1374 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_1:
1375 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_2:
1376 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_PXX2_RECEIVER_3:
1378 uint8_t receiverIdx = CURRENT_RECEIVER_EDITED(k);
1379 ModuleInformation & moduleInformation = reusableBuffer.moduleSetup.pxx2.moduleInformation;
1381 drawStringWithIndex(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_RECEIVER, receiverIdx + 1);
1383 if (!isPXX2ReceiverUsed(moduleIdx, receiverIdx)) {
1384 drawButton(MODEL_SETUP_2ND_COLUMN, y, STR_MODULE_BIND, attr);
1385 if (attr && event == EVT_KEY_BREAK(KEY_ENTER)) {
1386 setPXX2ReceiverUsed(moduleIdx, receiverIdx);
1387 memclear(g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx], PXX2_LEN_RX_NAME);
1388 onPXX2ReceiverMenu(STR_BIND);
1390 break;
1393 drawReceiverName(MODEL_SETUP_2ND_COLUMN, y, moduleIdx, receiverIdx, attr);
1395 if (s_editMode && isModuleR9MAccess(moduleIdx) && moduleState[moduleIdx].mode == MODULE_MODE_NORMAL && reusableBuffer.moduleSetup.bindInformation.step < 0) {
1396 if (reusableBuffer.moduleSetup.bindInformation.step == BIND_MODULE_TX_INFORMATION_REQUEST && moduleInformation.information.modelID) {
1397 // For R9M ACCESS the module information has been requested to know if we are in EU mode. We just receive it here and continue
1398 if (moduleInformation.information.variant == PXX2_VARIANT_EU) {
1399 // In EU mode we will need the power of the module to know if telemetry can be proposed
1400 reusableBuffer.moduleSetup.bindInformation.step = BIND_MODULE_TX_SETTINGS_REQUEST;
1401 #if defined(SIMU)
1402 reusableBuffer.moduleSetup.pxx2.moduleSettings.txPower = 14;
1403 break;
1404 #else
1405 moduleState[moduleIdx].readModuleSettings(&reusableBuffer.moduleSetup.pxx2.moduleSettings);
1406 #endif
1408 else {
1409 reusableBuffer.moduleSetup.bindInformation.step = 0;
1410 moduleState[moduleIdx].startBind(&reusableBuffer.moduleSetup.bindInformation);
1413 else if (reusableBuffer.moduleSetup.bindInformation.step == BIND_MODULE_TX_SETTINGS_REQUEST && reusableBuffer.moduleSetup.pxx2.moduleSettings.txPower > 0) {
1414 // We just receive the module settings (for TX power)
1415 reusableBuffer.moduleSetup.bindInformation.step = 0;
1416 moduleState[moduleIdx].startBind(&reusableBuffer.moduleSetup.bindInformation);
1419 else if (attr && (moduleState[moduleIdx].mode == MODULE_MODE_NORMAL || s_editMode == 0)) {
1420 if (moduleState[moduleIdx].mode) {
1421 moduleState[moduleIdx].mode = 0;
1422 removePXX2ReceiverIfEmpty(moduleIdx, receiverIdx);
1423 killEvents(event); // we stopped BIND / SHARE, we don't want to re-open the menu
1424 event = 0;
1425 CLEAR_POPUP();
1427 s_editMode = 0;
1430 if (moduleState[moduleIdx].mode == MODULE_MODE_BIND) {
1431 if (reusableBuffer.moduleSetup.bindInformation.step == BIND_INIT) {
1432 if (reusableBuffer.moduleSetup.bindInformation.candidateReceiversCount > 0) {
1433 popupMenuItemsCount = min<uint8_t>(reusableBuffer.moduleSetup.bindInformation.candidateReceiversCount, PXX2_MAX_RECEIVERS_PER_MODULE);
1434 for (auto rx = 0; rx < popupMenuItemsCount; rx++) {
1435 popupMenuItems[rx] = reusableBuffer.moduleSetup.bindInformation.candidateReceiversNames[rx];
1437 // popupMenuTitle = STR_PXX2_SELECT_RX;
1438 POPUP_MENU_START(onPXX2BindMenu);
1443 if (attr && event == EVT_KEY_BREAK(KEY_ENTER)) {
1444 POPUP_MENU_ADD_ITEM(STR_BIND);
1445 POPUP_MENU_ADD_ITEM(STR_OPTIONS);
1446 POPUP_MENU_ADD_ITEM(STR_SHARE);
1447 POPUP_MENU_ADD_ITEM(STR_DELETE);
1448 POPUP_MENU_ADD_ITEM(STR_RESET);
1449 POPUP_MENU_START(onPXX2ReceiverMenu);
1452 break;
1453 #if defined(HARDWARE_INTERNAL_MODULE)
1454 case ITEM_MODEL_SETUP_INTERNAL_MODULE_NOT_ACCESS_RXNUM_BIND_RANGE:
1455 #endif
1456 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_NOT_ACCESS_BIND:
1458 ModuleData & moduleData = g_model.moduleData[moduleIdx];
1459 if (isModulePPM(moduleIdx)) {
1460 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_PPMFRAME);
1461 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, (int16_t)moduleData.ppm.frameLength*5 + 225, (menuHorizontalPosition<=0 ? attr : 0) | PREC1|LEFT, 0, NULL, STR_MS);
1462 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN+80, y, (moduleData.ppm.delay*50)+300, (CURSOR_ON_LINE() || menuHorizontalPosition==1) ? attr|LEFT : LEFT, 0, NULL, "us");
1463 lcdDrawText(MODEL_SETUP_2ND_COLUMN+160, y, moduleData.ppm.pulsePol ? "+" : "-", (CURSOR_ON_LINE() || menuHorizontalPosition==2) ? attr : 0);
1464 if (attr && s_editMode>0) {
1465 switch (menuHorizontalPosition) {
1466 case 0:
1467 CHECK_INCDEC_MODELVAR(event, moduleData.ppm.frameLength, -20, 35);
1468 break;
1469 case 1:
1470 CHECK_INCDEC_MODELVAR(event, moduleData.ppm.delay, -4, 10);
1471 break;
1472 case 2:
1473 CHECK_INCDEC_MODELVAR_ZERO(event, moduleData.ppm.pulsePol, 1);
1474 break;
1478 else if (isModuleSBUS(moduleIdx)) {
1479 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_REFRESHRATE);
1480 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, (int16_t)moduleData.ppm.frameLength*5 + 225, (menuHorizontalPosition<=0 ? attr : 0) | PREC1|LEFT, 0, NULL, STR_MS);
1481 lcdDrawText(MODEL_SETUP_3RD_COLUMN, y, moduleData.sbus.noninverted ? "not inverted" : "normal", (CURSOR_ON_LINE() || menuHorizontalPosition==1) ? attr : 0);
1483 if (attr && s_editMode>0) {
1484 switch (menuHorizontalPosition) {
1485 case 0:
1486 CHECK_INCDEC_MODELVAR(event, moduleData.ppm.frameLength, -33, 35);
1487 break;
1488 case 1:
1489 CHECK_INCDEC_MODELVAR_ZERO(event, moduleData.sbus.noninverted, 1);
1490 break;
1494 else {
1495 int l_posHorz = menuHorizontalPosition;
1496 coord_t xOffsetBind = MODEL_SETUP_BIND_OFS;
1497 if (!isModuleRxNumAvailable(moduleIdx)) {
1498 xOffsetBind = 0;
1499 lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_RECEIVER);
1500 if (attr) l_posHorz += 1;
1502 else {
1503 lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_RECEIVER_NUM);
1505 if (isModuleBindRangeAvailable(moduleIdx)) {
1506 if (xOffsetBind)
1507 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, g_model.header.modelId[moduleIdx], (l_posHorz==0 ? attr : 0) | LEADING0 | LEFT, 2);
1508 if (attr && l_posHorz==0) {
1509 if (s_editMode>0) {
1510 CHECK_INCDEC_MODELVAR_ZERO(event, g_model.header.modelId[moduleIdx], getMaxRxNum(moduleIdx));
1511 if (event == EVT_KEY_LONG(KEY_ENTER)) {
1512 killEvents(event);
1513 uint8_t newVal = modelslist.findNextUnusedModelId(moduleIdx);
1514 if (newVal != g_model.header.modelId[moduleIdx]) {
1515 g_model.header.modelId[moduleIdx] = newVal;
1516 storageDirty(EE_MODEL);
1521 drawButton(MODEL_SETUP_2ND_COLUMN+xOffsetBind, y, STR_MODULE_BIND, (moduleState[moduleIdx].mode == MODULE_MODE_BIND ? BUTTON_ON : BUTTON_OFF) | (l_posHorz==1 ? attr : 0));
1522 drawButton(MODEL_SETUP_2ND_COLUMN+MODEL_SETUP_RANGE_OFS+xOffsetBind, y, STR_MODULE_RANGE, (moduleState[moduleIdx].mode == MODULE_MODE_RANGECHECK ? BUTTON_ON : BUTTON_OFF) | (l_posHorz==2 ? attr : 0));
1523 uint8_t newFlag = 0;
1524 #if defined(MULTIMODULE)
1525 if (getMultiBindStatus(moduleIdx) == MULTI_BIND_FINISHED) {
1526 setMultiBindStatus(moduleIdx, MULTI_NORMAL_OPERATION);
1527 s_editMode = 0;
1529 #endif
1530 if (attr && l_posHorz>0) {
1531 if (s_editMode>0) {
1532 if (l_posHorz == 1) {
1533 if (isModuleR9MNonAccess(moduleIdx) || isModuleD16(moduleIdx)) {
1534 if (event == EVT_KEY_BREAK(KEY_ENTER)) {
1535 startBindMenu(moduleIdx);
1536 continue;
1538 if (moduleState[moduleIdx].mode == MODULE_MODE_BIND) {
1539 newFlag = MODULE_MODE_BIND;
1541 else {
1542 if (!popupMenuItemsCount) {
1543 s_editMode = 0; // this is when popup is exited before a choice is made
1547 else {
1548 newFlag = MODULE_MODE_BIND;
1551 else if (l_posHorz == 2) {
1552 newFlag = MODULE_MODE_RANGECHECK;
1556 moduleState[moduleIdx].mode = newFlag;
1557 #if defined(MULTIMODULE)
1558 if (newFlag == MODULE_MODE_BIND)
1559 setMultiBindStatus(moduleIdx, MULTI_BIND_INITIATED);
1560 #endif
1563 break;
1565 #if defined(HARDWARE_INTERNAL_MODULE)
1566 case ITEM_MODEL_SETUP_INTERNAL_MODULE_FAILSAFE:
1567 #endif
1568 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_FAILSAFE:
1570 ModuleData & moduleData = g_model.moduleData[moduleIdx];
1571 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_FAILSAFE);
1572 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_VFAILSAFE, moduleData.failsafeMode, menuHorizontalPosition==0 ? attr : 0);
1573 if (moduleData.failsafeMode == FAILSAFE_CUSTOM) {
1574 drawButton(MODEL_SETUP_2ND_COLUMN + MODEL_SETUP_SET_FAILSAFE_OFS, y, STR_SET, menuHorizontalPosition==1 ? attr : 0);
1576 if (attr) {
1577 if (moduleData.failsafeMode != FAILSAFE_CUSTOM)
1578 menuHorizontalPosition = 0;
1579 if (menuHorizontalPosition==0) {
1580 if (s_editMode>0) {
1581 CHECK_INCDEC_MODELVAR_ZERO(event, moduleData.failsafeMode, isModuleR9M(moduleIdx) ? FAILSAFE_NOPULSES : FAILSAFE_LAST);
1582 if (checkIncDec_Ret) SEND_FAILSAFE_NOW(moduleIdx);
1585 else if (menuHorizontalPosition==1) {
1586 s_editMode = 0;
1587 if (moduleData.failsafeMode == FAILSAFE_CUSTOM) {
1588 if (event == EVT_KEY_LONG(KEY_ENTER)) {
1589 killEvents(event);
1590 setCustomFailsafe(moduleIdx);
1591 storageDirty(EE_MODEL);
1592 AUDIO_WARNING1();
1593 SEND_FAILSAFE_NOW(moduleIdx);
1595 else if (event == EVT_KEY_BREAK(KEY_ENTER)) {
1596 g_moduleIdx = moduleIdx;
1597 pushMenu(menuModelFailsafe);
1601 else {
1602 lcdDrawSolidFilledRect(MODEL_SETUP_2ND_COLUMN, y, LCD_W - MODEL_SETUP_2ND_COLUMN - 2, 8, TEXT_COLOR);
1605 break;
1607 #if defined(HARDWARE_INTERNAL_MODULE)
1608 case ITEM_MODEL_SETUP_INTERNAL_MODULE_OPTIONS:
1609 #endif
1610 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_OPTIONS:
1612 #if defined(MULTIMODULE)
1613 if (MULTIMODULE_PROTOCOL_KNOWN(moduleIdx)) {
1614 int optionValue = g_model.moduleData[moduleIdx].multi.optionValue;
1616 const uint8_t multi_proto = g_model.moduleData[moduleIdx].getMultiProtocol();
1617 if (multi_proto < MODULE_SUBTYPE_MULTI_LAST) {
1618 const mm_protocol_definition * pdef = getMultiProtocolDefinition(multi_proto);
1619 if (pdef->optionsstr) {
1620 lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, pdef->optionsstr);
1621 if (attr && pdef->optionsstr == STR_MULTI_RFTUNE) {
1622 lcdDrawNumber(LCD_W - 10, y, TELEMETRY_RSSI(), RIGHT, 0, "RSSI(", ")");
1626 else {
1627 MultiModuleStatus &status = getMultiModuleStatus(moduleIdx);
1628 lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, mm_options_strings::options[status.optionDisp]);
1629 if (attr && status.optionDisp == 2) {
1630 lcdDrawNumber(LCD_W - 10, y, TELEMETRY_RSSI(), RIGHT, 0, "RSSI(", ")");
1633 if (multi_proto == MODULE_SUBTYPE_MULTI_FS_AFHDS2A)
1634 optionValue = 50 + 5 * optionValue;
1636 lcdDrawNumber(MODEL_SETUP_2ND_COLUMN, y, optionValue, LEFT | attr);
1637 if (attr) {
1638 int8_t min, max;
1639 getMultiOptionValues(multi_proto, min, max);
1640 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].multi.optionValue, min, max);
1643 #endif
1644 if (isModuleR9MNonAccess(moduleIdx)) {
1645 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MODULE_TELEMETRY);
1646 if (isSportLineUsedByInternalModule())
1647 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_DISABLE_INTERNAL);
1648 else
1649 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_MODULE_TELEM_ON);
1651 else if (isModuleSBUS(moduleIdx)) {
1652 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_WARN_BATTVOLTAGE);
1653 drawValueWithUnit(MODEL_SETUP_4TH_COLUMN, y, getBatteryVoltage(), UNIT_VOLTS, attr|PREC2|LEFT);
1655 break;
1657 #if defined(HARDWARE_INTERNAL_MODULE)
1658 case ITEM_MODEL_SETUP_INTERNAL_MODULE_POWER:
1659 #endif
1660 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_POWER:
1662 if (isModuleR9MNonAccess(moduleIdx)) {
1663 lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_RFPOWER);
1664 if (isModuleR9M_FCC_VARIANT(moduleIdx)) {
1665 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_R9M_FCC_POWER_VALUES, g_model.moduleData[moduleIdx].pxx.power, LEFT | attr);
1666 if (attr)
1667 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].pxx.power, 0, R9M_FCC_POWER_MAX);
1669 else {
1670 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_R9M_LBT_POWER_VALUES, g_model.moduleData[moduleIdx].pxx.power, LEFT | attr);
1671 if (attr)
1672 CHECK_INCDEC_MODELVAR(event, g_model.moduleData[moduleIdx].pxx.power, 0, R9M_LBT_POWER_MAX);
1673 if (attr && s_editMode == 0 && reusableBuffer.moduleSetup.r9mPower != g_model.moduleData[moduleIdx].pxx.power) {
1674 if ((reusableBuffer.moduleSetup.r9mPower + g_model.moduleData[moduleIdx].pxx.power) < 5) { //switching between mode 2 and 3 does not require rebind
1675 POPUP_WARNING(STR_WARNING);
1676 SET_WARNING_INFO(STR_REBIND, sizeof(TR_REBIND), 0);
1678 reusableBuffer.moduleSetup.r9mPower = g_model.moduleData[moduleIdx].pxx.power;
1682 #if defined(MULTIMODULE)
1683 else if (isModuleMultimodule(moduleIdx)) {
1684 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MULTI_LOWPOWER);
1685 g_model.moduleData[moduleIdx].multi.lowPowerMode = editCheckBox(g_model.moduleData[moduleIdx].multi.lowPowerMode, MODEL_SETUP_2ND_COLUMN, y, attr, event);
1687 #endif
1689 break;
1691 #if defined(MULTIMODULE)
1692 #if defined(HARDWARE_INTERNAL_MODULE)
1693 case ITEM_MODEL_SETUP_INTERNAL_MODULE_AUTOBIND:
1694 #endif
1695 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_AUTOBIND:
1696 if (g_model.moduleData[moduleIdx].getMultiProtocol() == MODULE_SUBTYPE_MULTI_DSM2)
1697 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MULTI_DSM_AUTODTECT);
1698 else
1699 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MULTI_AUTOBIND);
1700 g_model.moduleData[moduleIdx].multi.autoBindMode = editCheckBox(g_model.moduleData[moduleIdx].multi.autoBindMode, MODEL_SETUP_2ND_COLUMN, y, attr, event);
1701 break;
1703 #if defined(HARDWARE_INTERNAL_MODULE)
1704 case ITEM_MODEL_SETUP_INTERNAL_MODULE_DISABLE_TELEM:
1705 #endif
1706 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_DISABLE_TELEM:
1707 lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_DISABLE_TELEM);
1708 g_model.moduleData[moduleIdx].multi.disableTelemetry = editCheckBox(g_model.moduleData[moduleIdx].multi.disableTelemetry, MODEL_SETUP_2ND_COLUMN, y, attr, event);
1709 break;
1711 #if defined(HARDWARE_INTERNAL_MODULE)
1712 case ITEM_MODEL_SETUP_INTERNAL_MODULE_DISABLE_MAPPING:
1713 #endif
1714 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_DISABLE_MAPPING:
1715 lcdDrawText(MENUS_MARGIN_LEFT + INDENT_WIDTH, y, STR_DISABLE_CH_MAP);
1716 g_model.moduleData[moduleIdx].multi.disableMapping = editCheckBox(g_model.moduleData[moduleIdx].multi.disableMapping, MODEL_SETUP_2ND_COLUMN, y, attr, event);
1717 break;
1719 #if defined(HARDWARE_INTERNAL_MODULE)
1720 case ITEM_MODEL_SETUP_INTERNAL_MODULE_STATUS:
1721 #endif
1722 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_STATUS:
1724 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MODULE_STATUS);
1726 char statusText[64];
1727 getMultiModuleStatus(moduleIdx).getStatusString(statusText);
1728 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, statusText);
1729 break;
1732 #if defined(HARDWARE_INTERNAL_MODULE)
1733 case ITEM_MODEL_SETUP_INTERNAL_MODULE_SYNCSTATUS:
1734 #endif
1735 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_SYNCSTATUS:
1737 lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MODULE_SYNC);
1739 char statusText[64];
1740 getMultiSyncStatus(moduleIdx).getRefreshString(statusText);
1741 lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, statusText);
1742 break;
1744 #endif
1749 if (isModuleInRangeCheckMode()) {
1750 theme->drawMessageBox("RSSI :", NULL, NULL, WARNING_TYPE_INFO);
1751 lcdDrawNumber(WARNING_LINE_X, WARNING_INFOLINE_Y, TELEMETRY_RSSI(), DBLSIZE|LEFT);
1754 // some field just finished being edited
1755 if (old_editMode > 0 && s_editMode == 0) {
1756 ModelCell* mod_cell = modelslist.getCurrentModel();
1757 if (mod_cell) {
1758 switch(menuVerticalPosition) {
1759 case ITEM_MODEL_SETUP_NAME:
1760 mod_cell->setModelName(g_model.header.name);
1761 break;
1762 #if defined(HARDWARE_INTERNAL_MODULE)
1763 case ITEM_MODEL_SETUP_INTERNAL_MODULE_NOT_ACCESS_RXNUM_BIND_RANGE:
1764 if (menuHorizontalPosition != 0)
1765 break;
1766 case ITEM_MODEL_SETUP_INTERNAL_MODULE_TYPE:
1767 mod_cell->setRfData(&g_model);
1768 checkModelIdUnique(INTERNAL_MODULE);
1769 break;
1770 #endif
1771 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_NOT_ACCESS_BIND:
1772 if (menuHorizontalPosition != 0)
1773 break;
1774 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_TYPE:
1775 mod_cell->setRfData(&g_model);
1776 if (g_model.moduleData[EXTERNAL_MODULE].type != MODULE_TYPE_NONE)
1777 checkModelIdUnique(EXTERNAL_MODULE);
1778 break;
1783 return true;