From bf10c912bcedab50f6c9e91c2af809f1e5e0f2d3 Mon Sep 17 00:00:00 2001 From: 3djc Date: Sat, 27 Apr 2019 11:50:55 +0200 Subject: [PATCH] Horus : very draft internal PXX2 module --- radio/src/gui/128x64/model_setup.cpp | 2 +- radio/src/gui/480x272/lcd.cpp | 8 + radio/src/gui/480x272/lcd.h | 1 + radio/src/gui/480x272/menus.cpp | 8 + radio/src/gui/480x272/menus.h | 3 + .../src/gui/480x272/model_module_options.cpp | 158 ++++++ .../gui/480x272/model_receiver_options.cpp | 135 +++++ radio/src/gui/480x272/model_setup.cpp | 470 ++++++++++++++---- 8 files changed, 696 insertions(+), 89 deletions(-) diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index 0c0fb76fd..03d3e5895 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -565,7 +565,7 @@ void menuModelSetup(event_t event) IF_NOT_PXX2_MODULE(INTERNAL_MODULE, IF_INTERNAL_MODULE_ON(HAS_RF_PROTOCOL_MODELINDEX(g_model.moduleData[INTERNAL_MODULE].rfProtocol) ? (uint8_t)2 : (uint8_t)1)), IF_PXX2_MODULE(INTERNAL_MODULE, 0), // RxNum ANTENNA_ROW - IF_INTERNAL_MODULE_ON(FAILSAFE_ROWS(INTERNAL_MODULE)), // Module start channel + IF_INTERNAL_MODULE_ON(FAILSAFE_ROWS(INTERNAL_MODULE)), // Failsafe IF_PXX2_MODULE(INTERNAL_MODULE, 1), // Range check and Register buttons IF_PXX2_MODULE(INTERNAL_MODULE, 0), // Module options IF_PXX2_MODULE(INTERNAL_MODULE, 0), // Receiver 1 diff --git a/radio/src/gui/480x272/lcd.cpp b/radio/src/gui/480x272/lcd.cpp index a9f9075ab..da7641910 100644 --- a/radio/src/gui/480x272/lcd.cpp +++ b/radio/src/gui/480x272/lcd.cpp @@ -511,3 +511,11 @@ void DMABitmapConvert(uint16_t * dest, const uint8_t * src, uint16_t w, uint16_t } } #endif + +void drawReceiverName(coord_t x, coord_t y, uint8_t moduleIdx, uint8_t receiverIdx, LcdFlags flags) +{ + if (g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx][0] != '\0') + lcdDrawSizedText(x, y, g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx], effectiveLen(g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx], PXX2_LEN_RX_NAME), flags); + else + lcdDrawText(x, y, "---", flags); +} \ No newline at end of file diff --git a/radio/src/gui/480x272/lcd.h b/radio/src/gui/480x272/lcd.h index 0cb926789..c541875c3 100644 --- a/radio/src/gui/480x272/lcd.h +++ b/radio/src/gui/480x272/lcd.h @@ -151,6 +151,7 @@ void lcdDrawNumber(coord_t x, coord_t y, int32_t val, LcdFlags flags=0, uint8_t void drawRtcTime(coord_t x, coord_t y, LcdFlags att=0); void drawTimer(coord_t x, coord_t y, putstime_t tme, LcdFlags att=0); +void drawReceiverName(coord_t x, coord_t y, uint8_t moduleIdx, uint8_t receiverIdx, LcdFlags flags=0); void putsModelName(coord_t x, coord_t y, char *name, uint8_t id, LcdFlags att); void putsStickName(coord_t x, coord_t y, uint8_t idx, LcdFlags att=0); diff --git a/radio/src/gui/480x272/menus.cpp b/radio/src/gui/480x272/menus.cpp index 55b8ddef0..498f39191 100644 --- a/radio/src/gui/480x272/menus.cpp +++ b/radio/src/gui/480x272/menus.cpp @@ -34,6 +34,14 @@ void popMenu() TRACE("popMenu(%d)", menuLevel); } +void abortPopMenu() +{ + menuLevel = menuLevel + 1; + menuEvent = 0; + TRACE("popMenu(%d) aborted", menuLevel); +} + + void chainMenu(MenuHandlerFunc newMenu) { menuHandlers[menuLevel] = newMenu; diff --git a/radio/src/gui/480x272/menus.h b/radio/src/gui/480x272/menus.h index b61d9a4dd..bf1ef32a3 100644 --- a/radio/src/gui/480x272/menus.h +++ b/radio/src/gui/480x272/menus.h @@ -51,6 +51,7 @@ extern event_t menuEvent; void chainMenu(MenuHandlerFunc newMenu); void pushMenu(MenuHandlerFunc newMenu); void popMenu(); +void abortPopMenu(); inline bool isRadioMenuDisplayed() { @@ -174,6 +175,8 @@ const uint8_t MONITOR_ICONS[] = { bool menuModelSetup(event_t event); bool menuModelFailsafe(event_t event); +bool menuModelModuleOptions(event_t event); +bool menuModelReceiverOptions(event_t event); bool menuModelHeli(event_t event); bool menuModelFlightModesAll(event_t event); bool menuModelExposAll(event_t event); diff --git a/radio/src/gui/480x272/model_module_options.cpp b/radio/src/gui/480x272/model_module_options.cpp index f72d3594f..75de269b3 100644 --- a/radio/src/gui/480x272/model_module_options.cpp +++ b/radio/src/gui/480x272/model_module_options.cpp @@ -19,3 +19,161 @@ */ #include +#include + +#define RECEIVER_OPTIONS_2ND_COLUMN (200) + +extern uint8_t g_moduleIdx; + +void onTxOptionsUpdateConfirm(const char * result) +{ + if (result == STR_OK) + moduleState[g_moduleIdx].readModuleSettings(&reusableBuffer.hardwareAndSettings.moduleSettings); + else + popMenu(); +} + +enum { + ITEM_MODULE_SETTINGS_RF_PROTOCOL, + ITEM_MODULE_SETTINGS_EXTERNAL_ANTENNA, + ITEM_MODULE_SETTINGS_POWER, + ITEM_MODULE_SETTINGS_COUNT +}; + +#define IF_MODULE_OPTIONS(option, count) uint8_t(isModuleOptionAvailable(modelId, option) ? count : HIDDEN_ROW) + +bool isPowerAvailable(int value) +{ + uint8_t modelId = reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].information.modelID; + uint8_t variant = reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].information.variant; + + if (modelId == PXX2_MODULE_R9M_LITE) { + if (variant == PXX2_VARIANT_EU) + return (value == 14 /* 25 mW */ || + value == 20 /* 100 mW */); + else + return value == 20; /* 100 mW */ + } + else if (modelId == PXX2_MODULE_R9M || modelId == PXX2_MODULE_R9M_LITE_PRO) { + if (variant == PXX2_VARIANT_EU) + return (value == 14 /* 25 mW */ || + value == 23 /* 200 mW */ || + value == 27 /* 500 mW */); + else + return (value == 10 /* 10 mW */ || + value == 20 /* 100 mW */ || + value == 27 /* 500 mW */ || + value == 30 /* 1000 mW */); + } + else { + return (value <= 20); /* 100 mW max for XJTs */ + } +} + +bool menuModelModuleOptions(event_t event) +{ + uint8_t modelId = reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].information.modelID; + // uint8_t variant = reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].information.variant; + + SUBMENU("Module Options", ICON_RADIO, ITEM_MODULE_SETTINGS_COUNT, { + IF_MODULE_OPTIONS(MODULE_OPTION_RF_PROTOCOL, 0), + IF_MODULE_OPTIONS(MODULE_OPTION_EXTERNAL_ANTENNA, 0), + IF_MODULE_OPTIONS(MODULE_OPTION_POWER, 0), + }); + + if (event == EVT_ENTRY) { +#if defined(SIMU) + reusableBuffer.hardwareAndSettings.moduleSettings.state = PXX2_SETTINGS_OK; +#else + // no need to initialize reusableBuffer.hardwareAndSettings.moduleState.state to PXX2_HARDWARE_INFO + moduleState[g_moduleIdx].readModuleInformation(&reusableBuffer.hardwareAndSettings.modules[g_moduleIdx], PXX2_HW_INFO_TX_ID, PXX2_HW_INFO_TX_ID); +#endif + } + + if (reusableBuffer.hardwareAndSettings.moduleSettings.state == PXX2_HARDWARE_INFO && moduleState[g_moduleIdx].mode == MODULE_MODE_NORMAL) { + moduleState[g_moduleIdx].readModuleSettings(&reusableBuffer.hardwareAndSettings.moduleSettings); + } + + if (menuEvent) { + moduleState[g_moduleIdx].mode = MODULE_MODE_NORMAL; + if (reusableBuffer.hardwareAndSettings.moduleSettingsDirty) { + abortPopMenu(); + POPUP_CONFIRMATION("Update TX options?", onTxOptionsUpdateConfirm); + } + else { + return true; + } + } + + if (event == EVT_KEY_LONG(KEY_ENTER) && reusableBuffer.hardwareAndSettings.moduleSettingsDirty) { + killEvents(event); + reusableBuffer.hardwareAndSettings.moduleSettingsDirty = 0; + moduleState[g_moduleIdx].writeModuleSettings(&reusableBuffer.hardwareAndSettings.moduleSettings); + } + + if (reusableBuffer.hardwareAndSettings.moduleSettingsDirty == 2 && reusableBuffer.hardwareAndSettings.moduleSettings.state == PXX2_SETTINGS_OK) { + popMenu(); + } + + if (modelId != 0 && mstate_tab[menuVerticalPosition] == HIDDEN_ROW) { + menuVerticalPosition = 0; + while (menuVerticalPosition < ITEM_MODULE_SETTINGS_COUNT && mstate_tab[menuVerticalPosition] == HIDDEN_ROW) { + ++menuVerticalPosition; + } + } + + int8_t sub = menuVerticalPosition; + lcdDrawText(300, 0, PXX2modulesModels[modelId] ); + + if (reusableBuffer.hardwareAndSettings.moduleSettings.state == PXX2_SETTINGS_OK) { + for (uint8_t k=0; k0 ? BLINK|INVERS : INVERS) : 0); + + switch (i) { + case ITEM_MODULE_SETTINGS_RF_PROTOCOL: + lcdDrawText(0, y, "RF Protocol"); + lcdDrawTextAtIndex(RECEIVER_OPTIONS_2ND_COLUMN, y, STR_XJT_PROTOCOLS, reusableBuffer.hardwareAndSettings.moduleSettings.rfProtocol + 1, attr); + if (attr) { + reusableBuffer.hardwareAndSettings.moduleSettings.rfProtocol = checkIncDec(event, reusableBuffer.hardwareAndSettings.moduleSettings.rfProtocol, RF_PROTO_X16, RF_PROTO_LAST, 0, nullptr); + if (checkIncDec_Ret) { + reusableBuffer.hardwareAndSettings.moduleSettingsDirty = true; + } + } + break; + + case ITEM_MODULE_SETTINGS_EXTERNAL_ANTENNA: + lcdDrawText(0, y, "Ext. antenna"); + reusableBuffer.hardwareAndSettings.moduleSettings.externalAntenna = editCheckBox(reusableBuffer.hardwareAndSettings.moduleSettings.externalAntenna, RECEIVER_OPTIONS_2ND_COLUMN, y, attr, event); + if (attr && checkIncDec_Ret) { + reusableBuffer.hardwareAndSettings.moduleSettingsDirty = true; + } + break; + + case ITEM_MODULE_SETTINGS_POWER: + lcdDrawText(0, y, "Power"); + lcdDrawNumber(RECEIVER_OPTIONS_2ND_COLUMN, y, reusableBuffer.hardwareAndSettings.moduleSettings.txPower, attr); + lcdDrawText(lcdNextPos, y, "dBm("); + drawPower(lcdNextPos, y, reusableBuffer.hardwareAndSettings.moduleSettings.txPower); + lcdDrawText(lcdNextPos, y, ")"); + if (attr) { + reusableBuffer.hardwareAndSettings.moduleSettings.txPower = checkIncDec(event, reusableBuffer.hardwareAndSettings.moduleSettings.txPower, 0, 30, 0, &isPowerAvailable); + if (checkIncDec_Ret) { + reusableBuffer.hardwareAndSettings.moduleSettingsDirty = true; + } + } + break; + } + } + } + else { + lcdDrawCenteredText(LCD_H/2, "Waiting for TX..."); + } + return true; +} diff --git a/radio/src/gui/480x272/model_receiver_options.cpp b/radio/src/gui/480x272/model_receiver_options.cpp index f72d3594f..1344bbcb4 100644 --- a/radio/src/gui/480x272/model_receiver_options.cpp +++ b/radio/src/gui/480x272/model_receiver_options.cpp @@ -19,3 +19,138 @@ */ #include + +#define RECEIVER_OPTIONS_2ND_COLUMN 80 + +extern uint8_t g_moduleIdx; + +void onRxOptionsUpdateConfirm(const char * result) +{ + if (result == STR_OK) { + reusableBuffer.hardwareAndSettings.receiverSettings.state = PXX2_SETTINGS_WRITE; + reusableBuffer.hardwareAndSettings.receiverSettings.dirty = 2; + reusableBuffer.hardwareAndSettings.receiverSettings.timeout = 0; + moduleState[g_moduleIdx].mode = MODULE_MODE_RECEIVER_SETTINGS; + } + else { + popMenu(); + } +} + +enum { + ITEM_RECEIVER_TELEMETRY, + ITEM_RECEIVER_PWM_RATE, + ITEM_RECEIVER_PINMAP_FIRST +}; + +bool menuModelReceiverOptions(event_t event) +{ + const int lim = (g_model.extendedLimits ? (512 * LIMIT_EXT_PERCENT / 100) : 512) * 2; + uint8_t wbar = LCD_W / 2 - 20; + auto outputsCount = min(16, reusableBuffer.hardwareAndSettings.receiverSettings.outputsCount); + + SIMPLE_SUBMENU(STR_RECEIVER_OPTIONS, ICON_RADIO, ITEM_RECEIVER_PINMAP_FIRST + outputsCount); + + if (event == EVT_ENTRY) { +#if defined(SIMU) + reusableBuffer.hardwareAndSettings.receiverSettings.state = PXX2_SETTINGS_OK; + reusableBuffer.hardwareAndSettings.receiverSettings.outputsCount = 8; +#else + // no need to initialize reusableBuffer.hardwareAndSettings.receiverSettings.state to PXX2_HARDWARE_INFO + moduleState[g_moduleIdx].readModuleInformation(&reusableBuffer.hardwareAndSettings.modules[g_moduleIdx], reusableBuffer.hardwareAndSettings.receiverSettings.receiverId, reusableBuffer.hardwareAndSettings.receiverSettings.receiverId); +#endif + } + + if (reusableBuffer.hardwareAndSettings.receiverSettings.state == PXX2_HARDWARE_INFO && moduleState[g_moduleIdx].mode == MODULE_MODE_NORMAL) { + reusableBuffer.hardwareAndSettings.receiverSettings.state = PXX2_SETTINGS_READ; + moduleState[g_moduleIdx].mode = MODULE_MODE_RECEIVER_SETTINGS; + } + + if (menuEvent) { + moduleState[g_moduleIdx].mode = MODULE_MODE_NORMAL; + if (reusableBuffer.hardwareAndSettings.receiverSettings.dirty) { + abortPopMenu(); + POPUP_CONFIRMATION("Update RX options?", onRxOptionsUpdateConfirm); + } + else { + return true; + } + } + + if (event == EVT_KEY_LONG(KEY_ENTER) && reusableBuffer.hardwareAndSettings.receiverSettings.dirty) { + killEvents(event); + reusableBuffer.hardwareAndSettings.receiverSettings.state = PXX2_SETTINGS_WRITE; + reusableBuffer.hardwareAndSettings.receiverSettings.dirty = 0; + reusableBuffer.hardwareAndSettings.receiverSettings.timeout = 0; + moduleState[g_moduleIdx].mode = MODULE_MODE_RECEIVER_SETTINGS; + } + + if (reusableBuffer.hardwareAndSettings.receiverSettings.dirty == 2 && reusableBuffer.hardwareAndSettings.receiverSettings.state == PXX2_SETTINGS_OK) { + popMenu(); + return true; + } + + int8_t sub = menuVerticalPosition; + drawReceiverName(130, 0, g_moduleIdx, reusableBuffer.hardwareAndSettings.receiverSettings.receiverId); + + if (reusableBuffer.hardwareAndSettings.receiverSettings.state == PXX2_SETTINGS_OK) { + for (uint8_t k=0; k0 ? BLINK|INVERS : INVERS) : 0); + + switch (i) { + case ITEM_RECEIVER_TELEMETRY: + lcdDrawText(MENUS_MARGIN_LEFT, y, "Telemetry disabled"); + reusableBuffer.hardwareAndSettings.receiverSettings.telemetryDisabled = editCheckBox(reusableBuffer.hardwareAndSettings.receiverSettings.telemetryDisabled, RECEIVER_OPTIONS_2ND_COLUMN, y, attr, event); + if (attr && checkIncDec_Ret) { + reusableBuffer.hardwareAndSettings.receiverSettings.dirty = true; + } + break; + + case ITEM_RECEIVER_PWM_RATE: + lcdDrawText(MENUS_MARGIN_LEFT, y, isModuleR9M2(g_moduleIdx) ? "6.67ms PWM": "9ms PWM"); + reusableBuffer.hardwareAndSettings.receiverSettings.pwmRate = editCheckBox(reusableBuffer.hardwareAndSettings.receiverSettings.pwmRate, RECEIVER_OPTIONS_2ND_COLUMN, y, attr, event); + if (attr && checkIncDec_Ret) { + reusableBuffer.hardwareAndSettings.receiverSettings.dirty = true; + } + break; + + default: + // Pin + { + uint8_t pin = i - ITEM_RECEIVER_PINMAP_FIRST; + if (pin < reusableBuffer.hardwareAndSettings.receiverSettings.outputsCount) { + uint8_t channel = g_model.moduleData[g_moduleIdx].channelsStart + reusableBuffer.hardwareAndSettings.receiverSettings.outputsMapping[pin]; + int32_t channelValue = channelOutputs[channel]; + lcdDrawText(0, y, "Pin"); + lcdDrawNumber(lcdNextPos + 1, y, pin + 1); + putsChn(80, y, channel + 1, attr); + + // Channel + if (attr) { + channel = checkIncDec(event, channel, 0, sentModuleChannels(g_moduleIdx) - 1); + if (checkIncDec_Ret) { + reusableBuffer.hardwareAndSettings.receiverSettings.outputsMapping[pin] = channel; + reusableBuffer.hardwareAndSettings.receiverSettings.dirty = true; + } + } + + // Bargraph +#if !defined(PCBX7) // X7 LCD doesn't like too many horizontal lines + lcdDrawRect(LCD_W - 3 - wbar, y + 1, wbar + 1, 4); +#endif + const uint8_t lenChannel = limit(1, (abs(channelValue) * wbar / 2 + lim / 2) / lim, wbar / 2); + const coord_t xChannel = (channelValue > 0) ? LCD_W - 3 - wbar / 2 : LCD_W - 2 - wbar / 2 - lenChannel; + lcdDrawHorizontalLine(xChannel, y + 2, lenChannel, SOLID, 0); + lcdDrawHorizontalLine(xChannel, y + 3, lenChannel, SOLID, 0); + } + break; + } + } + } + } + else { + lcdDrawCenteredText(LCD_H/2, "Waiting for RX..."); + } +} diff --git a/radio/src/gui/480x272/model_setup.cpp b/radio/src/gui/480x272/model_setup.cpp index ede463945..576dae5f1 100644 --- a/radio/src/gui/480x272/model_setup.cpp +++ b/radio/src/gui/480x272/model_setup.cpp @@ -23,6 +23,13 @@ uint8_t g_moduleIdx; +// TODO find why we need this (for REGISTER at least) +#if defined(PCBXLITE) +#define EVT_BUTTON_PRESSED() EVT_KEY_FIRST(KEY_ENTER) +#else +#define EVT_BUTTON_PRESSED() EVT_KEY_BREAK(KEY_ENTER) +#endif + enum MenuModelSetupItems { ITEM_MODEL_NAME, ITEM_MODEL_BITMAP, @@ -70,6 +77,11 @@ enum MenuModelSetupItems { ITEM_MODEL_INTERNAL_MODULE_PXX2_MODEL_NUM, ITEM_MODEL_INTERNAL_MODULE_FAILSAFE, ITEM_MODEL_INTERNAL_MODULE_ANTENNA, + ITEM_MODEL_INTERNAL_MODULE_PXX2_REGISTER_RANGE, + ITEM_MODEL_INTERNAL_MODULE_PXX2_OPTIONS, + ITEM_MODEL_INTERNAL_MODULE_PXX2_RECEIVER_1, + ITEM_MODEL_INTERNAL_MODULE_PXX2_RECEIVER_2, + ITEM_MODEL_INTERNAL_MODULE_PXX2_RECEIVER_3, ITEM_MODEL_EXTERNAL_MODULE_LABEL, ITEM_MODEL_EXTERNAL_MODULE_MODE, #if defined(MULTIMODULE) @@ -79,6 +91,7 @@ enum MenuModelSetupItems { ITEM_MODEL_EXTERNAL_MODULE_CHANNELS, ITEM_MODEL_EXTERNAL_MODULE_BIND, ITEM_MODEL_EXTERNAL_MODULE_FAILSAFE, + ITEM_MODEL_EXTERNAL_MODULE_PXX2_RECEIVER_1, ITEM_MODEL_EXTERNAL_MODULE_OPTIONS, #if defined(MULTIMODULE) ITEM_MODEL_EXTERNAL_MODULE_AUTOBIND, @@ -100,6 +113,162 @@ enum MenuModelSetupItems { #define MODEL_SETUP_SLIDPOT_SPACING 45 #define CURRENT_MODULE_EDITED(k) (k >= ITEM_MODEL_EXTERNAL_MODULE_LABEL ? EXTERNAL_MODULE : INTERNAL_MODULE) +#define CURRENT_RECEIVER_EDITED(k) (k - (k >= ITEM_MODEL_EXTERNAL_MODULE_LABEL ? ITEM_MODEL_EXTERNAL_MODULE_PXX2_RECEIVER_1 : ITEM_MODEL_INTERNAL_MODULE_PXX2_RECEIVER_1)) + +bool isPXX2ReceiverEmpty(uint8_t moduleIdx, uint8_t receiverIdx) +{ + return is_memclear(g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx], PXX2_LEN_RX_NAME); +} + +void removePXX2Receiver(uint8_t moduleIdx, uint8_t receiverIdx) +{ + memclear(g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx], PXX2_LEN_RX_NAME); + g_model.moduleData[moduleIdx].pxx2.receivers &= ~(1 << receiverIdx); + storageDirty(EE_MODEL); +} + +void removePXX2ReceiverIfEmpty(uint8_t moduleIdx, uint8_t receiverIdx) +{ + if (isPXX2ReceiverEmpty(moduleIdx, receiverIdx)) { + removePXX2Receiver(moduleIdx, receiverIdx); + } +} + +const char * STR_BIND_8CH_WITH_TELEM = TR("8CH with telem.", "8CH with telemetry"); +const char * STR_BIND_16CH_WITH_TELEM = TR("16CH with telem.", "16CH with telemetry"); +const char * STR_BIND_16CH_WITHOUT_TELEM = TR("16CH without telem.", "16CH without telemetry"); +const char * STR_BIND_FLEX_868 = "Flex 868MHz"; +const char * STR_BIND_FLEX_915 = "Flex 915MHz"; + +void onPXX2R9MBindModeMenu(const char * result) +{ + if (result == STR_BIND_8CH_WITH_TELEM) { + reusableBuffer.moduleSetup.bindInformation.lbtMode = 0; + } + else if (result == STR_BIND_16CH_WITH_TELEM) { + reusableBuffer.moduleSetup.bindInformation.lbtMode = 1; + } + else if (result == STR_BIND_16CH_WITHOUT_TELEM) { + reusableBuffer.moduleSetup.bindInformation.lbtMode = 2; + } + else if (result == STR_BIND_FLEX_868) { + reusableBuffer.moduleSetup.bindInformation.flexMode = 0; + } + else if (result == STR_BIND_FLEX_915) { + reusableBuffer.moduleSetup.bindInformation.flexMode = 1; + } + else { + // the user pressed [Exit] + uint8_t moduleIdx = CURRENT_MODULE_EDITED(menuVerticalPosition); + uint8_t receiverIdx = CURRENT_RECEIVER_EDITED(menuVerticalPosition); + moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; + removePXX2ReceiverIfEmpty(moduleIdx, receiverIdx); + return; + } + +#if defined(SIMU) + uint8_t moduleIdx = CURRENT_MODULE_EDITED(menuVerticalPosition); + uint8_t receiverIdx = CURRENT_RECEIVER_EDITED(menuVerticalPosition); + memcpy(g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx], reusableBuffer.moduleSetup.bindInformation.candidateReceiversNames[reusableBuffer.moduleSetup.bindInformation.selectedReceiverIndex], PXX2_LEN_RX_NAME); + storageDirty(EE_MODEL); + moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; + reusableBuffer.moduleSetup.bindInformation.step = BIND_OK; + POPUP_INFORMATION(STR_BIND_OK); +#else + reusableBuffer.moduleSetup.bindInformation.step = BIND_START; +#endif +} + +enum PopupRegisterItems { + ITEM_REGISTER_PASSWORD, + ITEM_REGISTER_MODULE_INDEX, + ITEM_REGISTER_RECEIVER_NAME, + ITEM_REGISTER_BUTTONS +}; + +#define REGISTER_POPUP_MARGIN 80 + +void runPopupRegister(event_t event) +{ + uint8_t backupVerticalPosition = menuVerticalPosition; + uint8_t backupHorizontalPosition = menuHorizontalPosition; + uint8_t backupVerticalOffset = menuVerticalOffset; + int8_t backupEditMode = s_editMode; + + menuVerticalPosition = reusableBuffer.moduleSetup.pxx2.registerPopupVerticalPosition; + menuHorizontalPosition = reusableBuffer.moduleSetup.pxx2.registerPopupHorizontalPosition; + s_editMode = reusableBuffer.moduleSetup.pxx2.registerPopupEditMode; + + switch (event) { + case EVT_KEY_BREAK(KEY_ENTER): + if (menuVerticalPosition != ITEM_REGISTER_BUTTONS) { + break; + } + else if (reusableBuffer.moduleSetup.pxx2.registerStep >= REGISTER_RX_NAME_RECEIVED && menuHorizontalPosition == 0) { + // [Enter] pressed + reusableBuffer.moduleSetup.pxx2.registerStep = REGISTER_RX_NAME_SELECTED; + backupEditMode = EDIT_MODIFY_FIELD; // so that the [Register] button blinks and the REGISTER process can continue + } + // no break + + case EVT_KEY_LONG(KEY_EXIT): + s_editMode = 0; + // no break; + + case EVT_KEY_BREAK(KEY_EXIT): + if (s_editMode <= 0) { + warningText = nullptr; + } + break; + } + + if (warningText) { + 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)}; + check(event, 0, nullptr, 0, dialogRows, 3, 4); // TODO add a comment for 3 - HEADER_LINE once understood + + showMessageBox(warningText); + + // registration password + lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y - 4, STR_REG_ID); + editName(WARNING_LINE_X + REGISTER_POPUP_MARGIN, WARNING_LINE_Y - 4, g_model.modelRegistrationID, PXX2_LEN_REGISTRATION_ID, event, menuVerticalPosition == ITEM_REGISTER_PASSWORD); + + // loop index (will be removed in future) + lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y - 4 + FH, "UID"); + lcdDrawNumber(WARNING_LINE_X + REGISTER_POPUP_MARGIN, WARNING_LINE_Y - 4 + FH, reusableBuffer.moduleSetup.pxx2.registerLoopIndex, menuVerticalPosition == ITEM_REGISTER_MODULE_INDEX ? (s_editMode ? INVERS + BLINK : INVERS) : 0); + if (menuVerticalPosition == ITEM_REGISTER_MODULE_INDEX && s_editMode) { + CHECK_INCDEC_MODELVAR_ZERO(event, reusableBuffer.moduleSetup.pxx2.registerLoopIndex, 2); + } + + // RX name + if (reusableBuffer.moduleSetup.pxx2.registerStep < REGISTER_RX_NAME_RECEIVED) { + lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y - 4 + 2 * FH, STR_WAITING); + lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y - 2 + 3 * FH, TR_EXIT, menuVerticalPosition == ITEM_REGISTER_BUTTONS ? INVERS : 0); + } + else { + lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y - 4 + 2 * FH, STR_RX_NAME); + editName(WARNING_LINE_X + REGISTER_POPUP_MARGIN, WARNING_LINE_Y - 4 + 2 * FH, reusableBuffer.moduleSetup.pxx2.registerRxName, PXX2_LEN_RX_NAME, event, menuVerticalPosition == ITEM_REGISTER_RECEIVER_NAME); + lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y - 2 + 3 * FH, TR_ENTER, menuVerticalPosition == ITEM_REGISTER_BUTTONS && menuHorizontalPosition == 0 ? INVERS : 0); + lcdDrawText(WARNING_LINE_X + REGISTER_POPUP_MARGIN, WARNING_LINE_Y - 2 + 3 * FH, TR_EXIT, menuVerticalPosition == ITEM_REGISTER_BUTTONS && menuHorizontalPosition == 1 ? INVERS : 0); + } + + reusableBuffer.moduleSetup.pxx2.registerPopupVerticalPosition = menuVerticalPosition; + reusableBuffer.moduleSetup.pxx2.registerPopupHorizontalPosition = menuHorizontalPosition; + reusableBuffer.moduleSetup.pxx2.registerPopupEditMode = s_editMode; + } + + menuVerticalPosition = backupVerticalPosition; + menuHorizontalPosition = backupHorizontalPosition; + menuVerticalOffset = backupVerticalOffset; + s_editMode = backupEditMode; +} + +void startRegisterDialog(uint8_t module) +{ + memclear(&reusableBuffer.moduleSetup.pxx2, sizeof(reusableBuffer.moduleSetup.pxx2)); + moduleState[module].mode = MODULE_MODE_REGISTER; + s_editMode = 0; + POPUP_INPUT("", runPopupRegister); +} void checkModelIdUnique(uint8_t moduleIdx) { @@ -118,6 +287,99 @@ void checkModelIdUnique(uint8_t moduleIdx) } } +void onPXX2BindMenu(const char * result) +{ + if (result != STR_EXIT) { + uint8_t moduleIdx = CURRENT_MODULE_EDITED(menuVerticalPosition); + reusableBuffer.moduleSetup.bindInformation.selectedReceiverIndex = (result - reusableBuffer.moduleSetup.bindInformation.candidateReceiversNames[0]) / sizeof(reusableBuffer.moduleSetup.bindInformation.candidateReceiversNames[0]); + if (isModuleR9M2(moduleIdx) && reusableBuffer.moduleSetup.pxx2.moduleInformation.information.variant == PXX2_VARIANT_EU) { + reusableBuffer.moduleSetup.bindInformation.step = BIND_RX_NAME_SELECTED; + POPUP_MENU_ADD_ITEM(STR_BIND_8CH_WITH_TELEM); + POPUP_MENU_ADD_ITEM(STR_BIND_16CH_WITH_TELEM); + POPUP_MENU_ADD_ITEM(STR_BIND_16CH_WITHOUT_TELEM); + POPUP_MENU_START(onPXX2R9MBindModeMenu); + } + else if (isModuleR9M2(moduleIdx) && reusableBuffer.moduleSetup.pxx2.moduleInformation.information.variant == PXX2_VARIANT_FLEX) { + reusableBuffer.moduleSetup.bindInformation.step = BIND_RX_NAME_SELECTED; + POPUP_MENU_ADD_ITEM(STR_BIND_FLEX_868); + POPUP_MENU_ADD_ITEM(STR_BIND_FLEX_915); + POPUP_MENU_START(onPXX2R9MBindModeMenu); + } + else { +#if defined(SIMU) + uint8_t receiverIdx = CURRENT_RECEIVER_EDITED(menuVerticalPosition); + memcpy(g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx], result, PXX2_LEN_RX_NAME); + storageDirty(EE_MODEL); + moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; + reusableBuffer.moduleSetup.bindInformation.step = BIND_OK; + POPUP_INFORMATION(STR_BIND_OK); +#else + reusableBuffer.moduleSetup.bindInformation.step = BIND_START; +#endif + } + } + else { + // the user pressed [Exit] + uint8_t moduleIdx = CURRENT_MODULE_EDITED(menuVerticalPosition); + uint8_t receiverIdx = CURRENT_RECEIVER_EDITED(menuVerticalPosition); + moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; + removePXX2ReceiverIfEmpty(moduleIdx, receiverIdx); + } +} + +void onResetReceiverConfirm(const char * result) +{ + if (result == STR_OK) { + uint8_t moduleIdx = CURRENT_MODULE_EDITED(menuVerticalPosition); + uint8_t receiverIdx = CURRENT_RECEIVER_EDITED(menuVerticalPosition); + moduleState[moduleIdx].mode = MODULE_MODE_RESET; + removePXX2Receiver(moduleIdx, receiverIdx); + } +} + +void onPXX2ReceiverMenu(const char * result) +{ + uint8_t moduleIdx = CURRENT_MODULE_EDITED(menuVerticalPosition); + uint8_t receiverIdx = CURRENT_RECEIVER_EDITED(menuVerticalPosition); + + if (result == STR_OPTIONS) { + memclear(&reusableBuffer.hardwareAndSettings, sizeof(reusableBuffer.hardwareAndSettings)); + reusableBuffer.hardwareAndSettings.receiverSettings.receiverId = receiverIdx; + g_moduleIdx = moduleIdx; + pushMenu(menuModelReceiverOptions); + } + else if (result == STR_BIND) { + memclear(&reusableBuffer.moduleSetup.bindInformation, sizeof(BindInformation)); + reusableBuffer.moduleSetup.bindInformation.rxUid = receiverIdx; + if (isModuleR9M2(moduleIdx)) { +#if defined(SIMU) + reusableBuffer.moduleSetup.pxx2.moduleInformation.information.modelID = 1; + reusableBuffer.moduleSetup.pxx2.moduleInformation.information.variant = 2; +#else + moduleState[moduleIdx].readModuleInformation(&reusableBuffer.moduleSetup.pxx2.moduleInformation, PXX2_HW_INFO_TX_ID, PXX2_HW_INFO_TX_ID); +#endif + } + else { + moduleState[moduleIdx].startBind(&reusableBuffer.moduleSetup.bindInformation); + } + s_editMode = 1; + } + else if (result == STR_SHARE) { + reusableBuffer.moduleSetup.pxx2.shareReceiverIndex = receiverIdx; + moduleState[moduleIdx].mode = MODULE_MODE_SHARE; + s_editMode = 1; + } + else if (result == STR_DELETE || result == STR_RESET) { + memclear(&reusableBuffer.moduleSetup.pxx2, sizeof(reusableBuffer.moduleSetup.pxx2)); + reusableBuffer.moduleSetup.pxx2.resetReceiverIndex = receiverIdx; + reusableBuffer.moduleSetup.pxx2.resetReceiverFlags = (result == STR_RESET ? 0xFF : 0x01); + POPUP_CONFIRMATION(result == STR_RESET ? STR_RECEIVER_RESET : STR_RECEIVER_DELETE, onResetReceiverConfirm); + } + else { + removePXX2ReceiverIfEmpty(moduleIdx, receiverIdx); + } +} + void onBindMenu(const char * result) { uint8_t moduleIdx = (menuVerticalPosition >= ITEM_MODEL_EXTERNAL_MODULE_LABEL ? EXTERNAL_MODULE : INTERNAL_MODULE); @@ -232,89 +494,6 @@ int getSwitchWarningsCount() return count; } -enum PopupRegisterItems { - ITEM_REGISTER_PASSWORD, - ITEM_REGISTER_MODULE_INDEX, - ITEM_REGISTER_RECEIVER_NAME, - ITEM_REGISTER_BUTTONS -}; - -#define REGISTER_POPUP_MARGIN 80 - -void runPopupRegister(event_t event) -{ - uint8_t backupVerticalPosition = menuVerticalPosition; - uint8_t backupHorizontalPosition = menuHorizontalPosition; - uint8_t backupVerticalOffset = menuVerticalOffset; - int8_t backupEditMode = s_editMode; - - menuVerticalPosition = reusableBuffer.moduleSetup.pxx2.registerPopupVerticalPosition; - menuHorizontalPosition = reusableBuffer.moduleSetup.pxx2.registerPopupHorizontalPosition; - s_editMode = reusableBuffer.moduleSetup.pxx2.registerPopupEditMode; - - switch (event) { - case EVT_KEY_BREAK(KEY_ENTER): - if (menuVerticalPosition != ITEM_REGISTER_BUTTONS) { - break; - } - else if (reusableBuffer.moduleSetup.pxx2.registerStep >= REGISTER_RX_NAME_RECEIVED && menuHorizontalPosition == 0) { - // [Enter] pressed - reusableBuffer.moduleSetup.pxx2.registerStep = REGISTER_RX_NAME_SELECTED; - backupEditMode = EDIT_MODIFY_FIELD; // so that the [Register] button blinks and the REGISTER process can continue - } - // no break - - case EVT_KEY_LONG(KEY_EXIT): - s_editMode = 0; - // no break; - - case EVT_KEY_BREAK(KEY_EXIT): - if (s_editMode <= 0) { - warningText = nullptr; - } - break; - } - - if (warningText) { - 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)}; - check(event, 0, nullptr, 0, dialogRows, 3, 4); // TODO add a comment for 3 - HEADER_LINE once understood - - showMessageBox(warningText); - - // registration password - lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y - 4, STR_REG_ID); - editName(WARNING_LINE_X + REGISTER_POPUP_MARGIN, WARNING_LINE_Y - 4, g_model.modelRegistrationID, PXX2_LEN_REGISTRATION_ID, event, menuVerticalPosition == ITEM_REGISTER_PASSWORD); - - // loop index (will be removed in future) - lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y - 4 + FH, "UID"); - lcdDrawNumber(WARNING_LINE_X + REGISTER_POPUP_MARGIN, WARNING_LINE_Y - 4 + FH, reusableBuffer.moduleSetup.pxx2.registerLoopIndex, menuVerticalPosition == ITEM_REGISTER_MODULE_INDEX ? (s_editMode ? INVERS + BLINK : INVERS) : 0); - if (menuVerticalPosition == ITEM_REGISTER_MODULE_INDEX && s_editMode) { - CHECK_INCDEC_MODELVAR_ZERO(event, reusableBuffer.moduleSetup.pxx2.registerLoopIndex, 2); - } - - // RX name - if (reusableBuffer.moduleSetup.pxx2.registerStep < REGISTER_RX_NAME_RECEIVED) { - lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y - 4 + 2 * FH, STR_WAITING); - lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y - 2 + 3 * FH, TR_EXIT, menuVerticalPosition == ITEM_REGISTER_BUTTONS ? INVERS : 0); - } - else { - lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y - 4 + 2 * FH, STR_RX_NAME); - editName(WARNING_LINE_X + REGISTER_POPUP_MARGIN, WARNING_LINE_Y - 4 + 2 * FH, reusableBuffer.moduleSetup.pxx2.registerRxName, PXX2_LEN_RX_NAME, event, menuVerticalPosition == ITEM_REGISTER_RECEIVER_NAME); - lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y - 2 + 3 * FH, TR_ENTER, menuVerticalPosition == ITEM_REGISTER_BUTTONS && menuHorizontalPosition == 0 ? INVERS : 0); - lcdDrawText(WARNING_LINE_X + REGISTER_POPUP_MARGIN, WARNING_LINE_Y - 2 + 3 * FH, TR_EXIT, menuVerticalPosition == ITEM_REGISTER_BUTTONS && menuHorizontalPosition == 1 ? INVERS : 0); - } - - reusableBuffer.moduleSetup.pxx2.registerPopupVerticalPosition = menuVerticalPosition; - reusableBuffer.moduleSetup.pxx2.registerPopupHorizontalPosition = menuHorizontalPosition; - reusableBuffer.moduleSetup.pxx2.registerPopupEditMode = s_editMode; - } - - menuVerticalPosition = backupVerticalPosition; - menuHorizontalPosition = backupHorizontalPosition; - menuVerticalOffset = backupVerticalOffset; - s_editMode = backupEditMode; -} - #define IF_INTERNAL_MODULE_ON(x) (IS_INTERNAL_MODULE_ENABLED() ? (uint8_t)(x) : HIDDEN_ROW) #define IF_EXTERNAL_MODULE_ON(x) (IS_EXTERNAL_MODULE_ENABLED() ? (uint8_t)(x) : HIDDEN_ROW) @@ -338,6 +517,8 @@ void runPopupRegister(event_t event) #define POT_WARN_ITEMS() ((g_model.potsWarnMode) ? uint8_t(NAVIGATION_LINE_BY_LINE|(NUM_POTS-1)) : (uint8_t)0) #define SLIDER_WARN_ITEMS() ((g_model.potsWarnMode) ? uint8_t(NAVIGATION_LINE_BY_LINE|(NUM_SLIDERS-1)) : (uint8_t)0) +#define ANTENNA_ROW IF_NOT_PXX2_MODULE(INTERNAL_MODULE, IF_INTERNAL_MODULE_ON(0)), + #define TRAINER_LINE1_BLUETOOTH_M_ROWS ((bluetooth.distantAddr[0] == 0 || bluetooth.state == BLUETOOTH_STATE_CONNECTED) ? (uint8_t)0 : (uint8_t)1) #define TRAINER_LINE1_ROWS (g_model.trainerData.mode == TRAINER_MODE_SLAVE ? (uint8_t)1 : (g_model.trainerData.mode == TRAINER_MODE_MASTER_BLUETOOTH ? TRAINER_LINE1_BLUETOOTH_M_ROWS : (g_model.trainerData.mode == TRAINER_MODE_SLAVE_BLUETOOTH ? (uint8_t)1 : HIDDEN_ROW))) #define TRAINER_LINE2_ROWS (g_model.trainerData.mode == TRAINER_MODE_SLAVE ? (uint8_t)2 : HIDDEN_ROW) @@ -362,12 +543,18 @@ bool menuModelSetup(event_t event) LABEL(PreflightCheck), 0, 0, SW_WARN_ITEMS(), POT_WARN_ROWS, (g_model.potsWarnMode ? POT_WARN_ITEMS() : HIDDEN_ROW), (g_model.potsWarnMode ? SLIDER_WARN_ITEMS() : HIDDEN_ROW), NAVIGATION_LINE_BY_LINE|(NUM_STICKS+NUM_POTS+NUM_SLIDERS+NUM_ROTARY_ENCODERS-1), 0, uint8_t((isDefaultModelRegistrationID() || (warningText && popupFunc == runPopupRegister)) ? HIDDEN_ROW : READONLY_ROW), // Registration ID LABEL(InternalModule), - INTERNAL_MODULE_MODE_ROWS, - INTERNAL_MODULE_CHANNELS_ROWS, + INTERNAL_MODULE_MODE_ROWS, // module mode (PXX(2) / None) + INTERNAL_MODULE_CHANNELS_ROWS, // Channels min and count IF_NOT_PXX2_MODULE(INTERNAL_MODULE, IF_INTERNAL_MODULE_ON(HAS_RF_PROTOCOL_MODELINDEX(g_model.moduleData[INTERNAL_MODULE].rfProtocol) ? (uint8_t)2 : (uint8_t)1)), // RxNum + Bind - IF_PXX2_MODULE(INTERNAL_MODULE, 0), // RxNum - IF_INTERNAL_MODULE_ON((isModuleXJT(INTERNAL_MODULE)) ? FAILSAFE_ROWS(INTERNAL_MODULE) : HIDDEN_ROW), - IF_INTERNAL_MODULE_ON(0), + IF_PXX2_MODULE(INTERNAL_MODULE, 0), // RxNum + IF_INTERNAL_MODULE_ON(FAILSAFE_ROWS(INTERNAL_MODULE)), // Failsafe + ANTENNA_ROW // Antenna (pxx1 only) + IF_INTERNAL_MODULE_ON(FAILSAFE_ROWS(INTERNAL_MODULE)), // Failsafe + IF_PXX2_MODULE(INTERNAL_MODULE, 1), // Range check and Register buttons + IF_PXX2_MODULE(INTERNAL_MODULE, 0), // Module options + IF_PXX2_MODULE(INTERNAL_MODULE, 0), // Receiver 1 + IF_PXX2_MODULE(INTERNAL_MODULE, 0), // Receiver 2 + IF_PXX2_MODULE(INTERNAL_MODULE, 0), // Receiver 3 LABEL(ExternalModule), EXTERNAL_MODULE_MODE_ROWS, @@ -786,6 +973,113 @@ bool menuModelSetup(event_t event) } } } + } + break; + + case ITEM_MODEL_INTERNAL_MODULE_PXX2_REGISTER_RANGE: + + { + uint8_t moduleIdx = CURRENT_MODULE_EDITED(k); + lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MODULE); + lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_REGISTER_BUTTON, (menuHorizontalPosition == 0 ? attr : 0)); + lcdDrawText(lcdNextPos + 3, y, STR_MODULE_RANGE, (menuHorizontalPosition == 1 ? attr : 0)); + if (attr) { + if (moduleState[moduleIdx].mode == MODULE_MODE_NORMAL && s_editMode > 0) { + if (menuHorizontalPosition == 0 && event == EVT_BUTTON_PRESSED()) { + startRegisterDialog(moduleIdx); + } + else if (menuHorizontalPosition == 1) { + moduleState[moduleIdx].mode = MODULE_MODE_RANGECHECK; + } + } + if (s_editMode == 0 && !warningText) { + moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; + } + if (moduleState[moduleIdx].mode == MODULE_MODE_NORMAL) { + // REGISTER finished + s_editMode = 0; + } + } + } + break; + + case ITEM_MODEL_INTERNAL_MODULE_PXX2_OPTIONS: + + lcdDrawText(INDENT_WIDTH, y, STR_OPTIONS); + lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_SET, attr); + if (event == EVT_KEY_BREAK(KEY_ENTER) && attr) { + g_moduleIdx = CURRENT_MODULE_EDITED(k); + memclear(&reusableBuffer.hardwareAndSettings, sizeof(reusableBuffer.hardwareAndSettings)); + pushMenu(menuModelModuleOptions); + } + break; + + case ITEM_MODEL_INTERNAL_MODULE_PXX2_RECEIVER_1: + case ITEM_MODEL_INTERNAL_MODULE_PXX2_RECEIVER_2: + case ITEM_MODEL_INTERNAL_MODULE_PXX2_RECEIVER_3: + + { + uint8_t moduleIdx = CURRENT_MODULE_EDITED(k); + uint8_t receiverIdx = CURRENT_RECEIVER_EDITED(k); + + drawStringWithIndex(INDENT_WIDTH, y, STR_RECEIVER, receiverIdx + 1); + + if (!(g_model.moduleData[moduleIdx].pxx2.receivers & (1 << receiverIdx))) { + lcdDrawText(MODEL_SETUP_2ND_COLUMN, y, STR_MODULE_BIND, attr); + if (attr && s_editMode > 0) { + s_editMode = 0; + killEvents(event); + g_model.moduleData[moduleIdx].pxx2.receivers |= (1 << receiverIdx); + memclear(g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx], PXX2_LEN_RX_NAME); + storageDirty(EE_MODEL); + } + else { + break; + } + } + + drawReceiverName(MODEL_SETUP_2ND_COLUMN, y, moduleIdx, receiverIdx, attr); + + if (s_editMode && isModuleR9M2(moduleIdx) && moduleState[moduleIdx].mode == MODULE_MODE_NORMAL && reusableBuffer.moduleSetup.pxx2.moduleInformation.information.modelID) { + reusableBuffer.moduleSetup.pxx2.moduleInformation.information.modelID = 0; + moduleState[moduleIdx].startBind(&reusableBuffer.moduleSetup.bindInformation); + } + + if (attr && (moduleState[moduleIdx].mode == 0 || s_editMode == 0)) { + if (moduleState[moduleIdx].mode) { + moduleState[moduleIdx].mode = 0; + removePXX2ReceiverIfEmpty(moduleIdx, receiverIdx); + killEvents(event); // we stopped BIND / SHARE, we don't want to re-open the menu + event = 0; + } + s_editMode = 0; + } + + if (moduleState[moduleIdx].mode == MODULE_MODE_BIND) { + if (reusableBuffer.moduleSetup.bindInformation.step == BIND_INIT && reusableBuffer.moduleSetup.bindInformation.candidateReceiversCount > 0) { + popupMenuItemsCount = min(reusableBuffer.moduleSetup.bindInformation.candidateReceiversCount, PXX2_MAX_RECEIVERS_PER_MODULE); + for (uint8_t i=0; i