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.
22 #include "storage/modelslist.h"
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
,
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
,
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
,
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
,
67 ITEM_MODEL_SETUP_REGISTRATION_ID
,
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
,
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
,
85 #if defined(INTERNAL_MODULE_PXX1) && defined(EXTERNAL_ANTENNA)
86 ITEM_MODEL_SETUP_INTERNAL_MODULE_ANTENNA
,
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
,
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
,
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
,
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
,
124 ITEM_MODEL_SETUP_TRAINER_CHANNELS
,
125 ITEM_MODEL_SETUP_TRAINER_PPM_PARAMS
,
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))
141 #define CURRENT_RECEIVER_EDITED(k) (EXTERNAL_MODULE)
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
;
153 reusableBuffer
.moduleSetup
.bt
.devicesCount
= 0;
154 bluetooth
.state
= BLUETOOTH_STATE_DISCOVER_END
;
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;
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
);
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
);
194 reusableBuffer
.moduleSetup
.bindInformation
.step
= BIND_START
;
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
;
228 reusableBuffer
.moduleSetup
.pxx2
.moduleInformation
.information
.modelID
= 1;
229 reusableBuffer
.moduleSetup
.pxx2
.moduleInformation
.information
.variant
= 2;
231 moduleState
[moduleIdx
].readModuleInformation(&reusableBuffer
.moduleSetup
.pxx2
.moduleInformation
, PXX2_HW_INFO_TX_ID
, PXX2_HW_INFO_TX_ID
);
235 moduleState
[moduleIdx
].startBind(&reusableBuffer
.moduleSetup
.bindInformation
);
239 else if (result
== STR_SHARE
) {
240 reusableBuffer
.moduleSetup
.pxx2
.shareReceiverIndex
= receiverIdx
;
241 moduleState
[moduleIdx
].mode
= MODULE_MODE_SHARE
;
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
);
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
);
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
);
282 reusableBuffer
.moduleSetup
.bindInformation
.step
= BIND_START
;
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
);
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
;
320 case EVT_KEY_BREAK(KEY_ENTER
):
321 if (menuVerticalPosition
!= ITEM_REGISTER_BUTTONS
) {
324 else if (reusableBuffer
.moduleSetup
.pxx2
.registerStep
>= REGISTER_RX_NAME_RECEIVED
&& menuHorizontalPosition
== 0) {
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
331 case EVT_KEY_LONG(KEY_EXIT
):
335 case EVT_KEY_FIRST(KEY_EXIT
):
336 if (s_editMode
<= 0) {
337 warningText
= nullptr;
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);
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);
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
;
388 POPUP_INPUT("", runPopupRegister
);
391 void checkModelIdUnique(uint8_t moduleIdx
)
393 if (isModuleXJTD8(moduleIdx
))
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
) {
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)
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
);
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
);
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
) {
475 CHECK_INCDEC_MODELVAR(event
, timer
.countdownBeep
, COUNTDOWN_SILENT
, COUNTDOWN_COUNT
- 1);
478 timer
.countdownStart
= -checkIncDecModel(event
, -timer
.countdownStart
, -1, +2);
484 int getSwitchWarningsCount()
487 for (int i
=0; i
<NUM_SWITCHES
; ++i
) {
488 if (SWITCH_WARNING_ALLOWED(i
)) {
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
))
509 #if defined(MULTIMODULE)
510 else if (isModuleMultimodule(moduleIdx
)) {
511 return 1 + MULTIMODULE_RFPROTO_COLUMNS(moduleIdx
);
519 #define TIMERS_ROWS TIMER_ROWS(0)
521 #define TIMERS_ROWS TIMER_ROWS(0), TIMER_ROWS(1)
523 #define TIMERS_ROWS TIMER_ROWS(0), TIMER_ROWS(1), TIMER_ROWS(2)
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
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
541 #define REGISTRATION_ID_ROWS uint8_t((isDefaultModelRegistrationID() || (warningText && popupFunc == runPopupRegister)) ? HIDDEN_ROW : READONLY_ROW),
543 #define REGISTRATION_ID_ROWS
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
);
557 reusableBuffer
.moduleSetup
.antennaMode
= g_model
.moduleData
[INTERNAL_MODULE
].pxx
.antennaMode
;
561 #define EXTERNAL_ANTENNA_ROW
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 */
585 #define INTERNAL_MODULE_ROWS
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
,
599 0, // Extended limits
605 0, // Throttle reverse
606 0, // Throttle trace source
609 LABEL(PreflightCheck
),
610 0, // Display checklist
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
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
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
;
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
)
667 uint8_t moduleIdx
= CURRENT_MODULE_EDITED(k
);
668 LcdFlags blink
= ((s_editMode
>0) ? BLINK
|INVERS
: INVERS
);
669 LcdFlags attr
= (sub
== k
? blink
: 0);
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
);
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
);
682 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
, y
, STR_VCSWFUNC
, 0, attr
);
683 if (attr
&& event
==EVT_KEY_BREAK(KEY_ENTER
) && READ_ONLY_UNLOCKED()) {
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
);
689 POPUP_WARNING(STR_NO_BITMAPS_ON_SD
);
694 case ITEM_MODEL_SETUP_TIMER1
:
695 editTimerMode(0, y
, attr
, event
);
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
);
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
);
708 case ITEM_MODEL_SETUP_TIMER1_COUNTDOWN_BEEP
:
709 editTimerCountdown(0, y
, attr
, event
);
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
);
718 case ITEM_MODEL_SETUP_TIMER2
:
719 editTimerMode(1, y
, attr
, event
);
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
);
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
);
732 case ITEM_MODEL_SETUP_TIMER2_COUNTDOWN_BEEP
:
733 editTimerCountdown(1, y
, attr
, event
);
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
);
743 case ITEM_MODEL_SETUP_TIMER3
:
744 editTimerMode(2, y
, attr
, event
);
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
);
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
);
757 case ITEM_MODEL_SETUP_TIMER3_COUNTDOWN_BEEP
:
758 editTimerCountdown(2, y
, attr
, event
);
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
);
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
);
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) {
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
);
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
);
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
);
799 case ITEM_MODEL_SETUP_THROTTLE_LABEL
:
800 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_THROTTLE_LABEL
);
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
);
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
)
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
);
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
);
827 case ITEM_MODEL_SETUP_PREFLIGHT_LABEL
:
828 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_PREFLIGHT
);
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
);
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
);
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
)) {
846 START_NO_HIGHLIGHT();
848 for (int i
=0; i
<NUM_SWITCHES
; i
++) {
849 bool enabled
= ((g_model
.switchWarningState
>> (3*i
)) & 0x07);
851 g_model
.switchWarningState
&= ~(0x07 << (3*i
));
852 unsigned int newState
= (switches_states
>> (2*i
) & 0x03) + 1;
853 g_model
.switchWarningState
|= (newState
<< (3*i
));
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) {
874 s
[1] = "x\300-\301"[state
];
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
));
884 g_model
.switchWarningState
= newStates
;
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
);
892 CHECK_INCDEC_MODELVAR(event
, g_model
.potsWarnMode
, POTS_WARN_OFF
, POTS_WARN_AUTO
);
893 storageDirty(EE_MODEL
);
897 case ITEM_MODEL_SETUP_POTS_WARNING
:
899 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_POTWARNING
);
901 if (!READ_ONLY() && menuHorizontalPosition
>= 0 && event
==EVT_KEY_LONG(KEY_ENTER
)) {
903 if (g_model
.potsWarnMode
== POTS_WARN_MANUAL
) {
904 SAVE_POT_POSITION(menuHorizontalPosition
);
906 storageDirty(EE_MODEL
);
910 if (!READ_ONLY() && menuHorizontalPosition
>= 0 && s_editMode
&& event
==EVT_KEY_BREAK(KEY_ENTER
)) {
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) {
929 lcdDrawTextAtIndex(x
, y
, STR_VSRCRAW
, NUM_STICKS
+1+i
, flags
);
930 x
+= MODEL_SETUP_SLIDPOT_SPACING
;
936 case ITEM_MODEL_SETUP_SLIDERS_WARNING
:
937 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_SLIDERWARNING
);
939 if (!READ_ONLY() && menuHorizontalPosition
+1 && event
==EVT_KEY_LONG(KEY_ENTER
)) {
941 if (g_model
.potsWarnMode
== POTS_WARN_MANUAL
) {
942 SAVE_POT_POSITION(menuHorizontalPosition
+NUM_POTS
);
944 storageDirty(EE_MODEL
);
948 if (!READ_ONLY() && menuHorizontalPosition
+1 && s_editMode
&& event
==EVT_KEY_BREAK(KEY_ENTER
)) {
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) {
967 lcdDrawTextAtIndex(x
, y
, STR_VSRCRAW
, NUM_STICKS
+1+i
, flags
);
968 x
+= MODEL_SETUP_SLIDPOT_SPACING
;
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);
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()) {
990 g_model
.beepANACenter
^= ((BeepANACenter
)1<<menuHorizontalPosition
);
991 storageDirty(EE_MODEL
);
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);
1003 #if defined(HARDWARE_INTERNAL_MODULE)
1004 case ITEM_MODEL_SETUP_INTERNAL_MODULE_LABEL
:
1005 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_INTERNALRF
);
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);
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
);
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);
1048 g_model
.moduleData
[INTERNAL_MODULE
].pxx
.antennaMode
= reusableBuffer
.moduleSetup
.antennaMode
;
1049 checkExternalAntenna();
1055 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_LABEL
:
1056 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_EXTERNALRF
);
1059 #if defined(INTERNAL_MODULE_MULTI)
1060 case ITEM_MODEL_SETUP_INTERNAL_MODULE_TYPE
:
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);
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
;
1093 if (s_editMode
> 0) {
1094 switch (menuHorizontalPosition
) {
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
);
1106 reusableBuffer
.moduleSetup
.newType
= checkIncDec(event
, reusableBuffer
.moduleSetup
.newType
, MODULE_TYPE_NONE
, MODULE_TYPE_MAX
, EE_MODEL
,
1107 isExternalModuleAvailable
);
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
);
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
);
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
);
1137 #if defined(MULTIMODULE)
1139 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[moduleIdx
].subType
, 0, getMaxMultiSubtype(moduleIdx
));
1140 if (checkIncDec_Ret
) {
1141 resetMultiProtocolsOptions(moduleIdx
);
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);
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);
1170 #if defined(HARDWARE_INTERNAL_MODULE)
1171 case ITEM_MODEL_SETUP_INTERNAL_MODULE_CHANNELS
:
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
);
1183 lcdDrawText(lcdNextPos
+ 15, y
, delay
);
1184 if (attr
&& s_editMode
>0) {
1185 switch (menuHorizontalPosition
) {
1187 CHECK_INCDEC_MODELVAR_ZERO(event
, moduleData
.channelsStart
, 32-8-moduleData
.channelsCount
);
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
);
1200 case ITEM_MODEL_SETUP_TRAINER_LABEL
:
1201 lcdDrawText(MENUS_MARGIN_LEFT
, y
, STR_TRAINER
);
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;
1215 #if defined(BLUETOOTH)
1216 case ITEM_MODEL_SETUP_TRAINER_BLUETOOTH
:
1217 if (g_model
.trainerData
.mode
== TRAINER_MODE_MASTER_BLUETOOTH
) {
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
));
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
);
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
;
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
);
1255 if (bluetooth
.distantAddr
[0])
1256 lcdDrawText(MENUS_MARGIN_LEFT
+ INDENT_WIDTH
, y
+1, bluetooth
.distantAddr
);
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
);
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
) {
1272 CHECK_INCDEC_MODELVAR_ZERO(event
, g_model
.trainerData
.channelsStart
, 32-8-g_model
.trainerData
.channelsCount
);
1275 CHECK_INCDEC_MODELVAR(event
, g_model
.trainerData
.channelsCount
, -4, min
<int8_t>(MAX_TRAINER_CHANNELS_M8
, 32-8-g_model
.trainerData
.channelsStart
));
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
) {
1289 CHECK_INCDEC_MODELVAR(event
, g_model
.trainerData
.frameLength
, -20, 35);
1292 CHECK_INCDEC_MODELVAR(event
, g_model
.trainerData
.delay
, -4, 10);
1295 CHECK_INCDEC_MODELVAR_ZERO(event
, g_model
.trainerData
.pulsePol
, 1);
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
);
1307 lcdDrawSizedText(MODEL_SETUP_2ND_COLUMN
, y
, g_model
.modelRegistrationID
, PXX2_LEN_REGISTRATION_ID
, ZCHAR
);
1311 #if defined(HARDWARE_INTERNAL_MODULE)
1312 case ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_MODEL_NUM
:
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);
1319 CHECK_INCDEC_MODELVAR_ZERO(event
, g_model
.header
.modelId
[moduleIdx
], getMaxRxNum(moduleIdx
));
1320 if (event
== EVT_KEY_LONG(KEY_ENTER
)) {
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
);
1331 #if defined(HARDWARE_INTERNAL_MODULE)
1332 case ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_REGISTER_RANGE
:
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));
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
1358 #if defined(HARDWARE_INTERNAL_MODULE)
1359 case ITEM_MODEL_SETUP_INTERNAL_MODULE_PXX2_OPTIONS
:
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
);
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
:
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
);
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
;
1402 reusableBuffer
.moduleSetup
.pxx2
.moduleSettings
.txPower
= 14;
1405 moduleState
[moduleIdx
].readModuleSettings(&reusableBuffer
.moduleSetup
.pxx2
.moduleSettings
);
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
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
);
1453 #if defined(HARDWARE_INTERNAL_MODULE)
1454 case ITEM_MODEL_SETUP_INTERNAL_MODULE_NOT_ACCESS_RXNUM_BIND_RANGE
:
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
) {
1467 CHECK_INCDEC_MODELVAR(event
, moduleData
.ppm
.frameLength
, -20, 35);
1470 CHECK_INCDEC_MODELVAR(event
, moduleData
.ppm
.delay
, -4, 10);
1473 CHECK_INCDEC_MODELVAR_ZERO(event
, moduleData
.ppm
.pulsePol
, 1);
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
) {
1486 CHECK_INCDEC_MODELVAR(event
, moduleData
.ppm
.frameLength
, -33, 35);
1489 CHECK_INCDEC_MODELVAR_ZERO(event
, moduleData
.sbus
.noninverted
, 1);
1495 int l_posHorz
= menuHorizontalPosition
;
1496 coord_t xOffsetBind
= MODEL_SETUP_BIND_OFS
;
1497 if (!isModuleRxNumAvailable(moduleIdx
)) {
1499 lcdDrawText(MENUS_MARGIN_LEFT
+ INDENT_WIDTH
, y
, STR_RECEIVER
);
1500 if (attr
) l_posHorz
+= 1;
1503 lcdDrawText(MENUS_MARGIN_LEFT
+ INDENT_WIDTH
, y
, STR_RECEIVER_NUM
);
1505 if (isModuleBindRangeAvailable(moduleIdx
)) {
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) {
1510 CHECK_INCDEC_MODELVAR_ZERO(event
, g_model
.header
.modelId
[moduleIdx
], getMaxRxNum(moduleIdx
));
1511 if (event
== EVT_KEY_LONG(KEY_ENTER
)) {
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
);
1530 if (attr
&& l_posHorz
>0) {
1532 if (l_posHorz
== 1) {
1533 if (isModuleR9MNonAccess(moduleIdx
) || isModuleD16(moduleIdx
)) {
1534 if (event
== EVT_KEY_BREAK(KEY_ENTER
)) {
1535 startBindMenu(moduleIdx
);
1538 if (moduleState
[moduleIdx
].mode
== MODULE_MODE_BIND
) {
1539 newFlag
= MODULE_MODE_BIND
;
1542 if (!popupMenuItemsCount
) {
1543 s_editMode
= 0; // this is when popup is exited before a choice is made
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
);
1565 #if defined(HARDWARE_INTERNAL_MODULE)
1566 case ITEM_MODEL_SETUP_INTERNAL_MODULE_FAILSAFE
:
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);
1577 if (moduleData
.failsafeMode
!= FAILSAFE_CUSTOM
)
1578 menuHorizontalPosition
= 0;
1579 if (menuHorizontalPosition
==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) {
1587 if (moduleData
.failsafeMode
== FAILSAFE_CUSTOM
) {
1588 if (event
== EVT_KEY_LONG(KEY_ENTER
)) {
1590 setCustomFailsafe(moduleIdx
);
1591 storageDirty(EE_MODEL
);
1593 SEND_FAILSAFE_NOW(moduleIdx
);
1595 else if (event
== EVT_KEY_BREAK(KEY_ENTER
)) {
1596 g_moduleIdx
= moduleIdx
;
1597 pushMenu(menuModelFailsafe
);
1602 lcdDrawSolidFilledRect(MODEL_SETUP_2ND_COLUMN
, y
, LCD_W
- MODEL_SETUP_2ND_COLUMN
- 2, 8, TEXT_COLOR
);
1607 #if defined(HARDWARE_INTERNAL_MODULE)
1608 case ITEM_MODEL_SETUP_INTERNAL_MODULE_OPTIONS
:
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(", ")");
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
);
1639 getMultiOptionValues(multi_proto
, min
, max
);
1640 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[moduleIdx
].multi
.optionValue
, min
, max
);
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
);
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
);
1657 #if defined(HARDWARE_INTERNAL_MODULE)
1658 case ITEM_MODEL_SETUP_INTERNAL_MODULE_POWER
:
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
);
1667 CHECK_INCDEC_MODELVAR(event
, g_model
.moduleData
[moduleIdx
].pxx
.power
, 0, R9M_FCC_POWER_MAX
);
1670 lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN
, y
, STR_R9M_LBT_POWER_VALUES
, g_model
.moduleData
[moduleIdx
].pxx
.power
, LEFT
| 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
);
1691 #if defined(MULTIMODULE)
1692 #if defined(HARDWARE_INTERNAL_MODULE)
1693 case ITEM_MODEL_SETUP_INTERNAL_MODULE_AUTOBIND
:
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
);
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
);
1703 #if defined(HARDWARE_INTERNAL_MODULE)
1704 case ITEM_MODEL_SETUP_INTERNAL_MODULE_DISABLE_TELEM
:
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
);
1711 #if defined(HARDWARE_INTERNAL_MODULE)
1712 case ITEM_MODEL_SETUP_INTERNAL_MODULE_DISABLE_MAPPING
:
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
);
1719 #if defined(HARDWARE_INTERNAL_MODULE)
1720 case ITEM_MODEL_SETUP_INTERNAL_MODULE_STATUS
:
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
);
1732 #if defined(HARDWARE_INTERNAL_MODULE)
1733 case ITEM_MODEL_SETUP_INTERNAL_MODULE_SYNCSTATUS
:
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
);
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();
1758 switch(menuVerticalPosition
) {
1759 case ITEM_MODEL_SETUP_NAME
:
1760 mod_cell
->setModelName(g_model
.header
.name
);
1762 #if defined(HARDWARE_INTERNAL_MODULE)
1763 case ITEM_MODEL_SETUP_INTERNAL_MODULE_NOT_ACCESS_RXNUM_BIND_RANGE
:
1764 if (menuHorizontalPosition
!= 0)
1766 case ITEM_MODEL_SETUP_INTERNAL_MODULE_TYPE
:
1767 mod_cell
->setRfData(&g_model
);
1768 checkModelIdUnique(INTERNAL_MODULE
);
1771 case ITEM_MODEL_SETUP_EXTERNAL_MODULE_NOT_ACCESS_BIND
:
1772 if (menuHorizontalPosition
!= 0)
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
);