From 33e5aac846be55685c7b83aabe86881a0ba0807d Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 7 Nov 2021 11:28:45 +0100 Subject: [PATCH] Module and receiver options --- radio/src/gui/colorlcd/access_settings.cpp | 546 +++++++++++++++++- radio/src/gui/colorlcd/access_settings.h | 66 +++ radio/src/gui/colorlcd/channel_bar.h | 35 +- radio/src/gui/colorlcd/model_setup.cpp | 14 +- .../common/stdlcd/model_module_options.cpp | 25 +- radio/src/pulses/pxx2.cpp | 29 + radio/src/pulses/pxx2.h | 2 + 7 files changed, 679 insertions(+), 38 deletions(-) diff --git a/radio/src/gui/colorlcd/access_settings.cpp b/radio/src/gui/colorlcd/access_settings.cpp index aa544ec46..d6c87acc1 100644 --- a/radio/src/gui/colorlcd/access_settings.cpp +++ b/radio/src/gui/colorlcd/access_settings.cpp @@ -21,6 +21,7 @@ #include "opentx.h" #include "access_settings.h" +#include "channel_bar.h" #include @@ -43,12 +44,14 @@ BindRxChoiceMenu::BindRxChoiceMenu(Window* parent, uint8_t moduleIdx, if (isModuleR9MAccess(moduleIdx) && modInfo.information.variant == PXX2_VARIANT_EU) { bindInfo.step = BIND_RX_NAME_SELECTED; + // TODO: // POPUP_MENU_ADD_ITEM(STR_16CH_WITH_TELEMETRY); // POPUP_MENU_ADD_ITEM(STR_16CH_WITHOUT_TELEMETRY); // POPUP_MENU_START(onPXX2R9MBindModeMenu); } else if (isModuleR9MAccess(moduleIdx) && modInfo.information.variant == PXX2_VARIANT_FLEX) { bindInfo.step = BIND_RX_NAME_SELECTED; + // TODO: // POPUP_MENU_ADD_ITEM(STR_FLEX_868); // POPUP_MENU_ADD_ITEM(STR_FLEX_915); // POPUP_MENU_START(onPXX2R9MBindModeMenu); @@ -137,10 +140,8 @@ uint8_t ReceiverButton::pressBind() return 0; }); menu->addLine(STR_OPTIONS, [=]() { - auto hwSettings = getPXX2HardwareAndSettingsBuffer(); - memclear(&hwSettings, sizeof(hwSettings)); - hwSettings.receiverSettings.receiverId = receiverIdx; // pushMenu(menuModelReceiverOptions); + new RxOptions(this, moduleIdx, receiverIdx); return 0; }); menu->addLine(STR_SHARE, [=]() { @@ -245,7 +246,7 @@ RegisterDialog::RegisterDialog(Window* parent, uint8_t moduleIdx) : // RX name new StaticText(form, grid.getLabelSlot(), STR_RX_NAME, 0, COLOR_THEME_PRIMARY1); - waiting = new StaticText(form, grid.getFieldSlot(), STR_WAITING, 0, + waiting = new StaticText(form, grid.getFieldSlot(), STR_WAITING_FOR_RX, 0, COLOR_THEME_PRIMARY1); grid.nextLine(); grid.spacer(6); @@ -315,3 +316,540 @@ void RegisterDialog::onEvent(event_t event) } } #endif + +ModuleOptions::ModuleOptions(Window* parent, uint8_t moduleIdx): + Dialog(parent, STR_MODULE_OPTIONS, {50, 73, LCD_W - 100, LCD_H - 146}), + moduleIdx(moduleIdx) +{ + setCloseWhenClickOutside(true); + auto form = &content->form; + new StaticText(form, {0, height() / 2, width(), PAGE_LINE_HEIGHT}, + STR_WAITING_FOR_MODULE, CENTERED | VCENTERED | COLOR_THEME_PRIMARY1); + +#if defined(SIMU) + auto& hwSettings = getPXX2HardwareAndSettingsBuffer(); + memclear(&hwSettings, sizeof(hwSettings)); + hwSettings.moduleSettings.state = PXX2_SETTINGS_OK; + moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; + state = MO_ReadModuleSettings; +#endif + setCloseHandler([=]() { moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; }); + setFocus(); +} + +void ModuleOptions::checkEvents() +{ + auto& hwSettings = getPXX2HardwareAndSettingsBuffer(); + switch (state) { + case MO_Init: + // kickstart fetching HW settings + memclear(&hwSettings, sizeof(hwSettings)); + moduleState[moduleIdx].readModuleInformation( + &hwSettings.modules[moduleIdx], PXX2_HW_INFO_TX_ID, + PXX2_HW_INFO_TX_ID); + state = MO_ReadModuleInfo; + break; + case MO_ReadModuleInfo: + // times out after a while (see pxx2.cpp) + if (moduleState[moduleIdx].mode == MODULE_MODE_NORMAL) { + moduleState[moduleIdx].readModuleSettings(&hwSettings.moduleSettings); + state = MO_ReadModuleSettings; + } + break; + case MO_ReadModuleSettings: + // this one does NOT timeout (see pxx2.cpp) + if (moduleState[moduleIdx].mode == MODULE_MODE_NORMAL && + hwSettings.moduleSettings.state == PXX2_SETTINGS_OK) { + state = MO_DisplaySettings; + update(); + } + break; + case MO_WriteSettings: + // TODO: ask for confirmation ??? + moduleState[moduleIdx].writeModuleSettings(&hwSettings.moduleSettings); + state = MO_WritingSettings; + break; + + case MO_WritingSettings: +#if defined(SIMU) + statusText.clear(); + deleteLater(); +#else + if (moduleState[moduleIdx].mode == MODULE_MODE_NORMAL && + hwSettings.moduleSettings.state == PXX2_SETTINGS_OK) { + statusText.clear(); + deleteLater(); + } +#endif + break; + + default: // MO_DisplaySettings + break; + } + + Dialog::checkEvents(); +} + +#if defined(HARDWARE_KEYS) +void ModuleOptions::onEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), + event); + + if (event == EVT_KEY_BREAK(KEY_EXIT)) { + deleteLater(); + } +} +#endif + +uint8_t ModuleOptions::getModuleSettingsState() +{ + const auto& hwSettings = getPXX2HardwareAndSettingsBuffer(); + return hwSettings.moduleSettings.state; +} + +void ModuleOptions::update() +{ + auto form = &content->form; + form->clear(); + + auto& hwSettings = getPXX2HardwareAndSettingsBuffer(); + uint8_t modelId = hwSettings.modules[moduleIdx].information.modelID; + +#if defined(SIMU) + if (modelId == 0) { + modelId = PXX2_MODULE_R9M_LITE_PRO; + hwSettings.modules[moduleIdx].information.modelID = modelId; + hwSettings.modules[moduleIdx].information.variant = PXX2_VARIANT_FCC; + } +#endif + + uint8_t optionsAvailable = + getPXX2ModuleOptions(modelId) & + ((1 << MODULE_OPTION_EXTERNAL_ANTENNA) | (1 << MODULE_OPTION_POWER)); + + FormGridLayout grid(content->form.width(), 10); + grid.setLabelWidth(width() / 3); + + new StaticText(form, grid.getLabelSlot(), STR_MODULE); + new StaticText(form, grid.getFieldSlot(), getPXX2ModuleName(modelId)); + grid.nextLine(); + + if (!optionsAvailable) { + // no options available + new StaticText(form, grid.getCenteredSlot(), STR_NO_TX_OPTIONS, 0, CENTERED); + } else { + // some options available + if (optionsAvailable & (1 << MODULE_OPTION_EXTERNAL_ANTENNA)) { + new StaticText(form, grid.getLabelSlot(), STR_EXT_ANTENNA, 0, + COLOR_THEME_PRIMARY1); + new CheckBox(form, grid.getFieldSlot(), + []() { + const auto& hwSettings = getPXX2HardwareAndSettingsBuffer(); + return hwSettings.moduleSettings.externalAntenna; + }, + [&](uint8_t val) { + auto& hwSettings = getPXX2HardwareAndSettingsBuffer(); + hwSettings.moduleSettings.externalAntenna = val; + }); + grid.nextLine(); + } + + if (optionsAvailable & (1 << MODULE_OPTION_POWER)) { + + // TODO: use isTelemetryAvailable() to check if rebind is necessary + new StaticText(form, grid.getLabelSlot(), STR_POWER, 0, + COLOR_THEME_PRIMARY1); + auto txPower = new Choice( + form, grid.getFieldSlot(), 0, 30, + []() { + const auto& hwSettings = getPXX2HardwareAndSettingsBuffer(); + return hwSettings.moduleSettings.txPower; + }, + [&](int val) { + auto& hwSettings = getPXX2HardwareAndSettingsBuffer(); + hwSettings.moduleSettings.txPower = val; + //state = MO_WriteSettings; + }); + + txPower->setTextHandler([](int val) { + switch (val) { + case 10: + return std::string("10 mW"); + case 14: + return std::string("25 mW"); + case 20: + return std::string("100 mW"); + case 23: + return std::string("200 mW"); + case 27: + return std::string("500 mW"); + case 30: + return std::string("1000 mW"); + default: + return std::string("---"); + } + }); + + txPower->setAvailableHandler([=](int val) { + const auto& hwSettings = getPXX2HardwareAndSettingsBuffer(); + return isPXX2PowerAvailable(hwSettings.modules[moduleIdx].information, val); + }); + grid.nextLine(); + } + } + + new DynamicText(form, grid.getFieldSlot(), [=]() { return statusText; }); + grid.setLabelWidth(15); + grid.setMarginRight(15); + grid.nextLine(); + + auto exitButton = + new TextButton(form, grid.getFieldSlot(2, 0), STR_EXIT, [=]() -> int8_t { + this->deleteLater(); + return 0; + }); + + new TextButton(form, grid.getFieldSlot(2, 1), STR_OK, [=]() -> int8_t { + this->writeSettings(); + return 0; + }); + + exitButton->setFocus(SET_FOCUS_DEFAULT); + grid.nextLine(); + grid.spacer(PAGE_PADDING); + + form->setInnerHeight(grid.getWindowHeight()); + form->setHeight(form->getInnerHeight()); + + content->adjustHeight(); + content->setWindowCentered(); +} + +void ModuleOptions::writeSettings() +{ + if (state == MO_DisplaySettings) { + statusText = STR_WRITING; + state = MO_WriteSettings; + } +} + +RxOptions::RxOptions(Window* parent, uint8_t moduleIdx, uint8_t rxIdx): + Dialog(parent, STR_RECEIVER_OPTIONS, {50, 73, LCD_W - 100, LCD_H - 146}), + moduleIdx(moduleIdx), + receiverIdx(rxIdx) +{ + setCloseWhenClickOutside(true); + auto form = &content->form; + new StaticText( + form, + {0, static_cast(form->height() / 2 - PAGE_LINE_HEIGHT), + form->width(), PAGE_LINE_HEIGHT}, + STR_WAITING_FOR_RX, 0, CENTERED | VCENTERED | COLOR_THEME_PRIMARY1); + +#if defined(SIMU) + auto& hwSettings = getPXX2HardwareAndSettingsBuffer(); + memclear(&hwSettings, sizeof(hwSettings)); + hwSettings.moduleSettings.state = PXX2_SETTINGS_OK; + hwSettings.receiverSettings.state = PXX2_SETTINGS_OK; + moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; + state = RO_ReadReceiverSettings; + auto& rxInfo = hwSettings.modules[moduleIdx].receivers[receiverIdx].information; + rxInfo.capabilities = 0xFFFFFFFF; + hwSettings.receiverSettings.outputsCount = 6; + for (int i=0; i<6; i++) { + hwSettings.receiverSettings.outputsMapping[i] = i; + } +#endif + setCloseHandler([=]() { moduleState[moduleIdx].mode = MODULE_MODE_NORMAL; }); + setFocus(); +} + +void RxOptions::checkEvents() +{ + auto& hwSettings = getPXX2HardwareAndSettingsBuffer(); + auto& module = hwSettings.modules[moduleIdx]; + auto& receiver = module.receivers[receiverIdx]; + + switch (state) { + case RO_Init: + // kickstart fetching RX settings + memclear(&hwSettings, sizeof(hwSettings)); + //hwSettings.receiverSettings.receiverId = receiverIdx; + moduleState[moduleIdx].readModuleInformation( + &hwSettings.modules[moduleIdx], receiverIdx, receiverIdx); + state = RO_ReadModuleInfo; + break; + case RO_ReadModuleInfo: + // times out after a while (see pxx2.cpp) + if (moduleState[moduleIdx].mode == MODULE_MODE_NORMAL) { + // uint8_t rxModelId = receiver.information.modelID; + uint8_t rxVariant = receiver.information.variant; + if (isModuleR9MAccess(moduleIdx) && rxVariant == PXX2_VARIANT_EU && + !hwSettings.moduleSettings.txPower) { + moduleState[moduleIdx].readModuleSettings(&hwSettings.moduleSettings); + state = RO_ReadModuleSettings; + } else { + moduleState[moduleIdx].readReceiverSettings(&hwSettings.receiverSettings); + state = RO_ReadReceiverSettings; + } + } + break; + case RO_ReadModuleSettings: + // this one does NOT timeout (see pxx2.cpp) + if (moduleState[moduleIdx].mode == MODULE_MODE_NORMAL && + hwSettings.moduleSettings.state == PXX2_SETTINGS_OK) { + moduleState[moduleIdx].readReceiverSettings(&hwSettings.receiverSettings); + state = RO_ReadReceiverSettings; + } + break; + case RO_ReadReceiverSettings: + if (moduleState[moduleIdx].mode == MODULE_MODE_NORMAL && + hwSettings.receiverSettings.state == PXX2_SETTINGS_OK) { + state = RO_DisplaySettings; + update(); + } + break; + case RO_WriteSettings: + // TODO: ask for confirmation ??? + moduleState[moduleIdx].writeReceiverSettings(&hwSettings.receiverSettings); + state = RO_WritingSettings; + break; + + case RO_WritingSettings: +#if defined(SIMU) + statusText.clear(); + deleteLater(); +#else + if (moduleState[moduleIdx].mode == MODULE_MODE_NORMAL && + hwSettings.receiverSettings.state == PXX2_SETTINGS_OK) { + statusText.clear(); + deleteLater(); + } +#endif + break; + + default: // RO_DisplaySettings + break; + } + + Dialog::checkEvents(); +} + +#if defined(HARDWARE_KEYS) +void RxOptions::onEvent(event_t event) +{ + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), + event); + + if (event == EVT_KEY_BREAK(KEY_EXIT)) { + deleteLater(); + } +} +#endif + +#define CH_ENABLE_SPORT 4 +#define CH_ENABLE_SBUS 5 + +static uint8_t getShiftedChannel(int8_t moduleIdx, int ch) +{ + return g_model.moduleData[moduleIdx].channelsStart + ch; +} + +static std::string getChannelText(int8_t moduleIdx, uint8_t pin, int val) +{ + uint8_t ch = getShiftedChannel(moduleIdx, val); + uint8_t channelsMax = sentModuleChannels(moduleIdx) - 1; + if (val <= channelsMax) { + return std::string("CH") + std::to_string(ch + 1); + } else if (pin == CH_ENABLE_SPORT) { + return std::string("S.PORT"); + } else if (pin == CH_ENABLE_SBUS) { + return std::string("SBUS"); + } + return std::string(); +} + +void RxOptions::update() +{ + auto form = &content->form; + form->clear(); + + auto& hwSettings = getPXX2HardwareAndSettingsBuffer(); + auto& rxInfo = hwSettings.modules[moduleIdx].receivers[receiverIdx].information; + // uint8_t receiverModelId = rxInfo.modelID; + uint8_t receiverVariant = rxInfo.variant; + uint8_t capabilities = rxInfo.capabilities; + + FormGridLayout grid(content->form.width(), 10); + grid.setLabelWidth(width() / 3); + + new StaticText(form, grid.getLabelSlot(), STR_RECEIVER); + new StaticText(form, grid.getFieldSlot(), + g_model.moduleData[moduleIdx].pxx2.receiverName[receiverIdx]); + grid.nextLine(); + + // PWM rate + new StaticText(form, grid.getLabelSlot(), + isModuleR9MAccess(moduleIdx) ? "6.67ms PWM" : "7ms PWM"); + new CheckBox( + form, grid.getFieldSlot(), + []() { + auto& hwSettings = getPXX2HardwareAndSettingsBuffer(); + return hwSettings.receiverSettings.pwmRate; + }, + [](int val) { + auto& hwSettings = getPXX2HardwareAndSettingsBuffer(); + hwSettings.receiverSettings.pwmRate = val; + }); + grid.nextLine(); + + // telemetry disabled + new StaticText(form, grid.getLabelSlot(), STR_TELEMETRY_DISABLED); + auto tele25mw = new CheckBox( + form, grid.getFieldSlot(), + []() { + auto& hwSettings = getPXX2HardwareAndSettingsBuffer(); + return hwSettings.receiverSettings.telemetryDisabled; + }, + [](int val) { + auto& hwSettings = getPXX2HardwareAndSettingsBuffer(); + hwSettings.receiverSettings.telemetryDisabled = val; + }); + + if (isModuleR9MAccess(moduleIdx) && receiverVariant == PXX2_VARIANT_EU && + hwSettings.moduleSettings.txPower > 14 /*25mW*/) { + // read only field in this case + tele25mw->disable(); + } + grid.nextLine(); + + if (capabilities & (1 << RECEIVER_CAPABILITY_TELEMETRY_25MW)) { + // telemetry 25 mW + new StaticText(form, grid.getLabelSlot(), "25mw Tele"); + new CheckBox( + form, grid.getFieldSlot(), + []() { + auto& hwSettings = getPXX2HardwareAndSettingsBuffer(); + return hwSettings.receiverSettings.telemetry25mw; + }, + [](int val) { + auto& hwSettings = getPXX2HardwareAndSettingsBuffer(); + hwSettings.receiverSettings.telemetry25mw = val; + }); + grid.nextLine(); + } + + if (capabilities & + ((1 << RECEIVER_CAPABILITY_FPORT) | (1 << RECEIVER_CAPABILITY_FPORT2))) { + + // SPORT modes + new StaticText(form, grid.getLabelSlot(), STR_PROTOCOL); + auto sportModes = new Choice( + form, grid.getFieldSlot(2, 0), STR_SPORT_MODES, 0, 2, + []() { + const auto& hwSettings = getPXX2HardwareAndSettingsBuffer(); + const auto& rxSettings = hwSettings.receiverSettings; + return rxSettings.fport | (rxSettings.fport2 << 1); + }, + [](int val) { + auto& hwSettings = getPXX2HardwareAndSettingsBuffer(); + auto& rxSettings = hwSettings.receiverSettings; + rxSettings.fport = val & 0x01; + rxSettings.fport2 = (val & 0x02) >> 1; + }); + + sportModes->setAvailableHandler([=](int val) { + switch (val) { + case 1: // FPORT + return (bool)(capabilities & (1 << RECEIVER_CAPABILITY_FPORT)); + case 2: // FPORT2 + return (bool)(capabilities & (1 << RECEIVER_CAPABILITY_FPORT2)); + } + return true; + }); + grid.nextLine(); + } + + auto outputsCount = min(16, hwSettings.receiverSettings.outputsCount); + for (uint8_t i = 0; i < outputsCount; i++) { + std::string i_str = std::to_string(i+1); + new StaticText(form, grid.getLabelSlot(), std::string(STR_PIN) + i_str); + + uint8_t channelsMax = sentModuleChannels(moduleIdx) - 1; + uint8_t selectionMax = channelsMax; + if (capabilities & (1 << RECEIVER_CAPABILITY_ENABLE_PWM_CH5_CH6) + && (CH_ENABLE_SPORT == i || CH_ENABLE_SBUS == i)) { + selectionMax++; + } + + uint8_t mapping = hwSettings.receiverSettings.outputsMapping[i]; + uint8_t channel = getShiftedChannel(moduleIdx, mapping); + + auto r = grid.getFieldSlot(2, 1); + if (r.h > BAR_HEIGHT) { + r.y += (r.h - BAR_HEIGHT)/2; + r.h = BAR_HEIGHT; + } + auto chBar = new OutputChannelBar(form, r, channel); + + auto chDn = new Choice( + form, grid.getFieldSlot(2, 0), 0, selectionMax, + [=]() { + auto& hwSettings = getPXX2HardwareAndSettingsBuffer(); + return hwSettings.receiverSettings.outputsMapping[i]; + }, + [=](int val) { + auto& hwSettings = getPXX2HardwareAndSettingsBuffer(); + hwSettings.receiverSettings.outputsMapping[i] = val; + if (val <= channelsMax) { + chBar->setChannel(getShiftedChannel(moduleIdx, val)); + chBar->setEnabled(true); + } else { + chBar->setEnabled(false); + } + }); + chDn->setTextHandler( + [=](int val) { return getChannelText(moduleIdx, i, val); }); + + grid.nextLine(); + } + + new DynamicText(form, grid.getFieldSlot(), [=]() { return statusText; }); + grid.setLabelWidth(15); + grid.setMarginRight(15); + grid.nextLine(); + + auto exitButton = + new TextButton(form, grid.getFieldSlot(2, 0), STR_EXIT, [=]() -> int8_t { + this->deleteLater(); + return 0; + }); + + new TextButton(form, grid.getFieldSlot(2, 1), STR_OK, [=]() -> int8_t { + this->writeSettings(); + return 0; + }); + + exitButton->setFocus(SET_FOCUS_DEFAULT); + grid.nextLine(); + grid.spacer(PAGE_PADDING); + + form->setInnerHeight(grid.getWindowHeight()); + if (form->getInnerHeight() < LCD_H - 2*POPUP_HEADER_HEIGHT) { + form->setHeight(form->getInnerHeight()); + } else { + form->setHeight(LCD_H - 2*POPUP_HEADER_HEIGHT); + } + + content->adjustHeight(); + content->setWindowCentered(); +} + +void RxOptions::writeSettings() +{ + if (state == RO_DisplaySettings) { + statusText = STR_WRITING; + state = RO_WriteSettings; + } +} diff --git a/radio/src/gui/colorlcd/access_settings.h b/radio/src/gui/colorlcd/access_settings.h index a685a31fb..945fa03af 100644 --- a/radio/src/gui/colorlcd/access_settings.h +++ b/radio/src/gui/colorlcd/access_settings.h @@ -86,3 +86,69 @@ class RegisterDialog : public Dialog TextEdit* rxName = nullptr; TextButton* exitButton; }; + +class ModuleOptions : public Dialog +{ + public: + ModuleOptions(Window* parent, uint8_t moduleIdx); + + void checkEvents() override; +#if defined(HARDWARE_KEYS) + void onEvent(event_t event) override; +#endif + + protected: + + enum { + MO_Init=0, + MO_ReadModuleInfo, + MO_ReadModuleSettings, + MO_DisplaySettings, + MO_WriteSettings, + MO_WritingSettings, + }; + + uint8_t moduleIdx; + uint8_t state = MO_Init; + + std::string statusText; + + uint8_t getModuleSettingsState(); + + void update(); + void writeSettings(); +}; + +class RxOptions : public Dialog +{ + public: + RxOptions(Window* parent, uint8_t moduleIdx, uint8_t rxIdx); + + void checkEvents() override; +#if defined(HARDWARE_KEYS) + void onEvent(event_t event) override; +#endif + + protected: + + enum { + RO_Init=0, + RO_ReadModuleInfo, + RO_ReadModuleSettings, + RO_ReadReceiverSettings, + RO_DisplaySettings, + RO_WriteSettings, + RO_WritingSettings, + }; + + uint8_t moduleIdx; + uint8_t receiverIdx; + uint8_t state = RO_Init; + + std::string statusText; + + uint8_t getRxSettingsState(); + + void update(); + void writeSettings(); +}; diff --git a/radio/src/gui/colorlcd/channel_bar.h b/radio/src/gui/colorlcd/channel_bar.h index 6d8644ab9..84bf3a354 100644 --- a/radio/src/gui/colorlcd/channel_bar.h +++ b/radio/src/gui/colorlcd/channel_bar.h @@ -36,15 +36,26 @@ constexpr coord_t LEG_COLORBOX = 15; class ChannelBar : public Window { - public: - ChannelBar(Window * parent, const rect_t & rect, uint8_t channel): - Window(parent, rect), - channel(channel) - { - } - - protected: - uint8_t channel = 0; + public: + ChannelBar(Window* parent, const rect_t& rect, uint8_t channel) : + Window(parent, rect), channel(channel) + { + } + + void setChannel(uint8_t ch) + { + channel = ch; + invalidate(); + } + + void setEnabled(bool en) + { + enabled = en; + } + + protected: + uint8_t channel = 0; + bool enabled = true; }; class MixerChannelBar : public ChannelBar @@ -54,6 +65,8 @@ class MixerChannelBar : public ChannelBar void paint(BitmapBuffer * dc) override { + if (!enabled) return; + int chanVal = calcRESXto100(ex_chans[channel]); const int displayVal = chanVal; @@ -112,6 +125,8 @@ class OutputChannelBar : public ChannelBar void paint(BitmapBuffer * dc) override { + if (!enabled) return; + int chanVal = calcRESXto100(channelOutputs[channel]); int displayVal = chanVal; @@ -238,6 +253,8 @@ class ComboChannelBar : public ChannelBar void paint(BitmapBuffer * dc) override { + if (!enabled) return; + char chanString[] = "CH32 "; int usValue = PPM_CH_CENTER(channel) + channelOutputs[channel] / 2; diff --git a/radio/src/gui/colorlcd/model_setup.cpp b/radio/src/gui/colorlcd/model_setup.cpp index 477cefe6d..284d39424 100644 --- a/radio/src/gui/colorlcd/model_setup.cpp +++ b/radio/src/gui/colorlcd/model_setup.cpp @@ -639,7 +639,9 @@ class ModuleWindow : public FormGroup { update(); rfChoice->setFocus(SET_FOCUS_DEFAULT); }); - } else if (isModulePXX2(moduleIdx)) { + } +#if defined(PXX2) + else if (isModulePXX2(moduleIdx)) { rfChoice = new Choice(this, grid.getFieldSlot(2, 1), STR_ISRM_RF_PROTOCOLS, 0, MODULE_SUBTYPE_ISRM_PXX2_ACCST_LR12, @@ -651,6 +653,7 @@ class ModuleWindow : public FormGroup { rfChoice->setFocus(SET_FOCUS_DEFAULT); }); } +#endif #if defined(AFHDS2) || defined(AFHDS3) else if (isModuleFlySky(moduleIdx)) { rfChoice = @@ -1206,6 +1209,15 @@ class ModuleWindow : public FormGroup { }); grid.nextLine(); + + new StaticText(this, grid.getLabelSlot(true), TR_OPTIONS, 0, + COLOR_THEME_PRIMARY1); + auto options = new TextButton(this, grid.getFieldSlot(2, 0), TR_SET); + options->setPressHandler([=]() { + new ModuleOptions(this, moduleIdx); + return 0; + }); + grid.nextLine(); } #endif diff --git a/radio/src/gui/common/stdlcd/model_module_options.cpp b/radio/src/gui/common/stdlcd/model_module_options.cpp index 3af79e172..6274cae3f 100644 --- a/radio/src/gui/common/stdlcd/model_module_options.cpp +++ b/radio/src/gui/common/stdlcd/model_module_options.cpp @@ -61,30 +61,7 @@ enum { 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 with telemetry */ || - value == 20 /* 100 mW without telemetry */); - 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 */ - } + return isPXX2PowerAvailable(reusableBuffer.hardwareAndSettings.modules[g_moduleIdx].information, value); } void menuModelModuleOptions(event_t event) diff --git a/radio/src/pulses/pxx2.cpp b/radio/src/pulses/pxx2.cpp index ffdfade86..fa02b77e7 100644 --- a/radio/src/pulses/pxx2.cpp +++ b/radio/src/pulses/pxx2.cpp @@ -25,6 +25,35 @@ #include "io/frsky_firmware_update.h" #include "libopenui/src/libopenui_file.h" +bool isPXX2PowerAvailable(const PXX2HardwareInformation& info, int value) +{ + uint8_t modelId = info.modelID; + uint8_t variant = info.variant; + + if (modelId == PXX2_MODULE_R9M_LITE) { + if (variant == PXX2_VARIANT_EU) + return (value == 14 /* 25 mW with telemetry */ || + value == 20 /* 100 mW without telemetry */); + 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 { + // other modules do not have the power option + return false; + } +} + PXX2ModuleSetup& getPXX2ModuleSetupBuffer() { return reusableBuffer.moduleSetup.pxx2; diff --git a/radio/src/pulses/pxx2.h b/radio/src/pulses/pxx2.h index 5c1305631..f3c6db6b5 100644 --- a/radio/src/pulses/pxx2.h +++ b/radio/src/pulses/pxx2.h @@ -331,6 +331,8 @@ PACK(struct PXX2HardwareInformation { uint8_t capabilityNotSupported; }); +bool isPXX2PowerAvailable(const PXX2HardwareInformation& info, int value); + PACK(struct ModuleInformation { int8_t current; int8_t maximum;